Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support of immutable.ArraySeq + more inlinings #1279

Merged
merged 3 commits into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package zio.json

import scala.collection.immutable

trait JsonCodecVersionSpecific {
implicit def arraySeq[A: JsonEncoder: JsonDecoder: reflect.ClassTag]: JsonCodec[immutable.ArraySeq[A]] =
JsonCodec(JsonEncoder.arraySeq[A], JsonDecoder.arraySeq[A])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package zio.json

import zio.json.JsonDecoder.JsonError
import zio.json.internal.RetractReader

import scala.collection.immutable

private[json] trait JsonDecoderVersionSpecific {
implicit def arraySeq[A: JsonDecoder: reflect.ClassTag]: JsonDecoder[immutable.ArraySeq[A]] =
new CollectionJsonDecoder[immutable.ArraySeq[A]] {
private[this] val arrayDecoder = JsonDecoder.array[A]

override def unsafeDecodeMissing(trace: List[JsonError]): immutable.ArraySeq[A] = immutable.ArraySeq.empty

def unsafeDecode(trace: List[JsonError], in: RetractReader): immutable.ArraySeq[A] =
immutable.ArraySeq.unsafeWrapArray(arrayDecoder.unsafeDecode(trace, in))
}
}

private[json] trait DecoderLowPriorityVersionSpecific
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package zio.json

import zio.json.ast.Json
import zio.json.internal.Write

import scala.collection.immutable

private[json] trait JsonEncoderVersionSpecific {
implicit def arraySeq[A: JsonEncoder: scala.reflect.ClassTag]: JsonEncoder[immutable.ArraySeq[A]] =
new JsonEncoder[immutable.ArraySeq[A]] {
private[this] val arrayEnc = JsonEncoder.array[A]

override def isEmpty(as: immutable.ArraySeq[A]): Boolean = as.isEmpty

def unsafeEncode(as: immutable.ArraySeq[A], indent: Option[Int], out: Write): Unit =
arrayEnc.unsafeEncode(as.unsafeArray.asInstanceOf[Array[A]], indent, out)

override final def toJsonAST(as: immutable.ArraySeq[A]): Either[String, Json] =
arrayEnc.toJsonAST(as.unsafeArray.asInstanceOf[Array[A]])
}
}

private[json] trait EncoderLowPriorityVersionSpecific
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package zio.json

import scala.collection.immutable

private[json] trait JsonCodecVersionSpecific {
inline def derived[A: deriving.Mirror.Of](using config: JsonCodecConfiguration): JsonCodec[A] = DeriveJsonCodec.gen[A]

implicit def arraySeq[A: JsonEncoder: JsonDecoder: reflect.ClassTag]: JsonCodec[immutable.ArraySeq[A]] =
JsonCodec(JsonEncoder.arraySeq[A], JsonDecoder.arraySeq[A])
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
package zio.json

import zio.json.JsonDecoder.JsonError
import zio.json.internal.RetractReader

import scala.collection.immutable
import scala.compiletime.*
import scala.compiletime.ops.any.IsConst

private[json] trait JsonDecoderVersionSpecific {
inline def derived[A: deriving.Mirror.Of](using config: JsonCodecConfiguration): JsonDecoder[A] =
DeriveJsonDecoder.gen[A]

implicit def arraySeq[A: JsonDecoder: reflect.ClassTag]: JsonDecoder[immutable.ArraySeq[A]] =
new CollectionJsonDecoder[immutable.ArraySeq[A]] {
private[this] val arrayDecoder = JsonDecoder.array[A]

override def unsafeDecodeMissing(trace: List[JsonError]): immutable.ArraySeq[A] = immutable.ArraySeq.empty

def unsafeDecode(trace: List[JsonError], in: RetractReader): immutable.ArraySeq[A] =
immutable.ArraySeq.unsafeWrapArray(arrayDecoder.unsafeDecode(trace, in))
}
}

trait DecoderLowPriorityVersionSpecific {

inline given unionOfStringEnumeration[T](using IsUnionOf[String, T]): JsonDecoder[T] =
val values = UnionDerivation.constValueUnionTuple[String, T]
JsonDecoder.string.mapOrFail {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
package zio.json

import zio.json.ast.Json
import zio.json.internal.Write

import scala.collection.immutable
import scala.compiletime.ops.any.IsConst

private[json] trait JsonEncoderVersionSpecific {
inline def derived[A: deriving.Mirror.Of](using config: JsonCodecConfiguration): JsonEncoder[A] =
DeriveJsonEncoder.gen[A]

implicit def arraySeq[A: JsonEncoder: scala.reflect.ClassTag]: JsonEncoder[immutable.ArraySeq[A]] =
new JsonEncoder[immutable.ArraySeq[A]] {
private[this] val arrayEnc = JsonEncoder.array[A]

override def isEmpty(as: immutable.ArraySeq[A]): Boolean = as.isEmpty

def unsafeEncode(as: immutable.ArraySeq[A], indent: Option[Int], out: Write): Unit =
arrayEnc.unsafeEncode(as.unsafeArray.asInstanceOf[Array[A]], indent, out)

override final def toJsonAST(as: immutable.ArraySeq[A]): Either[String, Json] =
arrayEnc.toJsonAST(as.unsafeArray.asInstanceOf[Array[A]])
}
}

private[json] trait EncoderLowPriorityVersionSpecific {

inline given unionOfStringEnumeration[T](using IsUnionOf[String, T]): JsonEncoder[T] =
JsonEncoder.string.asInstanceOf[JsonEncoder[T]]
}
2 changes: 2 additions & 0 deletions zio-json/shared/src/main/scala/zio/json/JsonCodec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ object JsonCodec extends GeneratedTupleCodecs with CodecLowPriority0 with JsonCo
}

private[json] trait CodecLowPriority0 extends CodecLowPriority1 { this: JsonCodec.type =>
implicit def array[A: JsonEncoder: JsonDecoder: reflect.ClassTag]: JsonCodec[Array[A]] =
JsonCodec(JsonEncoder.array[A], JsonDecoder.array[A])

implicit def chunk[A: JsonEncoder: JsonDecoder]: JsonCodec[Chunk[A]] =
JsonCodec(JsonEncoder.chunk[A], JsonDecoder.chunk[A])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ private[json] trait MappedJsonDecoder[A] extends JsonDecoder[A] {
private[json] trait DecoderLowPriority1 extends DecoderLowPriority2 {
this: JsonDecoder.type =>

implicit def array[A](implicit A: JsonDecoder[A], ct: reflect.ClassTag[A]): JsonDecoder[Array[A]] =
implicit def array[A](implicit A: JsonDecoder[A], classTag: reflect.ClassTag[A]): JsonDecoder[Array[A]] =
new CollectionJsonDecoder[Array[A]] {
override def unsafeDecodeMissing(trace: List[JsonError]): Array[A] = Array.empty

Expand Down
85 changes: 26 additions & 59 deletions zio-json/shared/src/main/scala/zio/json/JsonEncoder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import zio.{ Chunk, NonEmptyChunk }
import java.util.UUID
import scala.annotation._
import scala.collection.{ immutable, mutable }
import scala.reflect.ClassTag

trait JsonEncoder[A] extends JsonEncoderPlatformSpecific[A] {
self =>
Expand All @@ -33,16 +32,14 @@ trait JsonEncoder[A] extends JsonEncoderPlatformSpecific[A] {
* user-defined function.
*/
final def contramap[B](f: B => A): JsonEncoder[B] = new JsonEncoder[B] {

override def unsafeEncode(b: B, indent: Option[Int], out: Write): Unit =
self.unsafeEncode(f(b), indent, out)

override def isNothing(b: B): Boolean = self.isNothing(f(b))

override def isEmpty(b: B): Boolean = self.isEmpty(f(b))

override final def toJsonAST(b: B): Either[String, Json] =
self.toJsonAST(f(b))
override final def toJsonAST(b: B): Either[String, Json] = self.toJsonAST(f(b))
}

/**
Expand Down Expand Up @@ -114,10 +111,9 @@ trait JsonEncoder[A] extends JsonEncoderPlatformSpecific[A] {
}

object JsonEncoder extends GeneratedTupleEncoders with EncoderLowPriority1 with JsonEncoderVersionSpecific {
def apply[A](implicit a: JsonEncoder[A]): JsonEncoder[A] = a
@inline def apply[A](implicit a: JsonEncoder[A]): JsonEncoder[A] = a

implicit val string: JsonEncoder[String] = new JsonEncoder[String] {

override def unsafeEncode(a: String, indent: Option[Int], out: Write): Unit = {
out.write('"')
val len = a.length
Expand All @@ -134,8 +130,7 @@ object JsonEncoder extends GeneratedTupleEncoders with EncoderLowPriority1 with
out.write('"')
}

override final def toJsonAST(a: String): Either[String, Json] =
Right(Json.Str(a))
override final def toJsonAST(a: String): Either[String, Json] = new Right(Json.Str(a))

private[this] def writeEncoded(a: String, out: Write): Unit = {
val len = a.length
Expand All @@ -157,11 +152,9 @@ object JsonEncoder extends GeneratedTupleEncoders with EncoderLowPriority1 with
}
out.write('"')
}

}

implicit val char: JsonEncoder[Char] = new JsonEncoder[Char] {

override def unsafeEncode(a: Char, indent: Option[Int], out: Write): Unit = {
out.write('"')
(a: @switch) match {
Expand All @@ -179,15 +172,13 @@ object JsonEncoder extends GeneratedTupleEncoders with EncoderLowPriority1 with
out.write('"')
}

override final def toJsonAST(a: Char): Either[String, Json] =
Right(Json.Str(a.toString))
override final def toJsonAST(a: Char): Either[String, Json] = new Right(Json.Str(a.toString))
}

private[json] def explicit[A](f: A => String, g: A => Json): JsonEncoder[A] = new JsonEncoder[A] {
def unsafeEncode(a: A, indent: Option[Int], out: Write): Unit = out.write(f(a))

override final def toJsonAST(a: A): Either[String, Json] =
Right(g(a))
override final def toJsonAST(a: A): Either[String, Json] = new Right(g(a))
}

private[json] def stringify[A](f: A => String): JsonEncoder[A] = new JsonEncoder[A] {
Expand All @@ -197,8 +188,7 @@ object JsonEncoder extends GeneratedTupleEncoders with EncoderLowPriority1 with
out.write('"')
}

override final def toJsonAST(a: A): Either[String, Json] =
Right(Json.Str(f(a)))
override final def toJsonAST(a: A): Either[String, Json] = new Right(Json.Str(f(a)))
}

def suspend[A](encoder0: => JsonEncoder[A]): JsonEncoder[A] =
Expand All @@ -224,48 +214,36 @@ object JsonEncoder extends GeneratedTupleEncoders with EncoderLowPriority1 with
explicit(_.toString, n => Json.Num(new java.math.BigDecimal(n)))
implicit val scalaBigInt: JsonEncoder[BigInt] =
explicit(_.toString, n => Json.Num(new java.math.BigDecimal(n.bigInteger)))
implicit val double: JsonEncoder[Double] =
explicit(SafeNumbers.toString, n => Json.Num(n))
implicit val float: JsonEncoder[Float] =
explicit(SafeNumbers.toString, n => Json.Num(n))
implicit val double: JsonEncoder[Double] = explicit(SafeNumbers.toString, n => Json.Num(n))
implicit val float: JsonEncoder[Float] = explicit(SafeNumbers.toString, n => Json.Num(n))
implicit val bigDecimal: JsonEncoder[java.math.BigDecimal] = explicit(_.toString, Json.Num.apply)
implicit val scalaBigDecimal: JsonEncoder[BigDecimal] = explicit(_.toString, n => Json.Num(n.bigDecimal))

implicit def option[A](implicit A: JsonEncoder[A]): JsonEncoder[Option[A]] = new JsonEncoder[Option[A]] {
def unsafeEncode(oa: Option[A], indent: Option[Int], out: Write): Unit =
if (oa eq None) out.write("null")
else A.unsafeEncode(oa.get, indent, out)

def unsafeEncode(oa: Option[A], indent: Option[Int], out: Write): Unit = oa match {
case None => out.write("null")
case Some(a) => A.unsafeEncode(a, indent, out)
}

override def isNothing(oa: Option[A]): Boolean =
oa match {
case None => true
case Some(a) => A.isNothing(a)
}
override def isNothing(oa: Option[A]): Boolean = (oa eq None) || A.isNothing(oa.get)

override final def toJsonAST(oa: Option[A]): Either[String, Json] =
oa match {
case None => Right(Json.Null)
case Some(a) => A.toJsonAST(a)
}
if (oa eq None) new Right(Json.Null)
else A.toJsonAST(oa.get)
}

def bump(indent: Option[Int]): Option[Int] = indent match {
case None => None
case Some(i) => Some(i + 1)
}
def bump(indent: Option[Int]): Option[Int] =
if (indent ne None) new Some(indent.get + 1)
else indent

def pad(indent: Option[Int], out: Write): Unit = indent match {
case None => ()
case Some(n) =>
def pad(indent: Option[Int], out: Write): Unit =
if (indent ne None) {
out.write('\n')
var i = n
var i = indent.get
while (i > 0) {
out.write(" ")
i -= 1
}
}
}

implicit def either[A, B](implicit A: JsonEncoder[A], B: JsonEncoder[B]): JsonEncoder[Either[A, B]] =
new JsonEncoder[Either[A, B]] {
Expand Down Expand Up @@ -320,12 +298,8 @@ object JsonEncoder extends GeneratedTupleEncoders with EncoderLowPriority1 with
private[json] trait EncoderLowPriority1 extends EncoderLowPriority2 {
this: JsonEncoder.type =>

implicit def array[A](implicit
A: JsonEncoder[A],
classTag: ClassTag[A]
): JsonEncoder[Array[A]] =
implicit def array[A](implicit A: JsonEncoder[A], classTag: scala.reflect.ClassTag[A]): JsonEncoder[Array[A]] =
new JsonEncoder[Array[A]] {

override def isEmpty(as: Array[A]): Boolean = as.isEmpty

def unsafeEncode(as: Array[A], indent: Option[Int], out: Write): Unit =
Expand Down Expand Up @@ -389,17 +363,14 @@ private[json] trait EncoderLowPriority1 extends EncoderLowPriority2 {

implicit def vector[A: JsonEncoder]: JsonEncoder[Vector[A]] = iterable[A, Vector]

implicit def set[A: JsonEncoder]: JsonEncoder[Set[A]] =
iterable[A, Set]
implicit def set[A: JsonEncoder]: JsonEncoder[Set[A]] = iterable[A, Set]

implicit def hashSet[A: JsonEncoder]: JsonEncoder[immutable.HashSet[A]] =
iterable[A, immutable.HashSet]
implicit def hashSet[A: JsonEncoder]: JsonEncoder[immutable.HashSet[A]] = iterable[A, immutable.HashSet]

implicit def sortedSet[A: Ordering: JsonEncoder]: JsonEncoder[immutable.SortedSet[A]] =
iterable[A, immutable.SortedSet]

implicit def map[K: JsonFieldEncoder, V: JsonEncoder]: JsonEncoder[Map[K, V]] =
keyValueIterable[K, V, Map]
implicit def map[K: JsonFieldEncoder, V: JsonEncoder]: JsonEncoder[Map[K, V]] = keyValueIterable[K, V, Map]

implicit def hashMap[K: JsonFieldEncoder, V: JsonEncoder]: JsonEncoder[immutable.HashMap[K, V]] =
keyValueIterable[K, V, immutable.HashMap]
Expand All @@ -417,11 +388,8 @@ private[json] trait EncoderLowPriority1 extends EncoderLowPriority2 {
private[json] trait EncoderLowPriority2 extends EncoderLowPriority3 {
this: JsonEncoder.type =>

implicit def iterable[A, T[X] <: Iterable[X]](implicit
A: JsonEncoder[A]
): JsonEncoder[T[A]] =
implicit def iterable[A, T[X] <: Iterable[X]](implicit A: JsonEncoder[A]): JsonEncoder[T[A]] =
new JsonEncoder[T[A]] {

override def isEmpty(as: T[A]): Boolean = as.isEmpty

def unsafeEncode(as: T[A], indent: Option[Int], out: Write): Unit =
Expand Down Expand Up @@ -471,7 +439,6 @@ private[json] trait EncoderLowPriority2 extends EncoderLowPriority3 {
K: JsonFieldEncoder[K],
A: JsonEncoder[A]
): JsonEncoder[T[K, A]] = new JsonEncoder[T[K, A]] {

override def isEmpty(a: T[K, A]): Boolean = a.isEmpty

def unsafeEncode(kvs: T[K, A], indent: Option[Int], out: Write): Unit =
Expand Down
Loading