Skip to content

Commit 1598774

Browse files
authored
Merge pull request #251 from vigoo/zio-2
ZIO 2 support
2 parents f96701c + b12c262 commit 1598774

File tree

5 files changed

+225
-6
lines changed

5 files changed

+225
-6
lines changed

build.sbt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@ lazy val zio = Project("clipp-zio", file("clipp-zio")).settings(commonSettings).
9696
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")
9797
).dependsOn(core)
9898

99+
lazy val zio2 = Project("clipp-zio-2", file("clipp-zio-2")).settings(commonSettings).settings(
100+
description := "Clipp ZIO 2 interface",
101+
102+
resolvers +=
103+
"Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
104+
libraryDependencies ++= Seq(
105+
"dev.zio" %% "zio" % "2.0.0-M2+72-616ee01d-SNAPSHOT",
106+
"dev.zio" %% "zio-test" % "2.0.0-M2+72-616ee01d-SNAPSHOT" % Test,
107+
"dev.zio" %% "zio-test-sbt" % "2.0.0-M2+72-616ee01d-SNAPSHOT" % Test
108+
),
109+
110+
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")
111+
).dependsOn(core)
112+
99113
lazy val catsEffect = Project("clipp-cats-effect", file("clipp-cats-effect")).settings(commonSettings).settings(
100114
description := "Clipp Cats-Effect interface",
101115

@@ -131,8 +145,8 @@ lazy val docs = project
131145
description := "Functional command line argument parser and usage info generator for Scala",
132146
publishArtifact := false,
133147
siteSubdirName in ScalaUnidoc := "api",
134-
addMappingsToSiteDir(mappings in (ScalaUnidoc, packageDoc), siteSubdirName in ScalaUnidoc),
135-
unidocProjectFilter in ( ScalaUnidoc, unidoc ) := inProjects(
148+
addMappingsToSiteDir(mappings in(ScalaUnidoc, packageDoc), siteSubdirName in ScalaUnidoc),
149+
unidocProjectFilter in(ScalaUnidoc, unidoc) := inProjects(
136150
core,
137151
catsEffect,
138152
zio
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package io.github.vigoo.clipp
2+
3+
import cats.data.NonEmptyList
4+
import io.github.vigoo.clipp.errors.CustomParserError
5+
import zio.{CanFail, Console, Has, Runtime, Tag, URIO, ZIO, ZIOAppArgs, ZLayer}
6+
7+
package object zioapi {
8+
type ClippEnv = Has[Console]
9+
type ClippZIO[+A] = ZIO[ClippEnv, ParserFailure, A]
10+
11+
implicit val clippZio: ClippIO[ClippZIO] = new ClippIO[ClippZIO] {
12+
override def succeed[T](value: T): ClippZIO[T] =
13+
ZIO.succeed(value)
14+
15+
override def failWith[T](parserFailure: ParserFailure): ClippZIO[T] =
16+
ZIO.fail(parserFailure)
17+
18+
override def recoverWith[T](io: ClippZIO[T])(f: ParserFailure => ClippZIO[T]): ClippZIO[T] =
19+
io.foldZIO(f, v => ZIO.succeed(v))
20+
21+
override def flatMap[T, U](io: ClippZIO[T])(f: T => ClippZIO[U]): ClippZIO[U] =
22+
io.flatMap(f)
23+
24+
override def showErrors(errors: String): ClippZIO[Unit] =
25+
Console.printLine(errors).orDie
26+
27+
override def showUsage(usageInfo: String): ClippZIO[Unit] =
28+
Console.printLine(usageInfo).orDie
29+
}
30+
31+
object Clipp extends ClippImpl[ClippZIO]
32+
33+
def liftZIO[R, E, T](description: String, examples: NonEmptyList[T])(f: ZIO[R, E, T])(implicit runtime: Runtime[R], ev: CanFail[E], customParserError: CustomParserError[E]): Parameter.Spec[T] =
34+
syntax.liftEither(description, examples) {
35+
runtime.unsafeRun(f.either)
36+
}
37+
38+
def liftZIO[R, E, T](description: String, example: T)(f: ZIO[R, E, T])(implicit runtime: Runtime[R], ev: CanFail[E], customParserError: CustomParserError[E]): Parameter.Spec[T] =
39+
syntax.liftEither(description, example) {
40+
runtime.unsafeRun(f.either)
41+
}
42+
43+
def liftURIO[R, T](description: String, examples: NonEmptyList[T])(f: URIO[R, T])(implicit runtime: Runtime[R]): Parameter.Spec[T] =
44+
syntax.lift(description, examples) {
45+
runtime.unsafeRun(f)
46+
}
47+
48+
def liftURIO[R, T](description: String, example: T)(f: URIO[R, T])(implicit runtime: Runtime[R]): Parameter.Spec[T] =
49+
syntax.lift(description, example) {
50+
runtime.unsafeRun(f)
51+
}
52+
53+
case class ZioDSL[R](runtime: Runtime[R]) extends syntax {
54+
private implicit val r: Runtime[R] = runtime
55+
56+
def liftZIO[E, T](description: String, examples: NonEmptyList[T])(f: ZIO[R, E, T])(implicit ev: CanFail[E], customParserError: CustomParserError[E]): Parameter.Spec[T] =
57+
zioapi.liftZIO[R, E, T](description, examples)(f)
58+
59+
def liftZIO[E, T](description: String, example: T)(f: ZIO[R, E, T])(implicit ev: CanFail[E], customParserError: CustomParserError[E]): Parameter.Spec[T] =
60+
zioapi.liftZIO[R, E, T](description, example)(f)
61+
62+
def liftURIO[T](description: String, examples: NonEmptyList[T])(f: URIO[R, T]): Parameter.Spec[T] =
63+
zioapi.liftURIO[R, T](description, examples)(f)
64+
65+
def liftURIO[T](description: String, example: T)(f: URIO[R, T]): Parameter.Spec[T] =
66+
zioapi.liftURIO[R, T](description, example)(f)
67+
}
68+
69+
def parametersFromArgs[T: Tag](spec: Parameter.Spec[T]): ZLayer[Has[Console] with Has[ZIOAppArgs], ParserFailure, Has[T]] =
70+
(for {
71+
args <- ZIO.service[ZIOAppArgs]
72+
result <- Clipp.parseOrFail(args.args, spec)
73+
} yield result).toLayer
74+
75+
def effectfulParametersFromArgs[R, T: Tag](createSpec: ZioDSL[R] => Parameter.Spec[T]): ZLayer[Has[Console] with Has[ZIOAppArgs] with R, ParserFailure, Has[T]] = {
76+
for {
77+
runtime <- ZIO.runtime[R]
78+
args <- ZIO.service[ZIOAppArgs]
79+
spec = createSpec(ZioDSL(runtime))
80+
result <- Clipp.parseOrFail(args.args, spec)
81+
} yield result
82+
}.toLayer
83+
84+
implicit class ZLayerOps[R <: Has[Console], T](layer: ZLayer[R, ParserFailure, T]) {
85+
def printUsageInfoOnFailure: ZLayer[R, ParserFailure, T] =
86+
layer.tapError { (parserFailure: ParserFailure) =>
87+
Clipp.displayErrorsAndUsageInfo(parserFailure)
88+
}
89+
}
90+
91+
def parameters[T: Tag]: URIO[Has[T], T] = ZIO.service[T]
92+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package io.github.vigoo.clipp
2+
3+
import io.github.vigoo.clipp.errors.{CustomError, ParserError}
4+
import io.github.vigoo.clipp.syntax._
5+
import io.github.vigoo.clipp.zioapi._
6+
import zio.{Console, _}
7+
import zio.test.Assertion._
8+
import zio.test._
9+
10+
object ZioSpecs extends DefaultRunnableSpec {
11+
12+
def spec = suite("ZIO interface")(
13+
test("successfully parse") {
14+
val spec = flag("Test", 'x')
15+
for {
16+
result <- Clipp.parseOrFail[Boolean](List("-x"), spec)
17+
} yield assert(result)(isTrue)
18+
},
19+
20+
test("fail on bad spec") {
21+
val spec = flag("Test", 'x')
22+
for {
23+
result <- Clipp.parseOrFail[Boolean](List("x"), spec).either
24+
} yield assert(result)(isLeft(anything))
25+
},
26+
27+
test("fail or print succeeds") {
28+
val spec = flag("Test", 'x')
29+
for {
30+
result <- Clipp.parseOrDisplayErrors(List("x"), spec, ()) { _ => ZIO.unit }
31+
} yield assert(result)(anything)
32+
},
33+
34+
test("can be used with return values") {
35+
val spec = flag("Test", 'x')
36+
for {
37+
result <- Clipp.parseOrDisplayErrors(List("-x"), spec, ExitCode.failure) { _ => ZIO.succeed(ExitCode.success) }
38+
} yield assert(result)(equalTo(ExitCode.success))
39+
},
40+
41+
test("can provide as layer") {
42+
val spec = flag("Test", 'x')
43+
val config: ZLayer[Has[Console] with Has[ZIOAppArgs], ParserFailure, Has[Boolean]] = parametersFromArgs(spec).printUsageInfoOnFailure
44+
val test: ZIO[Has[Boolean], Nothing, TestResult] =
45+
parameters[Boolean].map(p => assert(p)(isTrue))
46+
47+
test.injectCustom(ZLayer.succeed(ZIOAppArgs(Chunk("-x"))), config)
48+
},
49+
50+
suite("liftEffect")(
51+
test("success") {
52+
val config = effectfulParametersFromArgs[Any, String] { p =>
53+
p.liftURIO("test", "ex") {
54+
ZIO.succeed("test")
55+
}
56+
}
57+
val test: ZIO[Has[String], Nothing, TestResult] =
58+
parameters[String].map(p => assert(p)(equalTo("test")))
59+
60+
test.provideSomeLayer(config)
61+
},
62+
test("failure") {
63+
val config = effectfulParametersFromArgs[Any, String] { p =>
64+
p.liftZIO("test", "ex") {
65+
ZIO.fail("failure")
66+
}
67+
}
68+
69+
assertM(parameters[String].unit.provideSomeLayer(config).run)(fails(
70+
hasField("errors", _.errors.toList, contains[ParserError](CustomError("failure")))))
71+
}
72+
)
73+
)
74+
}

docs/docs/docs/catseffect.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ title: Cats Effect
88
To use the Cats-Effect interface add the following dependency:
99

1010
```scala
11-
libraryDependencies += "io.github.vigoo" %% "clipp-cats-effect" % "0.6.1"
11+
libraryDependencies += "io.github.vigoo" %% "clipp-cats-effect" % "0.6.2"
1212
```
1313

1414
Example:
@@ -39,7 +39,7 @@ object Test extends IOApp {
3939
To use the Cats-Effect interface add the following dependency:
4040

4141
```scala
42-
libraryDependencies += "io.github.vigoo" %% "clipp-cats-effect3" % "0.6.1"
42+
libraryDependencies += "io.github.vigoo" %% "clipp-cats-effect3" % "0.6.2"
4343
```
4444

4545
Example:

docs/docs/docs/zio.md

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ layout: docs
33
title: ZIO
44
---
55

6-
# Using with ZIO
6+
# Using with ZIO 1.x
77
To use the ZIO interface add the following dependency:
88

99
```scala
10-
libraryDependencies += "io.github.vigoo" %% "clipp-zio" % "0.6.1"
10+
libraryDependencies += "io.github.vigoo" %% "clipp-zio" % "0.6.2"
1111
```
1212

1313
It is possible to directly call the ZIO interface wrapper, for example:
@@ -75,4 +75,43 @@ effectfulParametersFromArgs[Clock, String](List.empty) { p =>
7575
instant.map(_.toString)
7676
}
7777
}
78+
```
79+
80+
# Using with ZIO 2.x
81+
ZIO 2 support is currently published separately for snapshot versions of ZIO. Once ZIO 2 is released there will be only
82+
one clipp-zio artifact maintained.
83+
84+
```scala
85+
libraryDependencies += "io.github.vigoo" %% "clipp-zio-2" % "0.6.2"
86+
```
87+
88+
The only difference currently is that instead of providing the list of arguments as a parameter, Clipp takes it from the
89+
`ZIOAppArgs` layer:
90+
91+
```scala
92+
import io.github.vigoo.clipp._
93+
import io.github.vigoo.clipp.parsers._
94+
import io.github.vigoo.clipp.syntax._
95+
import io.github.vigoo.clipp.zioapi._
96+
97+
import zio._
98+
99+
object Test2 extends ZIOApp {
100+
def run = {
101+
val paramSpec = for {
102+
_ <- metadata("zio-test")
103+
x <- flag("test parameter", 'x')
104+
} yield x
105+
106+
val clippConfig = parametersFromArgs(paramSpec).printUsageInfoOnFailure
107+
val program = for {
108+
x <- parameters[Boolean]
109+
_ <- console.putStrLn(s"x was: $x").ignore
110+
} yield ExitCode.success
111+
112+
program
113+
.injectCustom(clippConfig)
114+
.catchAll { _: ParserFailure => ZIO.succeed(ExitCode.failure) }
115+
}
116+
}
78117
```

0 commit comments

Comments
 (0)