Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Decline #196

Merged
merged 4 commits into from
Dec 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
.scala-build
*.iml
tmp
out
out
.history
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ ivy"io.github.iltotore::iron:version"
| iron-cats | ✔️ | ✔️ | ✔️ |
| iron-circe | ✔️ | ✔️ | ✔️ |
| iron-ciris | ✔️ | ✔️ | ✔️ |
| iron-decline | ✔️ | ✔️ | ✔️ |
| iron-jsoniter | ✔️ | ✔️ | ✔️ |
| iron-scalacheck | ✔️ | ✔️ | ❌ |
| iron-skunk | ✔️ | ✔️ | ✔️ |
Expand Down
20 changes: 18 additions & 2 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ object docs extends BaseModule {

def artifactName = "iron-docs"

val modules: Seq[ScalaModule] = Seq(main, cats, circe, upickle, ciris, jsoniter, scalacheck, skunk, zio, zioJson)
val modules: Seq[ScalaModule] = Seq(main, cats, circe, decline, upickle, ciris, jsoniter, scalacheck, skunk, zio, zioJson)

def docSources = T.sources {
T.traverse(modules)(_.docSources)().flatten
Expand Down Expand Up @@ -140,7 +140,8 @@ object docs extends BaseModule {
".*zio[^\\.json].*" -> ("scaladoc3", "https://javadoc.io/doc/dev.zio/zio_3/latest/"),
".*org.scalacheck.*" -> ("scaladoc3", "https://javadoc.io/doc/org.scalacheck/scalacheck_3/latest/"),
".*org.scalacheck.*" -> ("scaladoc3", "https://javadoc.io/doc/org.scalacheck/scalacheck_3/latest/"),
".*skunk.*" -> ("scaladoc3", "https://javadoc.io/doc/org.tpolecat/skunk-docs_3/latest/")
".*skunk.*" -> ("scaladoc3", "https://javadoc.io/doc/org.tpolecat/skunk-docs_3/latest/"),
".*com.monovore.decline.*" -> ("scaladoc3", "https://javadoc.io/doc/com.monovore/decline_3/latest/")
)

def scalaDocOptions = {
Expand Down Expand Up @@ -434,3 +435,18 @@ object doobie extends SubModule {

object test extends Tests
}

object decline extends SubModule {

def artifactName = "iron-decline"

def ivyDeps = Agg(
ivy"com.monovore::decline::2.4.1"
)

object test extends Tests

object js extends JSCrossModule

object native extends NativeCrossModule
}
23 changes: 23 additions & 0 deletions decline/src/io/github/iltotore/iron/decline.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.github.iltotore.iron

import _root_.com.monovore.decline.Argument
import _root_.io.github.iltotore.iron.*
import cats.data.Validated
import cats.data.Validated.Valid
import cats.data.Validated.Invalid
import cats.data.ValidatedNel // Add this import

object decline:
inline given [A, B](using inline argument: Argument[A], inline constraint: Constraint[A, B]): Argument[A :| B] =
new Argument[A :| B]:
def read(string: String): ValidatedNel[String, A :| B] =
argument.read(string) match
case Valid(a) => a.refineEither[B] match
case Left(value) => Validated.invalidNel(value)
case Right(value) => Validated.validNel(value)
case Invalid(e) => Validated.invalid(e)

def defaultMetavar: String = argument.defaultMetavar

inline given [T](using mirror: RefinedTypeOps.Mirror[T], argument: Argument[mirror.IronType]): Argument[T] =
argument.asInstanceOf[Argument[T]]
23 changes: 23 additions & 0 deletions decline/test/src/io/github/iltotore/iron/DeclineSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.github.iltotore.iron

import _root_.com.monovore.decline.Argument
import _root_.cats.data.Validated.Valid
import io.github.iltotore.iron.decline.given
import io.github.iltotore.iron.constraint.numeric.Positive
import utest.*

object DeclineSuite extends TestSuite:
val tests: Tests = Tests {

test("Argument") {
test("ironType") {
test("success") - assert(summon[Argument[Int :| Positive]].read("5") == Valid(5))
test("failure") - assert(summon[Argument[Int :| Positive]].read("-5").isInvalid)
}

test("newType") {
test("success") - assert(summon[Argument[Temperature]].read("5") == Valid(5))
test("failure") - assert(summon[Argument[Temperature]].read("-5").isInvalid)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.github.iltotore.iron

import io.github.iltotore.iron.constraint.numeric.Positive

opaque type Temperature = Int :| Positive
object Temperature extends RefinedTypeOps[Int, Positive, Temperature]
72 changes: 72 additions & 0 deletions docs/_docs/modules/decline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
title: "Decline Support"
---

# Skunk Support

This module provides refined types Argument instances for [Decline](https://ben.kirw.in/decline/).

## Dependency

SBT:

```scala
libraryDependencies += "io.github.iltotore" %% "iron-decline" % "version"
```

Mill:

```scala
ivy"io.github.iltotore::iron-decline:version"
```

### Following examples' dependencies

SBT:

```scala
libraryDependencies += "com.monovore" %% "decline" % "2.4.1"
```

Mill:

```scala
ivy"com.monovore::decline::2.4.1"
```

## Argument instances

Iron provides `Argument` instances for refined types:

```scala
import cats.implicits.*
import com.monovore.decline.*
import io.github.iltotore.iron.*
import io.github.iltotore.iron.constraint.all.*
import io.github.iltotore.iron.decline.given

type Person = String :| Not[Blank]

opaque type PositiveInt <: Int = Int :| Positive
object PositiveInt extends RefinedTypeOps[Int, Positive, PositiveInt]

object HelloWorld extends CommandApp(
name = "hello-world",
header = "Says hello!",
main = {
// Defining an option for a constrainted type
val userOpt =
Opts.option[Person]("target", help = "Person to greet.")
.withDefault("world")

// Defining an option for a refined opaque type
val nOpt =
Opts.option[PositiveInt]("quiet", help = "Number of times message is printed.")
.withDefault(PositiveInt(1))

(userOpt, nOpt).mapN { (user, n) =>
(1 to n).map(_ => println(s"Hello $user!"))
}
}
)
```
1 change: 1 addition & 0 deletions docs/sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ subsection:
- page: modules/cats.md
- page: modules/circe.md
- page: modules/ciris.md
- page: modules/decline.md
- page: modules/jsoniter.md
- page: modules/skunk.md
- page: modules/scalacheck.md
Expand Down
Loading