From 7554a8697dffb2ed6c1e33f678472bb6f72576c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Femen=C3=ADa?= <131800808+pablf@users.noreply.github.com> Date: Mon, 4 Sep 2023 19:15:25 +0200 Subject: [PATCH] Scala native support (#1010) --- .github/workflows/ci.yml | 12 ++-- build.sbt | 58 ++++++++++++++----- .../zio/JsonPackagePlatformSpecific.scala | 3 + .../json/JsonDecoderPlatformSpecific.scala | 3 + .../json/JsonEncoderPlatformSpecific.scala | 3 + .../src/test/scala/zio/json/CodecSpec.scala | 14 ++++- .../test/scala/zio/json/JavaTimeSpec.scala | 10 ++-- 7 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 zio-json/native/src/main/scala/zio/JsonPackagePlatformSpecific.scala create mode 100644 zio-json/native/src/main/scala/zio/json/JsonDecoderPlatformSpecific.scala create mode 100644 zio-json/native/src/main/scala/zio/json/JsonEncoderPlatformSpecific.scala diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5938e403b..a5b0c4b06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,7 +80,7 @@ jobs: matrix: java: ['8', '11', '17'] scala: ['2.12.18', '2.13.11', '3.3.0'] - platform: ['JVM', 'JS'] + platform: ['JVM', 'JS', 'Native'] steps: - name: Checkout current branch uses: actions/checkout@v3.3.0 @@ -94,12 +94,14 @@ jobs: check-latest: true - name: Cache scala dependencies uses: coursier/cache-action@v6 - - name: Run tests + - name: Install libuv + if: matrix.platform == 'Native' + run: sudo apt-get update && sudo apt-get install -y libuv1-dev + - name: Run Macros tests if: ${{ !startsWith(matrix.scala, '3.3.') }} + run: sbt ++${{ matrix.scala }}! testScala2${{ matrix.platform }} + - name: Run tests run: sbt ++${{ matrix.scala }}! test${{ matrix.platform }} - - name: Run Dotty tests - if: ${{ startsWith(matrix.scala, '3.3.') && matrix.platform == 'JVM' }} - run: sbt ++${{ matrix.scala }}! testJVMDotty ci: runs-on: ubuntu-20.04 diff --git a/build.sbt b/build.sbt index f36e05e24..c01b8261a 100644 --- a/build.sbt +++ b/build.sbt @@ -27,15 +27,33 @@ addCommandAlias("prepare", "fmt") addCommandAlias( "testJVM", - "zioJsonJVM/test; zioJsonYaml/test; zioJsonMacrosJVM/test; zioJsonInteropHttp4s/test; zioJsonInteropScalaz7xJVM/test; zioJsonGolden/test; zioJsonInteropScalaz7xJS/test; zioJsonInteropRefinedJVM/test; zioJsonInteropRefinedJS/test" + "zioJsonJVM/test; zioJsonYaml/test; zioJsonInteropHttp4s/test; zioJsonInteropScalaz7xJVM/test; zioJsonGolden/test" ) addCommandAlias( - "testJVMDotty", - "zioJsonJVM/test" + "testScala2JVM", + "zioJsonMacrosJVM/test; zioJsonInteropRefinedJVM/test" ) -addCommandAlias("testJS", "zioJsonJS/test") +addCommandAlias( + "testScala2JS", + "zioJsonMacrosJS/test; zioJsonInteropRefinedJS/test" +) + +addCommandAlias( + "testScala2Native", + "zioJsonMacrosNative/test; zioJsonInteropRefinedNative/test" +) + +addCommandAlias( + "testJS", + "zioJsonJS/test; zioJsonInteropScalaz7xJS/test" +) + +addCommandAlias( + "testNative", + "zioJsonNative/test; zioJsonInteropScalaz7xNative/test" +) val zioVersion = "2.0.16" @@ -49,20 +67,24 @@ lazy val zioJsonRoot = project docs, zioJsonJVM, zioJsonJS, + zioJson.native, zioJsonYaml, zioJsonMacrosJVM, zioJsonMacrosJS, + zioJsonMacros.native, zioJsonInteropHttp4s, zioJsonInteropRefined.js, zioJsonInteropRefined.jvm, + zioJsonInteropRefined.native, zioJsonInteropScalaz7x.js, zioJsonInteropScalaz7x.jvm, + zioJsonInteropScalaz7x.native, zioJsonGolden ) val circeVersion = "0.14.3" -lazy val zioJson = crossProject(JSPlatform, JVMPlatform) +lazy val zioJson = crossProject(JSPlatform, JVMPlatform, NativePlatform) .in(file("zio-json")) .settings(stdSettings("zio-json")) .settings(crossProjectSettings) @@ -103,7 +125,6 @@ lazy val zioJson = crossProject(JSPlatform, JVMPlatform) "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided, "com.softwaremill.magnolia1_2" %%% "magnolia" % "1.1.3", "io.circe" %%% "circe-generic-extras" % circeVersion % "test", - "com.typesafe.play" %%% "play-json" % "2.9.4" % "test", "com.github.plokhotnyuk.jsoniter-scala" %%% "jsoniter-scala-core" % "2.23.3" % "test", "com.github.plokhotnyuk.jsoniter-scala" %%% "jsoniter-scala-macros" % "2.23.3" % "test" ) @@ -211,12 +232,19 @@ lazy val zioJson = crossProject(JSPlatform, JVMPlatform) case _ => Seq( - "ai.x" %% "play-json-extensions" % "0.42.0" % "test", - "org.typelevel" %% "jawn-ast" % "1.5.1" % "test" + "ai.x" %% "play-json-extensions" % "0.42.0" % "test", + "com.typesafe.play" %%% "play-json" % "2.9.4" % "test", + "org.typelevel" %% "jawn-ast" % "1.5.1" % "test" ) } } ) + .nativeSettings(Test / fork := false) + .nativeSettings( + libraryDependencies ++= Seq( + "io.github.cquiroz" %%% "scala-java-time" % "2.5.0" + ) + ) .enablePlugins(BuildInfoPlugin) lazy val zioJsonJS = zioJson.js @@ -260,8 +288,9 @@ lazy val zioJsonYaml = project .dependsOn(zioJsonJVM) .enablePlugins(BuildInfoPlugin) -lazy val zioJsonMacros = crossProject(JSPlatform, JVMPlatform) +lazy val zioJsonMacros = crossProject(JSPlatform, JVMPlatform, NativePlatform) .in(file("zio-json-macros")) + .nativeConfigure(_.dependsOn(zioJson.native)) .settings(stdSettings("zio-json-macros")) .settings(crossProjectSettings) .settings(macroExpansionSettings) @@ -275,6 +304,7 @@ lazy val zioJsonMacros = crossProject(JSPlatform, JVMPlatform) ), testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework") ) + .nativeSettings(Test / fork := false) lazy val zioJsonMacrosJVM = zioJsonMacros.jvm.dependsOn(zioJsonJVM) @@ -301,10 +331,9 @@ lazy val zioJsonInteropHttp4s = project .dependsOn(zioJsonJVM) .enablePlugins(BuildInfoPlugin) -lazy val zioJsonInteropRefined = crossProject(JSPlatform, JVMPlatform) +lazy val zioJsonInteropRefined = crossProject(JSPlatform, JVMPlatform, NativePlatform) .in(file("zio-json-interop-refined")) - .jvmConfigure(_.dependsOn(zioJsonJVM)) - .jsConfigure(_.dependsOn(zioJsonJS)) + .dependsOn(zioJson) .settings(stdSettings("zio-json-interop-refined")) .settings(buildInfoSettings("zio.json.interop.refined")) .settings( @@ -317,10 +346,9 @@ lazy val zioJsonInteropRefined = crossProject(JSPlatform, JVMPlatform) ) .enablePlugins(BuildInfoPlugin) -lazy val zioJsonInteropScalaz7x = crossProject(JSPlatform, JVMPlatform) +lazy val zioJsonInteropScalaz7x = crossProject(JSPlatform, JVMPlatform, NativePlatform) .in(file("zio-json-interop-scalaz7x")) - .jvmConfigure(_.dependsOn(zioJsonJVM)) - .jsConfigure(_.dependsOn(zioJsonJS)) + .dependsOn(zioJson) .settings(stdSettings("zio-json-interop-scalaz7x")) .settings(buildInfoSettings("zio.json.interop.scalaz7x")) .settings( diff --git a/zio-json/native/src/main/scala/zio/JsonPackagePlatformSpecific.scala b/zio-json/native/src/main/scala/zio/JsonPackagePlatformSpecific.scala new file mode 100644 index 000000000..846000c41 --- /dev/null +++ b/zio-json/native/src/main/scala/zio/JsonPackagePlatformSpecific.scala @@ -0,0 +1,3 @@ +package zio + +trait JsonPackagePlatformSpecific {} diff --git a/zio-json/native/src/main/scala/zio/json/JsonDecoderPlatformSpecific.scala b/zio-json/native/src/main/scala/zio/json/JsonDecoderPlatformSpecific.scala new file mode 100644 index 000000000..d0fc4eb8e --- /dev/null +++ b/zio-json/native/src/main/scala/zio/json/JsonDecoderPlatformSpecific.scala @@ -0,0 +1,3 @@ +package zio.json + +trait JsonDecoderPlatformSpecific[A] { self: JsonDecoder[A] => } diff --git a/zio-json/native/src/main/scala/zio/json/JsonEncoderPlatformSpecific.scala b/zio-json/native/src/main/scala/zio/json/JsonEncoderPlatformSpecific.scala new file mode 100644 index 000000000..57ca8ecb7 --- /dev/null +++ b/zio-json/native/src/main/scala/zio/json/JsonEncoderPlatformSpecific.scala @@ -0,0 +1,3 @@ +package zio.json + +trait JsonEncoderPlatformSpecific[A] { self: JsonEncoder[A] => } diff --git a/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala b/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala index 75ca1e8c6..c05d08986 100644 --- a/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala +++ b/zio-json/shared/src/test/scala/zio/json/CodecSpec.scala @@ -103,7 +103,6 @@ object CodecSpec extends ZIOSpecDefault { assert(snakedLegacy.fromJson[legacy.Snaked])(isRight(equalTo(legacy.Snaked("")))) && assert(pascaled.fromJson[Pascaled])(isRight(equalTo(Pascaled("")))) && assert(cameled.fromJson[Cameled])(isRight(equalTo(Cameled("")))) && - assert(indianaJones.fromJson[Custom])(isRight(equalTo(Custom("")))) && assert(overrides.fromJson[OverridesAlsoWork])(isRight(equalTo(OverridesAlsoWork("", 0)))) && assertTrue(Kebabed("").toJson == kebabed) && assertTrue(Kebabed("").toJsonAST.toOption.get == kebabed.fromJson[Json].toOption.get) && @@ -117,10 +116,19 @@ object CodecSpec extends ZIOSpecDefault { assertTrue(Pascaled("").toJsonAST.toOption.get == pascaled.fromJson[Json].toOption.get) && assertTrue(Cameled("").toJson == cameled) && assertTrue(Cameled("").toJsonAST.toOption.get == cameled.fromJson[Json].toOption.get) && - assertTrue(Custom("").toJson == indianaJones) && - assertTrue(Custom("").toJsonAST.toOption.get == indianaJones.fromJson[Json].toOption.get) && assertTrue(OverridesAlsoWork("", 0).toJson == overrides) }, + test("key transformation - except native") { + // Problem in scala-native seems to be with implementation of package scala.scalanative.regex.Parser, in particular method `parsePerlFlags`. + // It should be fixed from there. Until then, better not to use regex. + + import exampletransformkeys._ + val indianaJones = """{"wHATcASEiStHIS":""}""" + + assert(indianaJones.fromJson[Custom])(isRight(equalTo(Custom("")))) && + assertTrue(Custom("").toJson == indianaJones) && + assertTrue(Custom("").toJsonAST.toOption.get == indianaJones.fromJson[Json].toOption.get) + } @@ TestAspect.exceptNative, test("unicode") { assert(""""€🐵🥰"""".fromJson[String])(isRight(equalTo("€🐵🥰"))) }, diff --git a/zio-json/shared/src/test/scala/zio/json/JavaTimeSpec.scala b/zio-json/shared/src/test/scala/zio/json/JavaTimeSpec.scala index c761b134d..7684e8187 100644 --- a/zio-json/shared/src/test/scala/zio/json/JavaTimeSpec.scala +++ b/zio-json/shared/src/test/scala/zio/json/JavaTimeSpec.scala @@ -345,8 +345,9 @@ object JavaTimeSpec extends ZIOSpecDefault { assert(stringify("foody").fromJson[DayOfWeek])( isLeft( equalTo("(No enum constant java.time.DayOfWeek.FOODY)") || // JVM - equalTo("(Unrecognized day of week name: FOODY)") - ) // Scala.js + equalTo("(Unrecognized day of week name: FOODY)") || // Scala.js 2. + equalTo("(enum case not found: FOODY)") // Scala.js 3. + ) ) }, test("Duration") { @@ -1498,8 +1499,9 @@ object JavaTimeSpec extends ZIOSpecDefault { assert(stringify("FebTober").fromJson[Month])( isLeft( equalTo("(No enum constant java.time.Month.FEBTOBER)") || // JVM - equalTo("(Unrecognized month name: FEBTOBER)") - ) // Scala.js + equalTo("(Unrecognized month name: FEBTOBER)") || // Scala.js 2. + equalTo("(enum case not found: FEBTOBER)") // Scala.js 3. + ) ) }, test("MonthDay") {