From c49b086153f9570242ee0034c835d9c11ecc0722 Mon Sep 17 00:00:00 2001 From: Danil Zasypkin Date: Mon, 13 May 2024 14:26:59 +0500 Subject: [PATCH] Add zio-json and zio-schema integrations --- build.sbt | 20 ++++++++ .../src/main/scala/derevo/zio/json/zio.scala | 14 ++++++ .../scala/derevo/zio/json/ZioJsonSpec.scala | 50 +++++++++++++++++++ .../main/scala/derevo/zio/schema/zio.scala | 9 ++++ .../scala/derevo/zio/json/ZioSchemaSpec.scala | 25 ++++++++++ project/Dependencies.scala | 8 +++ project/build.properties | 2 +- 7 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 modules/zioJson/src/main/scala/derevo/zio/json/zio.scala create mode 100644 modules/zioJson/src/test/scala/derevo/zio/json/ZioJsonSpec.scala create mode 100644 modules/zioSchema/src/main/scala/derevo/zio/schema/zio.scala create mode 100644 modules/zioSchema/src/test/scala/derevo/zio/json/ZioSchemaSpec.scala diff --git a/build.sbt b/build.sbt index 57529529..b60c3e75 100644 --- a/build.sbt +++ b/build.sbt @@ -51,6 +51,8 @@ lazy val derevo = project tethys, tethysMagnolia, sangria, + zioJson, + zioSchema, tests, ) @@ -164,6 +166,24 @@ lazy val sangria = ) .dependsOn(core) +lazy val zioJson = + (project in file("modules/zioJson")) + .settings(publishSettings) + .settings( + name := "derevo-zio-json", + libraryDependencies ++= Seq(Dependencies.zioJson), + ) + .dependsOn(core) + +lazy val zioSchema = + (project in file("modules/zioSchema")) + .settings(publishSettings) + .settings( + name := "derevo-zio-schema", + libraryDependencies ++= Seq(Dependencies.zioSchema, Dependencies.zioSchemaDerivation), + ) + .dependsOn(core) + lazy val tests = (project in file("modules/tests")) .settings(noPublishSettings) diff --git a/modules/zioJson/src/main/scala/derevo/zio/json/zio.scala b/modules/zioJson/src/main/scala/derevo/zio/json/zio.scala new file mode 100644 index 00000000..0f184c47 --- /dev/null +++ b/modules/zioJson/src/main/scala/derevo/zio/json/zio.scala @@ -0,0 +1,14 @@ +package derevo.zio.json + +import derevo.{Derevo, Derivation, NewTypeDerivation, delegating} +import zio.json._ + +@delegating("zio.json.DeriveJsonDecoder.gen") +object jsonDecoder extends Derivation[JsonDecoder] with NewTypeDerivation[JsonDecoder] { + def instance[A]: JsonDecoder[A] = macro Derevo.delegate[JsonDecoder, A] +} + +@delegating("zio.json.DeriveJsonEncoder.gen") +object jsonEncoder extends Derivation[JsonEncoder] with NewTypeDerivation[JsonEncoder] { + def instance[A]: JsonEncoder[A] = macro Derevo.delegate[JsonEncoder, A] +} diff --git a/modules/zioJson/src/test/scala/derevo/zio/json/ZioJsonSpec.scala b/modules/zioJson/src/test/scala/derevo/zio/json/ZioJsonSpec.scala new file mode 100644 index 00000000..e77b0cbe --- /dev/null +++ b/modules/zioJson/src/test/scala/derevo/zio/json/ZioJsonSpec.scala @@ -0,0 +1,50 @@ +package derevo.zio.json + +import derevo.derive +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import zio.json.JsonCodecConfiguration.SumTypeHandling +import zio.json._ + +@derive(jsonEncoder) +sealed trait Choice + +object Choice { + implicit val Configuration: JsonCodecConfiguration = + JsonCodecConfiguration(SumTypeHandling.DiscriminatorField("type")) +} + +@derive(jsonEncoder) +final case class First(a: Int, b: String) extends Choice + +@derive(jsonEncoder) +final case class Second(c: Boolean, f: Option[Foo]) extends Choice + +@derive(jsonEncoder) +final case class P[A, B](a: A, b: List[B]) + +@derive(jsonEncoder, jsonDecoder) +case class Foo(d: Int, e: String) + +@derive(jsonEncoder) +case class ChoiceList(list: List[Choice], amount: Int) + +class ZioJsonSpec extends AnyFlatSpec with Matchers { + "Encoder derivation for ADT" should "work correctly" in { + val choices = ChoiceList(List(First(1, "lol"), Second(c = true, Some(Foo(1, "kek")))), 2) + + choices.toJson shouldBe """{"list":[{"type":"First","a":1,"b":"lol"},{"type":"Second","c":true,"f":{"d":1,"e":"kek"}}],"amount":2}""" + } + + "Decoder derivation for case class" should "work correctly" in { + val foo = Foo(100, "kek") + + """{"d":100,"e":"kek"}""".fromJson[Foo] shouldBe Right(foo) + } + + "Parametric derivation for case class" should "work correctly" in { + val p = P(123.0, List(1, 2, 3)) + + p.toJson shouldBe """{"a":123.0,"b":[1,2,3]}""" + } +} diff --git a/modules/zioSchema/src/main/scala/derevo/zio/schema/zio.scala b/modules/zioSchema/src/main/scala/derevo/zio/schema/zio.scala new file mode 100644 index 00000000..6cf2e424 --- /dev/null +++ b/modules/zioSchema/src/main/scala/derevo/zio/schema/zio.scala @@ -0,0 +1,9 @@ +package derevo.zio.schema + +import derevo.{Derevo, Derivation, NewTypeDerivation, delegating} +import zio.schema.Schema + +@delegating("zio.schema.DeriveSchema.gen") +object schema extends Derivation[Schema] with NewTypeDerivation[Schema] { + def instance[A]: Schema[A] = macro Derevo.delegate[Schema, A] +} diff --git a/modules/zioSchema/src/test/scala/derevo/zio/json/ZioSchemaSpec.scala b/modules/zioSchema/src/test/scala/derevo/zio/json/ZioSchemaSpec.scala new file mode 100644 index 00000000..316f30f5 --- /dev/null +++ b/modules/zioSchema/src/test/scala/derevo/zio/json/ZioSchemaSpec.scala @@ -0,0 +1,25 @@ +package derevo.zio.json + +import derevo.derive +import derevo.zio.schema.schema +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import zio.schema._ + +@derive(schema) +final case class Person(name: String, age: Int) + +class ZioSchemaSpec extends AnyFlatSpec with Matchers { + "Schema derivation" should "work correctly" in { + val person1 = Person("Gabriel", 45) + val person2 = Person("Gabi", 54) + + val patch: Patch[Person] = Schema[Person].diff(person1, person2) + val inverted: Patch[Person] = patch.invert + + val result1: Either[String, Person] = patch.patch(person1) + val result2: Either[String, Person] = result1.flatMap(inverted.patch) + + result2 shouldBe Right(person1) + } +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index ed4fbe7f..9d93ff70 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -41,6 +41,10 @@ object Dependencies { val macroParadise = "2.1.1" val sangria = "3.2.0" + + val zioJson = "0.6.2" + + val zioSchema = "1.1.1" } lazy val magnolia = "com.propensive" %% "magnolia" % Version.magnolia @@ -66,6 +70,10 @@ object Dependencies { lazy val estatico = "io.estatico" %% "newtype" % Version.estatico lazy val supertagged = "org.rudogma" %% "supertagged" % Version.supertagged + lazy val zioSchema = "dev.zio" %% "zio-schema" % Version.zioSchema + lazy val zioSchemaDerivation = "dev.zio" %% "zio-schema-derivation" % Version.zioSchema + lazy val zioJson = "dev.zio" %% "zio-json" % Version.zioJson + lazy val macroParadise = "org.scalamacros" % "paradise" % Version.macroParadise cross CrossVersion.patch lazy val kindProjector = "org.typelevel" %% "kind-projector" % Version.kindProjector cross CrossVersion.patch diff --git a/project/build.properties b/project/build.properties index 22af2628..04267b14 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.1 +sbt.version=1.9.9