Skip to content

Commit

Permalink
Improve integration with ZIO (zio#6)
Browse files Browse the repository at this point in the history
Name changes, integration with ZIO, reorganization, and some documentation.

Co-authored-by: Ferdinand Svehla <[email protected]>
  • Loading branch information
jdegoes and fsvehla authored Sep 22, 2020
1 parent 598d0fb commit 65a30bb
Show file tree
Hide file tree
Showing 34 changed files with 1,234 additions and 932 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
target/
.bloop
.metals
metals*
*.json
106 changes: 53 additions & 53 deletions README.md

Large diffs are not rendered by default.

79 changes: 47 additions & 32 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
organization := "dev.zio"
name := "zio-json"

val zioVersion = "1.0.1"

crossScalaVersions := Seq("2.12.11", "2.13.3")
scalaVersion := crossScalaVersions.value.last

scalacOptions ++= Seq(
"-language:_",
//"-Xfatal-warnings", // the deprecations cause the compile to fail
"-deprecation",
"-deprecation"
// optimisations slow things down...
//"-opt:l:inline",
//"-opt-inline-from:**"
Expand All @@ -16,20 +18,30 @@ scalacOptions ++= Seq(
scalacOptions in (Compile, console) -= "-Xfatal-warnings"
scalacOptions in (Test, console) -= "-Xfatal-warnings"

libraryDependencies += "com.propensive" %% "magnolia" % "0.16.0"
libraryDependencies += "com.propensive" %% "magnolia" % "0.16.0"
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided

libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.3.2" intransitive()
libraryDependencies += "eu.timepit" %% "refined" % "0.9.15" intransitive()
libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.3.2" intransitive ()
libraryDependencies += "eu.timepit" %% "refined" % "0.9.15" intransitive ()

testFrameworks += new TestFramework("scalaprops.ScalapropsFramework")
libraryDependencies ++= Seq(
"dev.zio" %% "zio" % zioVersion,
"dev.zio" %% "zio-streams" % zioVersion,
"dev.zio" %% "zio-test" % zioVersion % "test",
"dev.zio" %% "zio-test-sbt" % zioVersion % "test"
)

testFrameworks ++= Seq(
new TestFramework("scalaprops.ScalapropsFramework"),
new TestFramework("zio.test.sbt.ZTestFramework")
)
libraryDependencies += "com.github.scalaprops" %% "scalaprops" % "0.8.0" % "test"
parallelExecution in Test := false // scalaprops does not support parallel execution

libraryDependencies += "com.lihaoyi" %% "utest" % "0.7.2" % "test"
testFrameworks += new TestFramework("utest.runner.Framework")

libraryDependencies += "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.5.0" % "test"
libraryDependencies += "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.5.0" % "test"
libraryDependencies += "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.5.0" % "test"

// circe is super easy to install (e_e)
Expand All @@ -39,32 +51,32 @@ libraryDependencies ++= Seq(
"io.circe" %% "circe-generic",
"io.circe" %% "circe-parser"
).map(_ % circeVersion % "test")
libraryDependencies += "io.circe" %% "circe-generic-extras" % "0.13.0" % "test"
libraryDependencies += "org.typelevel" %% "jawn-ast" % "1.0.0" // matches circe
libraryDependencies += "io.circe" %% "circe-generic-extras" % "0.13.0" % "test"
libraryDependencies += "org.typelevel" %% "jawn-ast" % "1.0.0" // matches circe

libraryDependencies += "com.typesafe.play" %% "play-json" % "2.9.0" % "test"
libraryDependencies += "ai.x" %% "play-json-extensions" % "0.42.0" % "test"
libraryDependencies += "com.typesafe.play" %% "play-json" % "2.9.0" % "test"
libraryDependencies += "ai.x" %% "play-json-extensions" % "0.42.0" % "test"

// scalafmtOnCompile := true

enablePlugins(NeoJmhPlugin)
inConfig(Jmh)(org.scalafmt.sbt.ScalafmtPlugin.scalafmtConfigSettings)

sourceGenerators in Compile += Def.task {
val dir = (sourceManaged in Compile).value
val dir = (sourceManaged in Compile).value
val file = dir / "zio" / "json" / "GeneratedTupleDecoders.scala"
val decoders = (1 to 22).map { i =>
val tparams = (1 to i).map(p => s"A$p").mkString(", ")
val implicits = (1 to i).map(p => s"A$p: Decoder[A$p]").mkString(", ")
val work = (1 to i).map { p =>
s"val a$p = A$p.unsafeDecode(traces($p) :: trace, in)"
}.mkString("\n Lexer.char(trace, in, ',')\n ")
val tparams = (1 to i).map(p => s"A$p").mkString(", ")
val implicits = (1 to i).map(p => s"A$p: JsonDecoder[A$p]").mkString(", ")
val work = (1 to i)
.map(p => s"val a$p = A$p.unsafeDecode(trace :+ traces($p), in)")
.mkString("\n Lexer.char(trace, in, ',')\n ")
val returns = (1 to i).map(p => s"a$p").mkString(", ")

s"""implicit def tuple${i}[$tparams](implicit $implicits): Decoder[Tuple${i}[$tparams]] =
| new Decoder[Tuple${i}[$tparams]] {
s"""implicit def tuple${i}[$tparams](implicit $implicits): JsonDecoder[Tuple${i}[$tparams]] =
| new JsonDecoder[Tuple${i}[$tparams]] {
| val traces: Array[JsonError] = (0 to $i).map(JsonError.ArrayAccess(_)).toArray
| def unsafeDecode(trace: List[JsonError], in: RetractReader): Tuple${i}[$tparams] = {
| def unsafeDecode(trace: Chunk[JsonError], in: RetractReader): Tuple${i}[$tparams] = {
| Lexer.char(trace, in, '[')
| $work
| Lexer.char(trace, in, ']')
Expand All @@ -76,26 +88,28 @@ sourceGenerators in Compile += Def.task {
file,
s"""package zio.json
|
|import zio.Chunk
|import zio.json.internal._
|
|private[json] trait GeneratedTupleDecoders { this: Decoder.type =>
|private[json] trait GeneratedTupleDecoders { this: JsonDecoder.type =>
| ${decoders.mkString("\n\n ")}
|}""".stripMargin)
|}""".stripMargin
)
Seq(file)
}.taskValue

sourceGenerators in Compile += Def.task {
val dir = (sourceManaged in Compile).value
val dir = (sourceManaged in Compile).value
val file = dir / "zio" / "json" / "GeneratedTupleEncoders.scala"
val encoders = (1 to 22).map { i =>
val tparams = (1 to i).map(p => s"A$p").mkString(", ")
val implicits = (1 to i).map(p => s"A$p: Encoder[A$p]").mkString(", ")
val work = (1 to i).map { p =>
s"A$p.unsafeEncode(t._$p, indent, out)"
}.mkString("\n if (indent.isEmpty) out.write(\",\") else out.write(\", \")\n ")

s"""implicit def tuple${i}[$tparams](implicit $implicits): Encoder[Tuple${i}[$tparams]] =
| new Encoder[Tuple${i}[$tparams]] {
val tparams = (1 to i).map(p => s"A$p").mkString(", ")
val implicits = (1 to i).map(p => s"A$p: JsonEncoder[A$p]").mkString(", ")
val work = (1 to i)
.map(p => s"A$p.unsafeEncode(t._$p, indent, out)")
.mkString("\n if (indent.isEmpty) out.write(\",\") else out.write(\", \")\n ")

s"""implicit def tuple${i}[$tparams](implicit $implicits): JsonEncoder[Tuple${i}[$tparams]] =
| new JsonEncoder[Tuple${i}[$tparams]] {
| def unsafeEncode(t: Tuple${i}[$tparams], indent: Option[Int], out: java.io.Writer): Unit = {
| out.write("[")
| $work
Expand All @@ -107,8 +121,9 @@ sourceGenerators in Compile += Def.task {
file,
s"""package zio.json
|
|private[json] trait GeneratedTupleEncoders { this: Encoder.type =>
|private[json] trait GeneratedTupleEncoders { this: JsonEncoder.type =>
| ${encoders.mkString("\n\n ")}
|}""".stripMargin)
|}""".stripMargin
)
Seq(file)
}.taskValue
3 changes: 1 addition & 2 deletions project/NeoJmhPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ object NeoJmhPlugin extends AutoPlugin {
dependencyClasspathAsJars in NeoJmhPlugin.JmhInternal ++= (fullClasspathAsJars in NeoJmhKeys.Jmh).value
)

def generateBenchmarkSourcesAndResources
: Def.Initialize[Task[(Seq[File], Seq[File])]] = Def.task {
def generateBenchmarkSourcesAndResources: Def.Initialize[Task[(Seq[File], Seq[File])]] = Def.task {
val s = streams.value
val cacheDir = crossTarget.value / "jmh-cache"
val bytecodeDir = (classDirectory in Jmh).value
Expand Down
13 changes: 6 additions & 7 deletions src/jmh/scala/zio/json/GeoJSONBenchmarks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import com.github.plokhotnyuk.jsoniter_scala.core._
import com.github.plokhotnyuk.jsoniter_scala.macros._
import io.circe
import zio.json.GeoJSONBenchmarks._
import zio.json.TestUtils._
//import zio.json.data.geojson.generated._
import zio.json.data.geojson.handrolled._
import testzio.json.TestUtils._
import testzio.json.data.geojson.handrolled._
import org.openjdk.jmh.annotations._
import play.api.libs.{ json => Play }

Expand Down Expand Up @@ -109,22 +108,22 @@ class GeoJSONBenchmarks {

@Benchmark
def decodeZioSuccess1(): Either[String, GeoJSON] =
json.parser.decode[GeoJSON](jsonChars1)
jsonChars1.fromJson[GeoJSON]

@Benchmark
def decodeZioSuccess2(): Either[String, GeoJSON] =
json.parser.decode[GeoJSON](jsonChars2)
jsonChars2.fromJson[GeoJSON]

@Benchmark
def encodeZio(): String = {
import zio.json.syntax._
import zio.json._

decoded.toJson
}

@Benchmark
def decodeZioError(): Either[String, GeoJSON] =
json.parser.decode[GeoJSON](jsonCharsErr)
jsonCharsErr.fromJson[GeoJSON]

}

Expand Down
24 changes: 12 additions & 12 deletions src/jmh/scala/zio/json/GoogleMapsAPIBenchmarks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import com.github.plokhotnyuk.jsoniter_scala.core._
import com.github.plokhotnyuk.jsoniter_scala.macros._
import io.circe
import zio.json.GoogleMapsAPIBenchmarks._
import zio.json.TestUtils._
import zio.json.data.googlemaps._
import testzio.json.TestUtils._
import testzio.json.data.googlemaps._
import org.openjdk.jmh.annotations._
import play.api.libs.{ json => Play }

Expand Down Expand Up @@ -238,46 +238,46 @@ class GoogleMapsAPIBenchmarks {

@Benchmark
def decodeZioSuccess1(): Either[String, DistanceMatrix] =
json.parser.decode[DistanceMatrix](jsonChars)
jsonChars.fromJson[DistanceMatrix]

@Benchmark
def decodeZioSuccess2(): Either[String, DistanceMatrix] =
json.parser.decode[DistanceMatrix](jsonCharsCompact)
jsonCharsCompact.fromJson[DistanceMatrix]

@Benchmark
def encodeZio(): String = {
import zio.json.syntax._
import zio.json._

decoded.toJson
}

// @Benchmark
// def decodeZioError(): Either[String, DistanceMatrix] =
// json.parser.decode[DistanceMatrix](jsonCharsErr)
// jsonCharsErr.fromJson[DistanceMatrix]

@Benchmark
def decodeZioErrorParse(): Either[String, DistanceMatrix] =
json.parser.decode[DistanceMatrix](jsonCharsErrParse)
jsonCharsErrParse.fromJson[DistanceMatrix]

@Benchmark
def decodeZioErrorNumber(): Either[String, DistanceMatrix] =
json.parser.decode[DistanceMatrix](jsonCharsErrNumber)
jsonCharsErrNumber.fromJson[DistanceMatrix]

@Benchmark
def decodeZioAttack0(): Either[String, DistanceMatrix] =
json.parser.decode[DistanceMatrix](jsonCharsAttack0)
jsonCharsAttack0.fromJson[DistanceMatrix]

@Benchmark
def decodeZioAttack1(): Either[String, DistanceMatrix] =
json.parser.decode[DistanceMatrix](jsonCharsAttack1)
jsonCharsAttack1.fromJson[DistanceMatrix]

@Benchmark
def decodeZioAttack2(): Either[String, DistanceMatrix] =
json.parser.decode[DistanceMatrix](jsonCharsAttack2)
jsonCharsAttack2.fromJson[DistanceMatrix]

@Benchmark
def decodeZioAttack3(): Either[String, DistanceMatrix] =
json.parser.decode[DistanceMatrix](jsonCharsAttack3)
jsonCharsAttack3.fromJson[DistanceMatrix]

}

Expand Down
18 changes: 9 additions & 9 deletions src/jmh/scala/zio/json/SyntheticBenchmarks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ import com.github.plokhotnyuk.jsoniter_scala.core._
import com.github.plokhotnyuk.jsoniter_scala.macros._
import io.circe
import zio.json.SyntheticBenchmarks._
import zio.json.TestUtils._
import testzio.json.TestUtils._
import org.openjdk.jmh.annotations._
import play.api.libs.{ json => Play }

import scala.util.Try

final case class Nested(n: Option[Nested])
object Nested {
implicit lazy val zioJsonDecoder: json.Decoder[Nested] =
json.MagnoliaDecoder.gen
implicit lazy val zioJsonEncoder: json.Encoder[Nested] =
json.MagnoliaEncoder.gen
implicit lazy val zioJsonJsonDecoder: JsonDecoder[Nested] =
DeriveJsonDecoder.gen
implicit lazy val zioJsonEncoder: JsonEncoder[Nested] =
DeriveJsonEncoder.gen

implicit val customConfig: circe.generic.extras.Configuration =
circe.generic.extras.Configuration.default
.copy(discriminator = Some("type"))
implicit lazy val circeDecoder: circe.Decoder[Nested] =
implicit lazy val circeJsonDecoder: circe.Decoder[Nested] =
circe.generic.extras.semiauto.deriveConfiguredDecoder[Nested]
implicit lazy val circeEncoder: circe.Encoder[Nested] =
circe.generic.extras.semiauto.deriveConfiguredEncoder[Nested]
Expand Down Expand Up @@ -68,7 +68,7 @@ class SyntheticBenchmarks {
@Benchmark
def decodeJsoniterSuccess(): Either[String, Nested] =
Try(readFromArray(jsonString.getBytes(UTF_8)))
.fold(t => Left(t.toString), Right.apply)
.fold(t => Left(t.toString), Right(_))

@Benchmark
def decodeCirceSuccess(): Either[circe.Error, Nested] =
Expand All @@ -92,11 +92,11 @@ class SyntheticBenchmarks {

@Benchmark
def decodeZioSuccess(): Either[String, Nested] =
json.parser.decode[Nested](jsonChars)
jsonChars.fromJson[Nested]

@Benchmark
def encodeZio(): String = {
import zio.json.syntax._
import zio.json._

decoded.toJson
}
Expand Down
16 changes: 8 additions & 8 deletions src/jmh/scala/zio/json/TwitterAPIBenchmarks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import zio.json
import com.github.plokhotnyuk.jsoniter_scala.core._
import com.github.plokhotnyuk.jsoniter_scala.macros._
import io.circe
import zio.json.TestUtils._
import zio.json.data.twitter._
import testzio.json.TestUtils._
import testzio.json.data.twitter._
import org.openjdk.jmh.annotations._
import play.api.libs.{ json => Play }
import TwitterAPIBenchmarks._
Expand Down Expand Up @@ -107,29 +107,29 @@ class TwitterAPIBenchmarks {

@Benchmark
def decodeZioSuccess1(): Either[String, List[Tweet]] =
json.parser.decode[List[Tweet]](jsonChars)
jsonChars.fromJson[List[Tweet]]

@Benchmark
def decodeZioSuccess2(): Either[String, List[Tweet]] =
json.parser.decode[List[Tweet]](jsonCharsCompact)
jsonCharsCompact.fromJson[List[Tweet]]

@Benchmark
def encodeZio(): String = {
import zio.json.syntax._
import zio.json._

decoded.toJson
}

@Benchmark
def decodeZioError(): Either[String, List[Tweet]] =
json.parser.decode[List[Tweet]](jsonCharsErr)
jsonCharsErr.fromJson[List[Tweet]]

}

object TwitterAPIBenchmarks {
// these avoid the List implicit from being recreated every time
implicit val zListTweet: json.Decoder[List[Tweet]] =
json.Decoder.list[Tweet]
implicit val zListTweet: JsonDecoder[List[Tweet]] =
JsonDecoder.list[Tweet]
implicit val cListTweet: circe.Decoder[List[Tweet]] =
circe.Decoder.decodeList[Tweet]
implicit val codec: JsonValueCodec[List[Tweet]] =
Expand Down
6 changes: 3 additions & 3 deletions src/jmh/scala/zio/json/internal/SafeNumbersBenchmarks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -183,18 +183,18 @@ class SafeNumbersBenchBigDecimal {

@Benchmark
def decodeFommilValid(): Array[Option[java.math.BigDecimal]] =
valids.map(SafeNumbers.bigdecimal(_))
valids.map(SafeNumbers.bigDecimal(_))

@Benchmark
def decodeFommilUnsafeValid(): Array[java.math.BigDecimal] =
valids.map(UnsafeNumbers.bigdecimal(_, 128))
valids.map(UnsafeNumbers.bigDecimal(_, 128))

@Benchmark
def decodeStdlibInvalid(): Array[Option[java.math.BigDecimal]] =
invalids.map(stdlib)

@Benchmark
def decodeFommilInvalid(): Array[Option[java.math.BigDecimal]] =
invalids.map(SafeNumbers.bigdecimal(_))
invalids.map(SafeNumbers.bigDecimal(_))

}
Loading

0 comments on commit 65a30bb

Please sign in to comment.