Skip to content

Commit

Permalink
Upgrade to ZIO 2.0 (#512)
Browse files Browse the repository at this point in the history
* upgrade to zio 2
* update scala 3 version
* update dependencies
  • Loading branch information
adamgfraser authored Dec 18, 2021
1 parent 4850bcc commit c08b282
Show file tree
Hide file tree
Showing 23 changed files with 373 additions and 368 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ jobs:
fail-fast: false
matrix:
java: ['[email protected]', '[email protected]']
scala: ['2.12.14', '2.13.6', '3.0.2']
scala: ['2.12.14', '2.13.6', '3.1.0']
platform: ['JVM', 'JS']
steps:
- name: Checkout current branch
Expand All @@ -83,10 +83,10 @@ jobs:
- name: Cache scala dependencies
uses: coursier/cache-action@v6
- name: Run tests
if: ${{ !startsWith(matrix.scala, '3.0.') }}
if: ${{ !startsWith(matrix.scala, '3.1.') }}
run: sbt ++${{ matrix.scala }}! test${{ matrix.platform }}
- name: Run Dotty tests
if: ${{ startsWith(matrix.scala, '3.0.') && matrix.platform == 'JVM' }}
if: ${{ startsWith(matrix.scala, '3.1.') && matrix.platform == 'JVM' }}
run: sbt ++${{ matrix.scala }}! testJVMDotty

ci:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ This attack is very effective in schemas with lots of numbers, causing ops/sec t

# Even Moar Performance

If `zio-json` isn't fast enough for you, then try out [jsoniter-scala](https://github.com/plokhotnyuk/jsoniter-scala); whereas `zio-json` is fully integrated into ZIO, including streams and transducer support, jsoniter is library agnostic.
If `zio-json` isn't fast enough for you, then try out [jsoniter-scala](https://github.com/plokhotnyuk/jsoniter-scala); whereas `zio-json` is fully integrated into ZIO, including streams and pipeline support, jsoniter is library agnostic.

JSON is an inefficient transport format and everybody would benefit from a port of this library to msgpack or protobuf. For legacy services, a port supporting XML is also be possible.

Expand Down
12 changes: 6 additions & 6 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ addCommandAlias(

addCommandAlias("testJS", "zioJsonJS/test")

val zioVersion = "1.0.12"
val zioVersion = "2.0.0-RC1"

lazy val root = project
.in(file("."))
Expand Down Expand Up @@ -265,12 +265,12 @@ lazy val zioJsonInteropHttp4s = project
.settings(
crossScalaVersions --= Vector("3.0.2"),
libraryDependencies ++= Seq(
"org.http4s" %% "http4s-dsl" % "0.21.31",
"org.http4s" %% "http4s-dsl" % "0.23.7",
"dev.zio" %% "zio" % zioVersion,
"org.typelevel" %% "cats-effect" % "2.5.4",
"dev.zio" %% "zio-interop-cats" % "2.5.1.0" % "test",
"dev.zio" %% "zio-test" % zioVersion % "test",
"dev.zio" %% "zio-test-sbt" % zioVersion % "test"
"org.typelevel" %% "cats-effect" % "3.3.0",
"dev.zio" %% "zio-interop-cats" % "3.3.0-RC1" % "test",
"dev.zio" %% "zio-test" % zioVersion % "test",
"dev.zio" %% "zio-test-sbt" % zioVersion % "test"
),
testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework")
)
Expand Down
4 changes: 2 additions & 2 deletions project/BuildHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object BuildHelper {
}
val Scala212: String = versions("2.12")
val Scala213: String = versions("2.13")
val ScalaDotty: String = "3.0.2"
val ScalaDotty: String = "3.1.0"

val SilencerVersion = "1.7.7"

Expand Down Expand Up @@ -139,7 +139,7 @@ object BuildHelper {

def extraOptions(scalaVersion: String, optimize: Boolean) =
CrossVersion.partialVersion(scalaVersion) match {
case Some((3, 0)) =>
case Some((3, 1)) =>
Seq(
"-language:implicitConversions",
"-Xignore-scala2-macros"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package zio.json.interop.http4s

import org.http4s._
import org.typelevel.ci._
import zio.Task
import zio.interop.catz._
import zio.json._
Expand All @@ -15,8 +16,8 @@ object ZIOJsonInstancesSpec extends DefaultRunnableSpec {

def spec: ZSpec[Environment, Failure] = suite("json instances")(
suite("jsonEncoderOf") {
testM("returns an EntityEncoder that can encode for the given effect and type") {
checkM(Gen.anyString, Gen.anyInt) { (s, i) =>
test("returns an EntityEncoder that can encode for the given effect and type") {
check(Gen.string, Gen.int) { (s, i) =>
val result = jsonEncoderOf[Task, Test]
.toEntity(Test(s, i))
.body
Expand All @@ -29,37 +30,37 @@ object ZIOJsonInstancesSpec extends DefaultRunnableSpec {
}
},
suite("jsonOf")(
testM("returns an EntityDecoder that can decode for the given effect and type")(
checkM(Gen.anyString, Gen.anyInt) { (s, i) =>
test("returns an EntityDecoder that can decode for the given effect and type")(
check(Gen.string, Gen.int) { (s, i) =>
val media = Request[Task]()
.withEntity(s"""{"string":"$s","int":$i}""")
.withHeaders(Header("Content-Type", "application/json"))
.withHeaders(Header.Raw(ci"Content-Type", "application/json"))

assertM(jsonOf[Task, Test].decode(media, true).value)(isRight(equalTo(Test(s, i))))
}
),
testM("returns MalformedMessageBodyFailure when json is empty") {
test("returns MalformedMessageBodyFailure when json is empty") {
val media = Request[Task]()
.withEntity("")
.withHeaders(Header("Content-Type", "application/json"))
.withHeaders(Header.Raw(ci"Content-Type", "application/json"))

assertM(jsonOf[Task, Test].decode(media, true).value)(
isLeft(equalTo(MalformedMessageBodyFailure("Invalid JSON: empty body")))
)
},
testM("returns MalformedMessageBodyFailure when json is invalid") {
test("returns MalformedMessageBodyFailure when json is invalid") {
val media = Request[Task]()
.withEntity("""{"bad" "json"}""")
.withHeaders(Header("Content-Type", "application/json"))
.withHeaders(Header.Raw(ci"Content-Type", "application/json"))

assertM(jsonOf[Task, Test].decode(media, true).value)(
isLeft(equalTo(MalformedMessageBodyFailure("(expected ':' got '\"')")))
)
},
testM("returns MalformedMessageBodyFailure when message body is not a json") {
test("returns MalformedMessageBodyFailure when message body is not a json") {
val media = Request[Task]()
.withEntity("not a json")
.withHeaders(Header("Content-Type", "text/plain"))
.withHeaders(Header.Raw(ci"Content-Type", "text/plain"))

assertM(jsonOf[Task, Test].decode(media, true).value)(isLeft(isSubtype[MediaTypeMismatch](anything)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package testzio.json
import zio.json._
import zio.test.Assertion._
import zio.test._
import zio.test.environment.TestEnvironment

object DeriveSpec extends DefaultRunnableSpec {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package zio.json.yaml
import zio.json.yaml.YamlEncoderSpec.{ Example, ex1, ex1Yaml, ex1Yaml2 }
import zio.test.Assertion.{ equalTo, isRight }
import zio.test._
import zio.test.environment.TestEnvironment

object YamlDecoderSpec extends DefaultRunnableSpec {
override def spec: ZSpec[TestEnvironment, Any] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import zio.json._
import zio.json.ast.Json
import zio.test.Assertion._
import zio.test._
import zio.test.environment.TestEnvironment

object YamlEncoderSpec extends DefaultRunnableSpec {
override def spec: ZSpec[TestEnvironment, Any] =
Expand Down
2 changes: 1 addition & 1 deletion zio-json/jvm/src/jmh/scala/zio/json/UUIDBenchmarks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package zio.json
import org.openjdk.jmh.annotations._
import zio.Chunk
import zio.json.uuid.UUIDParser
import zio.random.Random
import zio.Random
import zio.test.Gen

import java.util.UUID
Expand Down
95 changes: 57 additions & 38 deletions zio-json/jvm/src/main/scala/zio/JsonPackagePlatformSpecific.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package zio

import zio.blocking.Blocking
import zio.json.{ JsonDecoder, JsonEncoder, JsonStreamDelimiter, ast }
import zio.stream._

Expand All @@ -10,84 +9,104 @@ import java.nio.charset.StandardCharsets
import java.nio.file.{ Path, Paths }

trait JsonPackagePlatformSpecific {
def readJsonAs(file: File): ZStream[Blocking, Throwable, ast.Json] =
def readJsonAs(file: File): ZStream[Any, Throwable, ast.Json] =
readJsonLinesAs[ast.Json](file)

def readJsonAs(path: Path): ZStream[Blocking, Throwable, ast.Json] =
def readJsonAs(path: Path): ZStream[Any, Throwable, ast.Json] =
readJsonLinesAs[ast.Json](path)

def readJsonAs(path: String): ZStream[Blocking, Throwable, ast.Json] =
def readJsonAs(path: String): ZStream[Any, Throwable, ast.Json] =
readJsonLinesAs[ast.Json](path)

def readJsonAs(url: URL): ZStream[Blocking, Throwable, ast.Json] =
def readJsonAs(url: URL): ZStream[Any, Throwable, ast.Json] =
readJsonLinesAs[ast.Json](url)

def readJsonLinesAs[A: JsonDecoder](file: File): ZStream[Blocking, Throwable, A] =
def readJsonLinesAs[A: JsonDecoder](file: File): ZStream[Any, Throwable, A] =
readJsonLinesAs(file.toPath)

def readJsonLinesAs[A: JsonDecoder](path: Path): ZStream[Blocking, Throwable, A] =
def readJsonLinesAs[A: JsonDecoder](path: Path): ZStream[Any, Throwable, A] =
ZStream
.fromFile(path)
.transduce(
ZTransducer.utf8Decode >>>
.fromPath(path)
.via(
ZPipeline.utf8Decode >>>
stringToChars >>>
JsonDecoder[A].decodeJsonTransducer(JsonStreamDelimiter.Newline)
JsonDecoder[A].decodeJsonPipeline(JsonStreamDelimiter.Newline)
)

def readJsonLinesAs[A: JsonDecoder](path: String): ZStream[Blocking, Throwable, A] =
def readJsonLinesAs[A: JsonDecoder](path: String): ZStream[Any, Throwable, A] =
readJsonLinesAs(Paths.get(path))

def readJsonLinesAs[A: JsonDecoder](url: URL): ZStream[Blocking, Throwable, A] = {
def readJsonLinesAs[A: JsonDecoder](url: URL): ZStream[Any, Throwable, A] = {
val managed = ZManaged
.fromAutoCloseable(ZIO.effect(url.openStream()))
.fromAutoCloseable(ZIO.attempt(url.openStream()))
.refineToOrDie[IOException]

ZStream
.fromInputStreamManaged(managed)
.transduce(
ZTransducer.utf8Decode >>>
.via(
ZPipeline.utf8Decode >>>
stringToChars >>>
JsonDecoder[A].decodeJsonTransducer(JsonStreamDelimiter.Newline)
JsonDecoder[A].decodeJsonPipeline(JsonStreamDelimiter.Newline)
)
}

def writeJsonLines[R <: Blocking](file: File, stream: ZStream[R, Throwable, ast.Json]): RIO[R, Unit] =
def writeJsonLines[R](file: File, stream: ZStream[R, Throwable, ast.Json]): RIO[R, Unit] =
writeJsonLinesAs(file, stream)

def writeJsonLines[R <: Blocking](path: Path, stream: ZStream[R, Throwable, ast.Json]): RIO[R, Unit] =
def writeJsonLines[R](path: Path, stream: ZStream[R, Throwable, ast.Json]): RIO[R, Unit] =
writeJsonLinesAs(path, stream)

def writeJsonLines[R <: Blocking](path: String, stream: ZStream[R, Throwable, ast.Json]): RIO[R, Unit] =
def writeJsonLines[R](path: String, stream: ZStream[R, Throwable, ast.Json]): RIO[R, Unit] =
writeJsonLinesAs(path, stream)

def writeJsonLinesAs[R <: Blocking, A: JsonEncoder](file: File, stream: ZStream[R, Throwable, A]): RIO[R, Unit] =
def writeJsonLinesAs[R, A: JsonEncoder](file: File, stream: ZStream[R, Throwable, A]): RIO[R, Unit] =
writeJsonLinesAs(file.toPath, stream)

def writeJsonLinesAs[R <: Blocking, A: JsonEncoder](path: Path, stream: ZStream[R, Throwable, A]): RIO[R, Unit] =
def writeJsonLinesAs[R, A: JsonEncoder](path: Path, stream: ZStream[R, Throwable, A]): RIO[R, Unit] =
stream
.transduce(
JsonEncoder[A].encodeJsonLinesTransducer >>>
.via(
JsonEncoder[A].encodeJsonLinesPipeline >>>
charsToUtf8
)
.run(ZSink.fromFile(path))
.run(ZSink.fromPath(path))
.unit

def writeJsonLinesAs[R <: Blocking, A: JsonEncoder](path: String, stream: ZStream[R, Throwable, A]): RIO[R, Unit] =
def writeJsonLinesAs[R, A: JsonEncoder](path: String, stream: ZStream[R, Throwable, A]): RIO[R, Unit] =
writeJsonLinesAs(Paths.get(path), stream)

private def stringToChars: ZTransducer[Any, Nothing, String, Char] =
ZTransducer
.fromFunction[String, Chunk[Char]](s => Chunk.fromArray(s.toCharArray))
.mapChunks(_.flatten)
private def stringToChars: ZPipeline[Any, Nothing, String, Char] =
ZPipeline.mapChunks[String, Char](_.flatMap(_.toCharArray))

private def charsToUtf8: ZTransducer[Any, Nothing, Char, Byte] =
ZTransducer.fromPush {
case None =>
ZIO.succeed(Chunk.empty)

case Some(xs) =>
ZIO.effectTotal {
Chunk.fromArray((new String(xs.toArray)).getBytes(StandardCharsets.UTF_8))
private def charsToUtf8: ZPipeline[Any, Nothing, Char, Byte] =
ZPipeline.mapChunksZIO[Any, Nothing, Char, Byte] { chunk =>
ZIO.succeed {
Chunk.fromArray {
new String(chunk.toArray).getBytes(StandardCharsets.UTF_8)
}
}
}

private[zio] def fromManagedPush[Env, Err, In, Out](
push: ZManaged[Env, Nothing, Option[Chunk[In]] => ZIO[Env, Err, Chunk[Out]]]
): ZPipeline[Env, Err, In, Out] = {

def pull(
push: Option[Chunk[In]] => ZIO[Env, Err, Chunk[Out]]
): ZChannel[Env, Nothing, Chunk[In], Any, Err, Chunk[Out], Any] =
ZChannel.readWith[Env, Nothing, Chunk[In], Any, Err, Chunk[Out], Any](
in =>
ZChannel
.fromZIO(push(Some(in)))
.flatMap(out => ZChannel.write(out))
.zipRight[Env, Nothing, Chunk[In], Any, Err, Chunk[Out], Any](pull(push)),
err => ZChannel.fail(err),
_ => ZChannel.fromZIO(push(None)).flatMap(out => ZChannel.write(out))
)

ZPipeline.fromChannel {
ZChannel.unwrapManaged[Env, Nothing, Chunk[In], Any, Err, Chunk[Out], Any] {
push.map(pull)
}
}
}
}
Loading

0 comments on commit c08b282

Please sign in to comment.