diff --git a/.sbtopts b/.sbtopts index 164397150..ae2b88708 100644 --- a/.sbtopts +++ b/.sbtopts @@ -1,5 +1,3 @@ --J-Xss512M - -J-Xms5120m -J-Xmx5120m -J-XX:MaxMetaspaceSize=1024m diff --git a/tests/shared/src/test/scala-2/zio/schema/DynamicValueSpec.scala b/tests/shared/src/test/scala-2/zio/schema/DynamicValueSpec.scala index f7f7be453..5f44f1ba7 100644 --- a/tests/shared/src/test/scala-2/zio/schema/DynamicValueSpec.scala +++ b/tests/shared/src/test/scala-2/zio/schema/DynamicValueSpec.scala @@ -10,80 +10,97 @@ object DynamicValueSpec extends ZIOSpecDefault { def spec: Spec[Environment, Any] = suite("DynamicValueSpec")( - suite("Primitives")(primitiveTests: _*), - test("round-trips Records") { - check(SchemaGen.anyRecordOfRecordsAndValue) { - case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + suite("round-trip")( + suite("Primitives")(primitiveTests: _*), + test("round-trips Records") { + check(SchemaGen.anyRecordOfRecordsAndValue) { + case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips Enumerations") { + check(SchemaGen.anyEnumerationAndValue) { + case (schema, a) => + assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips Eithers") { + check(SchemaGen.anyEitherAndValue) { + case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips Tuples") { + check(SchemaGen.anyTupleAndValue) { + case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips Optionals") { + check(SchemaGen.anyOptionalAndValue) { + case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips Transform") { + check(SchemaGen.anyTransformAndValue) { + case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips CaseClass") { + check(SchemaGen.anyCaseClassAndValue) { + case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips Enum") { + check(SchemaGen.anyEnumAndValue) { + case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips any un-nested schema") { + check(SchemaGen.anyLeafAndValue) { + case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips any nested schema") { + check(SchemaGen.anyTree(1).flatMap(s => DynamicValueGen.anyDynamicValueOfSchema(s).map(s -> _))) { + case (schema, dynamic) => + assert(schema.fromDynamic(dynamic))(isRight) + } + }, + test("round-trips recursive data types") { + check(SchemaGen.anyRecursiveTypeAndValue) { + case (schema, a) => + assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips sequence") { + check(SchemaGen.anySequenceAndValue) { + case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips set") { + check(SchemaGen.anySetAndValue) { + case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } + }, + test("round-trips map") { + check(SchemaGen.anyMapAndValue) { + case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) + } } - }, - test("round-trips Enumerations") { - check(SchemaGen.anyEnumerationAndValue) { - case (schema, a) => - assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - }, - test("round-trips Eithers") { - check(SchemaGen.anyEitherAndValue) { - case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - }, - test("round-trips Tuples") { - check(SchemaGen.anyTupleAndValue) { - case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - }, - test("round-trips Optionals") { - check(SchemaGen.anyOptionalAndValue) { - case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - }, - test("round-trips Transform") { - check(SchemaGen.anyTransformAndValue) { - case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - }, - test("round-trips CaseClass") { - check(SchemaGen.anyCaseClassAndValue) { - case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - }, - test("round-trips Enum") { - check(SchemaGen.anyEnumAndValue) { - case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - }, - test("round-trips any un-nested schema") { - check(SchemaGen.anyLeafAndValue) { - case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - }, - test("round-trips any nested schema") { - check(SchemaGen.anyTree(1).flatMap(s => DynamicValueGen.anyDynamicValueOfSchema(s).map(s -> _))) { - case (schema, dynamic) => - assert(schema.fromDynamic(dynamic))(isRight) - } - }, - test("round-trips recursive data types") { - check(SchemaGen.anyRecursiveTypeAndValue) { - case (schema, a) => - assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - }, - test("round-trips sequence") { - check(SchemaGen.anySequenceAndValue) { - case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - }, - test("round-trips set") { - check(SchemaGen.anySetAndValue) { - case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - }, - test("round-trips map") { - check(SchemaGen.anyMapAndValue) { - case (schema, a) => assert(schema.fromDynamic(schema.toDynamic(a)))(isRight(equalTo(a))) - } - } + ), + suite("stack safety")( + test("fromSchemaAndValue is stack safe") { + check(Json.genDeep) { json => + val _ = DynamicValue.fromSchemaAndValue(Json.schema, json) + assertCompletes + } + } @@ TestAspect.sized(100), + test("toTyped is stack safe") { + check(Json.genDeep) { json => + val dyn = DynamicValue.fromSchemaAndValue(Json.schema, json) + val json2 = dyn.toTypedValue(Json.schema) + assertTrue(json2 == Right(json)) + } + } @@ TestAspect.sized(250) + ) ) val primitiveTests: List[Spec[Sized with TestConfig, Nothing]] = schemasAndGens.map { diff --git a/tests/shared/src/test/scala-2/zio/schema/SchemaGen.scala b/tests/shared/src/test/scala-2/zio/schema/SchemaGen.scala index e280e06fd..7f26cb158 100644 --- a/tests/shared/src/test/scala-2/zio/schema/SchemaGen.scala +++ b/tests/shared/src/test/scala-2/zio/schema/SchemaGen.scala @@ -632,6 +632,18 @@ object SchemaGen { keys <- Gen.setOfN(3)(Gen.string) values <- Gen.setOfN(3)(leafGen) } yield JObject(keys.zip(values).toList) + + val genDeep: Gen[Sized, Json] = + Gen.sized { max => + Gen.bounded(1, max) { depth => + leafGen.map { leaf => + (1 to depth).foldLeft(leaf) { + case (gen, n) => + JObject(List(n.toString -> gen)) + } + } + } + } } lazy val anyRecursiveType: Gen[Sized, Schema[_]] = @@ -643,6 +655,12 @@ object SchemaGen { value <- Json.gen } yield (schema, value) + lazy val anyDeepRecursiveTypeAndValue: Gen[Sized, SchemaAndValue[_]] = + for { + schema <- Gen.const(Schema[Json]) + value <- Json.genDeep + } yield (schema, value) + lazy val anyDynamic: Gen[Any, Schema[DynamicValue]] = Gen.const(Schema.dynamicValue) case class SchemaTest[A](name: String, schema: StandardType[A], gen: Gen[Sized, A]) diff --git a/zio-schema-json/shared/src/test/scala-2/zio/schema/codec/JsonCodecSpec.scala b/zio-schema-json/shared/src/test/scala-2/zio/schema/codec/JsonCodecSpec.scala index 59a3258a5..49116b440 100644 --- a/zio-schema-json/shared/src/test/scala-2/zio/schema/codec/JsonCodecSpec.scala +++ b/zio-schema-json/shared/src/test/scala-2/zio/schema/codec/JsonCodecSpec.scala @@ -570,6 +570,12 @@ object JsonCodecSpec extends ZIOSpecDefault { assertEncodesThenDecodes(schema, value) } }, + test("deep recursive data type") { + check(SchemaGen.anyDeepRecursiveTypeAndValue) { + case (schema, value) => + assertEncodesThenDecodes(schema, value) + } + } @@ TestAspect.sized(200), suite("dynamic")( test("dynamic int") { check( diff --git a/zio-schema-protobuf/shared/src/main/scala/zio/schema/codec/ProtobufCodec.scala b/zio-schema-protobuf/shared/src/main/scala/zio/schema/codec/ProtobufCodec.scala index ebe1986da..4bc43df49 100644 --- a/zio-schema-protobuf/shared/src/main/scala/zio/schema/codec/ProtobufCodec.scala +++ b/zio-schema-protobuf/shared/src/main/scala/zio/schema/codec/ProtobufCodec.scala @@ -1,21 +1,20 @@ package zio.schema.codec -import java.math.{ BigInteger, MathContext } import java.nio.charset.StandardCharsets import java.nio.{ ByteBuffer, ByteOrder } import java.time._ import java.util.UUID -import scala.annotation.tailrec import scala.collection.immutable.ListMap import scala.util.control.NonFatal +import zio.schema.MutableSchemaBasedValueBuilder.CreateValueFromSchemaError import zio.schema._ -import zio.schema.codec.BinaryCodec._ -import zio.schema.codec.DecodeError.{ ExtraFields, MalformedField, MissingField, ReadError, RecordMissingField } +import zio.schema.codec.BinaryCodec.{ BinaryDecoder, BinaryEncoder, BinaryStreamDecoder, BinaryStreamEncoder } +import zio.schema.codec.DecodeError.{ ExtraFields, MalformedField, MissingField, ReadError } import zio.schema.codec.ProtobufCodec.Protobuf.WireType.LengthDelimited import zio.stream.ZPipeline -import zio.{ Cause, Chunk, ZIO } +import zio.{ Cause, Chunk, Unsafe, ZIO } object ProtobufCodec extends BinaryCodec { @@ -23,7 +22,7 @@ object ProtobufCodec extends BinaryCodec { new BinaryEncoder[A] { override def encode(value: A): Chunk[Byte] = - ProtobufEncoder.encode(None, schema, value) + Encoder.process(schema, value) override def streamEncoder: BinaryStreamEncoder[A] = ZPipeline.mapChunks( @@ -36,15 +35,10 @@ object ProtobufCodec extends BinaryCodec { new BinaryDecoder[A] { override def decode(chunk: Chunk[Byte]): Either[DecodeError, A] = - ProtobufDecoder.decode(schema, chunk) + new Decoder(chunk).decode(schema) override def streamDecoder: BinaryStreamDecoder[A] = - ZPipeline.mapChunksZIO( - chunk => - ZIO.fromEither( - decode(chunk).map(Chunk(_)) - ) - ) + ZPipeline.mapChunksZIO(chunk => ZIO.fromEither(new Decoder(chunk).decode(schema).map(Chunk(_)))) } @@ -61,87 +55,6 @@ object ProtobufCodec extends BinaryCodec { case object Bit32 extends WireType } - private[codec] val bigDecimalStructure: Seq[Schema.Field[java.math.BigDecimal, _]] = - Seq( - Schema.Field( - "unscaled", - Schema.Primitive(StandardType.BigIntegerType), - get = _.unscaledValue, - set = (a, b: BigInteger) => new java.math.BigDecimal(b, a.scale) - ), - Schema - .Field( - "precision", - Schema.Primitive(StandardType.IntType), - get = _.precision(), - set = (a, b: Int) => new java.math.BigDecimal(a.unscaledValue, new MathContext(b)) - ), - Schema - .Field("scale", Schema.Primitive(StandardType.IntType), get = _.scale(), set = (a, b: Int) => a.setScale(b)) - ) - - private[codec] val monthDayStructure: Seq[Schema.Field[MonthDay, Int]] = - Seq( - Schema.Field( - "month", - Schema.Primitive(StandardType.IntType), - get = _.getMonthValue, - set = (a, b: Int) => a.`with`(Month.of(b)) - ), - Schema - .Field( - "day", - Schema.Primitive(StandardType.IntType), - get = _.getDayOfMonth, - set = (a, b: Int) => a.withDayOfMonth(b) - ) - ) - - private[codec] val periodStructure: Seq[Schema.Field[Period, Int]] = Seq( - Schema - .Field("years", Schema.Primitive(StandardType.IntType), get = _.getYears, set = (a, b: Int) => a.withYears(b)), - Schema.Field( - "months", - Schema.Primitive(StandardType.IntType), - get = _.getMonths, - set = (a, b: Int) => a.withMonths(b) - ), - Schema.Field("days", Schema.Primitive(StandardType.IntType), get = _.getDays, set = (a, b: Int) => a.withDays(b)) - ) - - private[codec] val yearMonthStructure: Seq[Schema.Field[YearMonth, Int]] = - Seq( - Schema.Field( - "year", - Schema.Primitive(StandardType.IntType), - get = _.getYear, - set = (a, b: Int) => a.`with`(Year.of(b)) - ), - Schema.Field( - "month", - Schema.Primitive(StandardType.IntType), - get = _.getMonthValue, - set = (a, b: Int) => a.`with`(Month.of(b)) - ) - ) - - private[codec] val durationStructure: Seq[Schema.Field[Duration, _]] = - Seq( - Schema.Field( - "seconds", - Schema.Primitive(StandardType.LongType), - get = _.getSeconds, - set = (a, b: Long) => a.plusSeconds(b) - ), - Schema - .Field( - "nanos", - Schema.Primitive(StandardType.IntType), - get = _.getNano, - set = (a, b: Int) => a.plusNanos(b.toLong) - ) - ) - /** * Used when encoding sequence of values to decide whether each value need its own key or values can be packed together without keys (for example numbers). */ @@ -192,172 +105,199 @@ object ProtobufCodec extends BinaryCodec { } } - object ProtobufEncoder { + final private[codec] case class EncoderContext(fieldNumber: Option[Int]) - import ProductEncoder._ + object Encoder extends MutableSchemaBasedValueProcessor[Chunk[Byte], EncoderContext] { import Protobuf._ - //scalafmt: { maxColumn = 400, optIn.configStyleArguments = false } - def encode[A](fieldNumber: Option[Int], schema: Schema[A], value: A): Chunk[Byte] = - (schema, value) match { - case (Schema.GenericRecord(_, structure, _), v: Map[String, _]) => - encodeRecord(fieldNumber, structure.toChunk, v) - case (Schema.Sequence(element, _, g, _, _), v) => encodeSequence(fieldNumber, element, g(v)) - case (Schema.Map(ks, vs, _), map) => encodeSequence(fieldNumber, ks <*> vs, Chunk.fromIterable(map)) - case (Schema.Set(s, _), set) => encodeSequence(fieldNumber, s, Chunk.fromIterable(set)) - case (Schema.Transform(codec, _, g, _, _), _) => g(value).map(encode(fieldNumber, codec, _)).getOrElse(Chunk.empty) - case (Schema.Primitive(standardType, _), v) => encodePrimitive(fieldNumber, standardType, v) - case (Schema.Tuple2(left, right, _), v @ (_, _)) => encodeTuple(fieldNumber, left, right, v) - case (Schema.Optional(codec: Schema[a], _), v: Option[_]) => encodeOptional(fieldNumber, codec, v.asInstanceOf[Option[a]]) - case (Schema.Either(left: Schema[a], right: Schema[b], _), v: scala.util.Either[_, _]) => encodeEither(fieldNumber, left, right, v.asInstanceOf[scala.util.Either[a, b]]) - case (lzy @ Schema.Lazy(_), v) => encode(fieldNumber, lzy.schema, v) - case (_: Schema.CaseClass0[_], v) => - encodeCaseClass(v)(fieldNumber) - case (cc: Schema.CaseClass1[_, _], v) => - encodeCaseClass(v, cc.field)(fieldNumber) - case (cc: Schema.CaseClass2[_, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2)(fieldNumber) - case (cc: Schema.CaseClass3[_, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3)(fieldNumber) - case (cc: Schema.CaseClass4[_, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4)(fieldNumber) - case (cc: Schema.CaseClass5[_, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5)(fieldNumber) - case (cc: Schema.CaseClass6[_, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6)(fieldNumber) - case (cc: Schema.CaseClass7[_, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7)(fieldNumber) - case (cc: Schema.CaseClass8[_, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8)(fieldNumber) - case (cc: Schema.CaseClass9[_, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9)(fieldNumber) - case (cc: Schema.CaseClass10[_, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10)(fieldNumber) - case (cc: Schema.CaseClass11[_, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11)(fieldNumber) - case (cc: Schema.CaseClass12[_, _, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11, cc.field12)(fieldNumber) - case (cc: Schema.CaseClass13[_, _, _, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11, cc.field12, cc.field13)(fieldNumber) - case (cc: Schema.CaseClass14[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11, cc.field12, cc.field13, cc.field14)(fieldNumber) - case (cc: Schema.CaseClass15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11, cc.field12, cc.field13, cc.field14, cc.field15)(fieldNumber) - case (cc: Schema.CaseClass16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11, cc.field12, cc.field13, cc.field14, cc.field15, cc.field16)(fieldNumber) - case (cc: Schema.CaseClass17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11, cc.field12, cc.field13, cc.field14, cc.field15, cc.field16, cc.field17)(fieldNumber) - case (cc: Schema.CaseClass18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11, cc.field12, cc.field13, cc.field14, cc.field15, cc.field16, cc.field17, cc.field18)(fieldNumber) - case (cc: Schema.CaseClass19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11, cc.field12, cc.field13, cc.field14, cc.field15, cc.field16, cc.field17, cc.field18, cc.field19)(fieldNumber) - case (cc: Schema.CaseClass20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11, cc.field12, cc.field13, cc.field14, cc.field15, cc.field16, cc.field17, cc.field18, cc.field19, cc.field20)(fieldNumber) - case (cc: Schema.CaseClass21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11, cc.field12, cc.field13, cc.field14, cc.field15, cc.field16, cc.field17, cc.field18, cc.field19, cc.field20, cc.field21)(fieldNumber) - case (cc: Schema.CaseClass22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _], v) => - encodeCaseClass(v, cc.field1, cc.field2, cc.field3, cc.field4, cc.field5, cc.field6, cc.field7, cc.field8, cc.field9, cc.field10, cc.field11, cc.field12, cc.field13, cc.field14, cc.field15, cc.field16, cc.field17, cc.field18, cc.field19, cc.field20, cc.field21, cc.field22)(fieldNumber) - case (Schema.Enum1(_, c, _), v) => encodeEnum(fieldNumber, v, c) - case (Schema.Enum2(_, c1, c2, _), v) => encodeEnum(fieldNumber, v, c1, c2) - case (Schema.Enum3(_, c1, c2, c3, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3) - case (Schema.Enum4(_, c1, c2, c3, c4, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4) - case (Schema.Enum5(_, c1, c2, c3, c4, c5, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5) - case (Schema.Enum6(_, c1, c2, c3, c4, c5, c6, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6) - case (Schema.Enum7(_, c1, c2, c3, c4, c5, c6, c7, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7) - case (Schema.Enum8(_, c1, c2, c3, c4, c5, c6, c7, c8, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8) - case (Schema.Enum9(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9) - case (Schema.Enum10(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) - case (Schema.Enum11(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) - case (Schema.Enum12(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) - case (Schema.Enum13(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) - case (Schema.Enum14(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14) - case (Schema.Enum15(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15) - case (Schema.Enum16(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16) - case (Schema.Enum17(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17) - case (Schema.Enum18(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18) - case (Schema.Enum19(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19) - case (Schema.Enum20(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20) - case (Schema.Enum21(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21) - case (Schema.Enum22(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22) - case (Schema.EnumN(_, cs, _), v) => encodeEnum(fieldNumber, v, cs.toSeq: _*) - case (Schema.Dynamic(_), v) => dynamicEncode(fieldNumber, DynamicValueSchema.schema, v) - case (_, _) => Chunk.empty - } - //scalafmt: { maxColumn = 120, optIn.configStyleArguments = true } - - private def dynamicEncode( - fieldNumber: Option[Int], - schema: Schema[DynamicValue], - value: DynamicValue - ): Chunk[Byte] = - encode(fieldNumber, schema, value) + override protected def processPrimitive(context: EncoderContext, value: Any, typ: StandardType[Any]): Chunk[Byte] = + encodePrimitive(context.fieldNumber, typ, value) - private def encodeEnum[Z](fieldNumber: Option[Int], value: Z, cases: Schema.Case[Z, _]*): Chunk[Byte] = { - val fieldIndex = cases.indexWhere(c => c.deconstructOption(value).isDefined) - val encoded = Chunk.fromIterable( - if (fieldIndex == -1) { - Chunk.empty - } else { - val subtypeCase = cases(fieldIndex) - encode( - Some(fieldIndex + 1), - subtypeCase.schema.asInstanceOf[Schema[Any]], - subtypeCase.deconstruct(value) - ) - } - ) - encodeKey(WireType.LengthDelimited(encoded.size), fieldNumber) ++ encoded + override protected def processRecord( + context: EncoderContext, + schema: Schema.Record[_], + value: ListMap[String, Chunk[Byte]] + ): Chunk[Byte] = { + val encodedRecord = Chunk.fromIterable(value.values).flatten + encodeKey(WireType.LengthDelimited(encodedRecord.size), context.fieldNumber) ++ encodedRecord } - private def encodeRecord( - fieldNumber: Option[Int], - structure: Seq[Schema.Field[_, _]], - data: ListMap[String, _] + override protected def processEnum( + context: EncoderContext, + schema: Schema.Enum[_], + tuple: (String, Chunk[Byte]) ): Chunk[Byte] = { - val encodedRecord = Chunk - .fromIterable(structure.zipWithIndex.map { - case (Schema.Field(label, schema, _, _, _, _), fieldNumber) => - data - .get(label) - .map(value => encode(Some(fieldNumber + 1), schema.asInstanceOf[Schema[Any]], value)) - .getOrElse(Chunk.empty) - }) - .flatten - - encodeKey(WireType.LengthDelimited(encodedRecord.size), fieldNumber) ++ encodedRecord + val encoded = tuple._2 + encodeKey(WireType.LengthDelimited(encoded.size), context.fieldNumber) ++ encoded } - /** - * Lists of lists are not really a type that is "native" to protobuf so - * we have to encode lists as enums in order to distinguish between List.empty, - * List(List.empty) and so forth. - * - * This adds a few extra bytes to the encoding but it does not seem like there - * is any way around it. - */ - private def encodeSequence[A]( - fieldNumber: Option[Int], - element: Schema[A], - sequence: Chunk[A] + override protected def processSequence( + context: EncoderContext, + schema: Schema.Sequence[_, _, _], + value: Chunk[Chunk[Byte]] ): Chunk[Byte] = - if (sequence.isEmpty) { + if (value.isEmpty) { val data = encodeKey(WireType.LengthDelimited(0), Some(1)) - encodeKey(WireType.LengthDelimited(data.size), fieldNumber) ++ encodeKey(WireType.LengthDelimited(0), Some(1)) + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ encodeKey( + WireType.LengthDelimited(0), + Some(1) + ) } else { - val data = if (canBePacked(element)) { - val chunk = sequence.flatMap(value => encode(None, element, value)) - encodeKey(WireType.LengthDelimited(chunk.size), Some(2)) ++ chunk - } else { - val chunk = sequence.zipWithIndexFrom(1).flatMap { - case (a, i) => encode(Some(i), element, a) - } - encodeKey(WireType.LengthDelimited(chunk.size), Some(2)) ++ chunk - } + val chunk = value.flatten + val data = encodeKey( + WireType.LengthDelimited(chunk.size), + Some(2) + ) ++ chunk - encodeKey(WireType.LengthDelimited(data.size), fieldNumber) ++ data + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ data } - @scala.annotation.tailrec + override protected def processDictionary( + context: EncoderContext, + schema: Schema.Map[_, _], + value: Chunk[(Chunk[Byte], Chunk[Byte])] + ): Chunk[Byte] = + if (value.isEmpty) { + val data = encodeKey(WireType.LengthDelimited(0), Some(1)) + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ encodeKey( + WireType.LengthDelimited(0), + Some(1) + ) + } else { + val chunk = value.map { + case (left, right) => + val leftDecoder = new Decoder(left) + val rightDecoder = new Decoder(right) + + ( + leftDecoder.keyDecoder(DecoderContext(None, packed = false, dictionaryElementContext = None)), + rightDecoder.keyDecoder(DecoderContext(None, packed = false, dictionaryElementContext = None)) + ) match { + case ((leftWireType, seqIndex), (rightWireType, _)) => + val data = + encodeKey(leftWireType, Some(1)) ++ + leftDecoder.remainder ++ + encodeKey(rightWireType, Some(2)) ++ + rightDecoder.remainder + encodeKey(WireType.LengthDelimited(data.size), Some(seqIndex)) ++ data + case other => + throw new IllegalStateException(s"Invalid state in processDictionary: $other") + } + }.flatten + val data = encodeKey( + WireType.LengthDelimited(chunk.size), + Some(2) + ) ++ chunk + + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ data + } + + override protected def processSet( + context: EncoderContext, + schema: Schema.Set[_], + value: Set[Chunk[Byte]] + ): Chunk[Byte] = + if (value.isEmpty) { + val data = encodeKey(WireType.LengthDelimited(0), Some(1)) + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ encodeKey( + WireType.LengthDelimited(0), + Some(1) + ) + } else { + val chunk = Chunk.fromIterable(value).flatten + val data = encodeKey( + WireType.LengthDelimited(chunk.size), + Some(2) + ) ++ chunk + + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ data + } + + override protected def processEither( + context: EncoderContext, + schema: Schema.Either[_, _], + value: Either[Chunk[Byte], Chunk[Byte]] + ): Chunk[Byte] = { + val encodedEither = value.merge + encodeKey(WireType.LengthDelimited(encodedEither.size), context.fieldNumber) ++ encodedEither + } + + override protected def processOption( + context: EncoderContext, + schema: Schema.Optional[_], + value: Option[Chunk[Byte]] + ): Chunk[Byte] = { + val data = value match { + case Some(bytes) => bytes + case None => encodeKey(WireType.LengthDelimited(0), Some(1)) + } + + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ data + } + + override protected def processTuple( + context: EncoderContext, + schema: Schema.Tuple2[_, _], + left: Chunk[Byte], + right: Chunk[Byte] + ): Chunk[Byte] = { + val data = left ++ right + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ data + } + + override protected def processDynamic(context: EncoderContext, value: DynamicValue): Option[Chunk[Byte]] = + None + + override protected def fail(context: EncoderContext, message: String): Chunk[Byte] = + throw new RuntimeException(message) + + override protected val initialContext: EncoderContext = EncoderContext(None) + + override protected def contextForRecordField( + context: EncoderContext, + index: Int, + field: Schema.Field[_, _] + ): EncoderContext = + context.copy(fieldNumber = Some(index + 1)) + + override protected def contextForTuple(context: EncoderContext, index: Int): EncoderContext = + context.copy(fieldNumber = Some(index)) + + override protected def contextForEnumConstructor( + context: EncoderContext, + index: Int, + c: Schema.Case[_, _] + ): EncoderContext = + context.copy(fieldNumber = Some(index + 1)) + + override protected def contextForEither(context: EncoderContext, e: Either[Unit, Unit]): EncoderContext = + e match { + case Left(_) => context.copy(fieldNumber = Some(1)) + case Right(_) => context.copy(fieldNumber = Some(2)) + } + + override protected def contextForOption(context: EncoderContext, o: Option[Unit]): EncoderContext = + o match { + case None => context.copy(fieldNumber = Some(1)) + case Some(_) => context.copy(fieldNumber = Some(2)) + } + + override protected def contextForSequence( + context: EncoderContext, + s: Schema.Sequence[_, _, _], + index: Int + ): EncoderContext = + if (canBePacked(s.elementSchema)) context.copy(fieldNumber = None) + else context.copy(fieldNumber = Some(index + 1)) + + override protected def contextForMap(context: EncoderContext, s: Schema.Map[_, _], index: Int): EncoderContext = + if (canBePacked(s.keySchema <*> s.valueSchema)) context.copy(fieldNumber = None) + else context.copy(fieldNumber = Some(index + 1)) + + override protected def contextForSet(context: EncoderContext, s: Schema.Set[_], index: Int): EncoderContext = + if (canBePacked(s.elementSchema)) context.copy(fieldNumber = None) + else context.copy(fieldNumber = Some(index + 1)) + private def encodePrimitive[A]( fieldNumber: Option[Int], standardType: StandardType[A], @@ -383,11 +323,14 @@ object ProtobufCodec extends BinaryCodec { val unscaled = v.unscaledValue() val precision = v.precision() val scale = v.scale() - encodeRecord( - fieldNumber, - bigDecimalStructure, - ListMap("unscaled" -> unscaled, "precision" -> precision, "scale" -> scale) - ) + + val encodedRecord = + encodePrimitive(Some(1), StandardType.BigIntegerType, unscaled) ++ + encodePrimitive(Some(2), StandardType.IntType, precision) ++ + encodePrimitive(Some(3), StandardType.IntType, scale) + + encodeKey(WireType.LengthDelimited(encodedRecord.size), fieldNumber) ++ encodedRecord + case (StandardType.BigIntegerType, v: java.math.BigInteger) => val encoded = Chunk.fromArray(v.toByteArray) encodeKey(WireType.LengthDelimited(encoded.size), fieldNumber) ++ encoded @@ -413,23 +356,36 @@ object ProtobufCodec extends BinaryCodec { case (StandardType.MonthType, v: Month) => encodePrimitive(fieldNumber, StandardType.IntType, v.getValue) case (StandardType.MonthDayType, v: MonthDay) => - encodeRecord(fieldNumber, monthDayStructure, ListMap("month" -> v.getMonthValue, "day" -> v.getDayOfMonth)) + val encodedRecord = + encodePrimitive(Some(1), StandardType.IntType, v.getMonthValue) ++ + encodePrimitive(Some(2), StandardType.IntType, v.getDayOfMonth) + + encodeKey(WireType.LengthDelimited(encodedRecord.size), fieldNumber) ++ encodedRecord case (StandardType.PeriodType, v: Period) => - encodeRecord( - fieldNumber, - periodStructure, - ListMap("years" -> v.getYears, "months" -> v.getMonths, "days" -> v.getDays) - ) + val encodedRecord = + encodePrimitive(Some(1), StandardType.IntType, v.getYears) ++ + encodePrimitive(Some(2), StandardType.IntType, v.getMonths) ++ + encodePrimitive(Some(3), StandardType.IntType, v.getDays) + + encodeKey(WireType.LengthDelimited(encodedRecord.size), fieldNumber) ++ encodedRecord case (StandardType.YearType, v: Year) => encodePrimitive(fieldNumber, StandardType.IntType, v.getValue) case (StandardType.YearMonthType, v: YearMonth) => - encodeRecord(fieldNumber, yearMonthStructure, ListMap("year" -> v.getYear, "month" -> v.getMonthValue)) + val encodedRecord = + encodePrimitive(Some(1), StandardType.IntType, v.getYear) ++ + encodePrimitive(Some(2), StandardType.IntType, v.getMonthValue) + + encodeKey(WireType.LengthDelimited(encodedRecord.size), fieldNumber) ++ encodedRecord case (StandardType.ZoneIdType, v: ZoneId) => encodePrimitive(fieldNumber, StandardType.StringType, v.getId) case (StandardType.ZoneOffsetType, v: ZoneOffset) => encodePrimitive(fieldNumber, StandardType.IntType, v.getTotalSeconds) case (StandardType.DurationType, v: Duration) => - encodeRecord(fieldNumber, durationStructure, ListMap("seconds" -> v.getSeconds, "nanos" -> v.getNano)) + val encodedRecord = + encodePrimitive(Some(1), StandardType.LongType, v.getSeconds) ++ + encodePrimitive(Some(2), StandardType.IntType, v.getNano) + + encodeKey(WireType.LengthDelimited(encodedRecord.size), fieldNumber) ++ encodedRecord case (StandardType.InstantType(formatter), v: Instant) => encodePrimitive(fieldNumber, StandardType.StringType, formatter.format(v)) case (StandardType.LocalDateType(formatter), v: LocalDate) => @@ -448,40 +404,6 @@ object ProtobufCodec extends BinaryCodec { throw new NotImplementedError(s"No encoder for $standardType") } - private def encodeTuple[A, B]( - fieldNumber: Option[Int], - left: Schema[A], - right: Schema[B], - tuple: (A, B) - ): Chunk[Byte] = { - val data = encode(Some(1), left, tuple._1) ++ encode(Some(2), right, tuple._2) - - encodeKey(WireType.LengthDelimited(data.size), fieldNumber) ++ data - } - - private def encodeEither[A, B]( - fieldNumber: Option[Int], - left: Schema[A], - right: Schema[B], - either: scala.util.Either[A, B] - ): Chunk[Byte] = { - val encodedEither = either match { - case Left(value) => encode(Some(1), left, value) - case Right(value) => encode(Some(2), right, value) - } - - encodeKey(WireType.LengthDelimited(encodedEither.size), fieldNumber) ++ encodedEither - } - - private def encodeOptional[A](fieldNumber: Option[Int], schema: Schema[A], value: Option[A]): Chunk[Byte] = { - val data = value match { - case Some(a) => encode(Some(2), schema, a) - case None => encodeKey(WireType.LengthDelimited(0), Some(1)) - } - - encodeKey(WireType.LengthDelimited(data.size), fieldNumber) ++ data - } - private def encodeVarInt(value: Int): Chunk[Byte] = encodeVarInt(value.toLong) @@ -517,451 +439,500 @@ object ProtobufCodec extends BinaryCodec { }.getOrElse(Chunk.empty) } - final case class ProtobufDecoder[+A](run: Chunk[Byte] => scala.util.Either[DecodeError, (Chunk[Byte], A)]) { - self => + final class DecoderState(chunk: Chunk[Byte], private var position: Int) { + def length(context: DecoderContext): Int = context.limit.getOrElse(chunk.length) - position - def map[B](f: A => B): ProtobufDecoder[B] = - ProtobufDecoder { bytes => - self.run(bytes).map { - case (remainder, a) => - (remainder, f(a)) - } - } + def read(count: Int): Chunk[Byte] = { + val oldPosition = position + position += count + chunk.slice(oldPosition, position) + } - def flatMap[B](f: A => ProtobufDecoder[B]): ProtobufDecoder[B] = - ProtobufDecoder { bytes => - self.run(bytes).flatMap { - case (remainder, a) => - f(a).run(remainder) - } - } + def all(context: DecoderContext): Chunk[Byte] = read(length(context)) - def loop: ProtobufDecoder[Chunk[A]] = - self.flatMap( - a0 => - ProtobufDecoder(bytes => { - if (bytes.isEmpty) { - Right((bytes, Chunk(a0))) - } else { - loop.run(bytes) match { - case Left(value) => Left(value) - case Right((remainder, a)) => Right((remainder, Chunk(a0) ++ a)) - } - } - }) - ) - - def take(n: Int): ProtobufDecoder[A] = - ProtobufDecoder(bytes => { - val (before, after) = bytes.splitAt(n) - self.run(before) match { - case Left(value) => Left(value) - case Right((_, a)) => Right((after, a)) - } - }) + def peek(context: DecoderContext): Chunk[Byte] = + chunk.slice(position, position + length(context)) + + def move(count: Int): Unit = + position += count + + def currentPosition: Int = position + } + + final case class DecoderContext( + limit: Option[Int], + packed: Boolean, + dictionaryElementContext: Option[DecoderContext] + ) { + + def limitedTo(state: DecoderState, w: Int): DecoderContext = + copy(limit = Some(state.currentPosition + w)) } - object ProtobufDecoder { + class Decoder(chunk: Chunk[Byte]) extends MutableSchemaBasedValueBuilder[Any, DecoderContext] { - import ProductDecoder._ import Protobuf._ - def fail(failure: DecodeError): ProtobufDecoder[Nothing] = ProtobufDecoder(_ => Left(failure)) - - def succeedNow[A](a: A): ProtobufDecoder[A] = ProtobufDecoder(bytes => Right((bytes, a))) - - def succeed[A](a: => A): ProtobufDecoder[A] = ProtobufDecoder(bytes => Right((bytes, a))) - - def binaryDecoder: ProtobufDecoder[Chunk[Byte]] = ProtobufDecoder(bytes => Right((Chunk.empty, bytes))) - - def collectAll[A](chunk: Chunk[ProtobufDecoder[A]]): ProtobufDecoder[Chunk[A]] = ??? - - def failWhen(cond: Boolean, decodeError: DecodeError): ProtobufDecoder[Unit] = - if (cond) ProtobufDecoder.fail(decodeError) else ProtobufDecoder.succeed(()) - - private[codec] val stringDecoder: ProtobufDecoder[String] = - ProtobufDecoder(bytes => Right((Chunk.empty, new String(bytes.toArray, StandardCharsets.UTF_8)))) - - def decode[A](schema: Schema[A], chunk: Chunk[Byte]): scala.util.Either[DecodeError, A] = - decoder(schema) - .run(chunk) - .map(_._2) - - private[codec] def decoder[A](schema: Schema[A]): ProtobufDecoder[A] = - //scalafmt: { maxColumn = 400, optIn.configStyleArguments = false } - schema match { - case Schema.GenericRecord(_, structure, _) => recordDecoder(structure.toChunk) - case Schema.Sequence(elementSchema, fromChunk, _, _, _) => - if (canBePacked(elementSchema)) packedSequenceDecoder(elementSchema).map(fromChunk) - else nonPackedSequenceDecoder(elementSchema).map(fromChunk) - case Schema.Map(ks: Schema[k], vs: Schema[v], _) => decoder(Schema.Sequence(ks <*> vs, (c: Chunk[(k, v)]) => Map(c: _*), (m: Map[k, v]) => Chunk.fromIterable(m), identity = "Map")) - case Schema.Set(schema: Schema[s], _) => decoder(Schema.Sequence(schema, (c: Chunk[s]) => scala.collection.immutable.Set(c: _*), (m: Set[s]) => Chunk.fromIterable(m), identity = "Set")) - case Schema.Transform(codec, f, _, _, _) => transformDecoder(codec, f) - case Schema.Primitive(standardType, _) => primitiveDecoder(standardType) - case Schema.Tuple2(left, right, _) => tupleDecoder(left, right) - case Schema.Optional(codec, _) => optionalDecoder(codec) - case Schema.Fail(message, _) => fail(ReadError(Cause.empty, message)) - case Schema.Either(left, right, _) => eitherDecoder(left, right) - case lzy @ Schema.Lazy(_) => decoder(lzy.schema) - // case Schema.Meta(_, _) => astDecoder - case s: Schema.CaseClass0[A] => caseClass0Decoder(s) - case s: Schema.CaseClass1[_, A] => caseClass1Decoder(s) - case s: Schema.CaseClass2[_, _, A] => caseClass2Decoder(s) - case s: Schema.CaseClass3[_, _, _, A] => caseClass3Decoder(s) - case s: Schema.CaseClass4[_, _, _, _, A] => caseClass4Decoder(s) - case s: Schema.CaseClass5[_, _, _, _, _, A] => caseClass5Decoder(s) - case s: Schema.CaseClass6[_, _, _, _, _, _, A] => caseClass6Decoder(s) - case s: Schema.CaseClass7[_, _, _, _, _, _, _, A] => caseClass7Decoder(s) - case s: Schema.CaseClass8[_, _, _, _, _, _, _, _, A] => caseClass8Decoder(s) - case s: Schema.CaseClass9[_, _, _, _, _, _, _, _, _, A] => caseClass9Decoder(s) - case s: Schema.CaseClass10[_, _, _, _, _, _, _, _, _, _, A] => caseClass10Decoder(s) - case s: Schema.CaseClass11[_, _, _, _, _, _, _, _, _, _, _, A] => caseClass11Decoder(s) - case s: Schema.CaseClass12[_, _, _, _, _, _, _, _, _, _, _, _, A] => caseClass12Decoder(s) - case s: Schema.CaseClass13[_, _, _, _, _, _, _, _, _, _, _, _, _, A] => caseClass13Decoder(s) - case s: Schema.CaseClass14[_, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => caseClass14Decoder(s) - case s: Schema.CaseClass15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => caseClass15Decoder(s) - case s: Schema.CaseClass16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => caseClass16Decoder(s) - case s: Schema.CaseClass17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => caseClass17Decoder(s) - case s: Schema.CaseClass18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => caseClass18Decoder(s) - case s: Schema.CaseClass19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => caseClass19Decoder(s) - case s: Schema.CaseClass20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => - caseClass20Decoder(s) - case s: Schema.CaseClass21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => - caseClass21Decoder(s) - case s: Schema.CaseClass22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => - caseClass22Decoder(s) - case Schema.Enum1(_, c, _) => enumDecoder(c) - case Schema.Enum2(_, c1, c2, _) => enumDecoder(c1, c2) - case Schema.Enum3(_, c1, c2, c3, _) => enumDecoder(c1, c2, c3) - case Schema.Enum4(_, c1, c2, c3, c4, _) => enumDecoder(c1, c2, c3, c4) - case Schema.Enum5(_, c1, c2, c3, c4, c5, _) => enumDecoder(c1, c2, c3, c4, c5) - case Schema.Enum6(_, c1, c2, c3, c4, c5, c6, _) => enumDecoder(c1, c2, c3, c4, c5, c6) - case Schema.Enum7(_, c1, c2, c3, c4, c5, c6, c7, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7) - case Schema.Enum8(_, c1, c2, c3, c4, c5, c6, c7, c8, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8) - case Schema.Enum9(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9) - case Schema.Enum10(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) - case Schema.Enum11(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) - case Schema.Enum12(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) - case Schema.Enum13(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) - case Schema.Enum14(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14) - case Schema.Enum15(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15) - case Schema.Enum16(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16) - case Schema.Enum17(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17) - case Schema.Enum18(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18) - case Schema.Enum19(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19) - case Schema.Enum20(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20) - case Schema.Enum21(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21) - case Schema.Enum22(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22, _) => enumDecoder(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22) - case Schema.EnumN(_, cs, _) => enumDecoder(cs.toSeq: _*) - case Schema.Dynamic(_) => dynamicDecoder + private val state: DecoderState = new DecoderState(chunk, 0) + + def decode[A](schema: Schema[A]): scala.util.Either[DecodeError, A] = + try { + Right(create(schema).asInstanceOf[A]) + } catch { + case CreateValueFromSchemaError(_, cause) => + cause match { + case error: DecodeError => Left(error) + case _ => + Left(DecodeError.ReadError(Cause.fail(cause), cause.getMessage)) + } + case NonFatal(err) => + Left(DecodeError.ReadError(Cause.fail(err), err.getMessage)) + } + + private def createTypedPrimitive[A](context: DecoderContext, standardType: StandardType[A]): A = + createPrimitive(context, standardType).asInstanceOf[A] + + override protected def createPrimitive(context: DecoderContext, typ: StandardType[_]): Any = + typ match { + case StandardType.UnitType => () + case StandardType.StringType => stringDecoder(context) + case StandardType.BoolType => varIntDecoder(context) != 0 + case StandardType.ShortType => varIntDecoder(context).shortValue + case StandardType.ByteType => varIntDecoder(context).byteValue + case StandardType.IntType => varIntDecoder(context).intValue + case StandardType.LongType => varIntDecoder(context) + case StandardType.FloatType => floatDecoder(context) + case StandardType.DoubleType => doubleDecoder(context) + case StandardType.BigIntegerType => + val bytes = binaryDecoder(context) + new java.math.BigInteger(bytes.toArray) + case StandardType.BigDecimalType => + val unscaled = createTypedPrimitive(rawFieldDecoder(context, 1), StandardType.BigIntegerType) + val precision = createTypedPrimitive(rawFieldDecoder(context, 2), StandardType.IntType) + val scale = createTypedPrimitive(rawFieldDecoder(context, 3), StandardType.IntType) + val ctx = new java.math.MathContext(precision) + new java.math.BigDecimal(unscaled, scale, ctx) + + case StandardType.BinaryType => binaryDecoder(context) + case StandardType.CharType => stringDecoder(context).charAt(0) + case StandardType.UUIDType => + val uuid = stringDecoder(context) + try UUID.fromString(uuid) + catch { + case NonFatal(_) => + throw MalformedField(Schema.primitive[UUID], s"Invalid UUID string $uuid") + } + case StandardType.DayOfWeekType => + DayOfWeek.of(varIntDecoder(context).intValue) + case StandardType.MonthType => + Month.of(varIntDecoder(context).intValue) + case StandardType.MonthDayType => + val month = createTypedPrimitive(rawFieldDecoder(context, 1), StandardType.IntType) + val day = createTypedPrimitive(rawFieldDecoder(context, 2), StandardType.IntType) + MonthDay.of(month, day) + + case StandardType.PeriodType => + val years = createTypedPrimitive(rawFieldDecoder(context, 1), StandardType.IntType) + val months = createTypedPrimitive(rawFieldDecoder(context, 2), StandardType.IntType) + val days = createTypedPrimitive(rawFieldDecoder(context, 3), StandardType.IntType) + Period.of(years, months, days) + case StandardType.YearType => + Year.of(varIntDecoder(context).intValue) + case StandardType.YearMonthType => + val year = createTypedPrimitive(rawFieldDecoder(context, 1), StandardType.IntType) + val month = createTypedPrimitive(rawFieldDecoder(context, 2), StandardType.IntType) + YearMonth.of(year, month) + case StandardType.ZoneIdType => ZoneId.of(stringDecoder(context)) + case StandardType.ZoneOffsetType => + ZoneOffset.ofTotalSeconds(varIntDecoder(context).intValue) + case StandardType.DurationType => + val seconds = createTypedPrimitive(rawFieldDecoder(context, 1), StandardType.LongType) + val nanos = createTypedPrimitive(rawFieldDecoder(context, 2), StandardType.IntType) + Duration.ofSeconds(seconds, nanos.toLong) + case StandardType.InstantType(formatter) => + Instant.from(formatter.parse(stringDecoder(context))) + case StandardType.LocalDateType(formatter) => + LocalDate.parse(stringDecoder(context), formatter) + case StandardType.LocalTimeType(formatter) => + LocalTime.parse(stringDecoder(context), formatter) + case StandardType.LocalDateTimeType(formatter) => + LocalDateTime.parse(stringDecoder(context), formatter) + case StandardType.OffsetTimeType(formatter) => + OffsetTime.parse(stringDecoder(context), formatter) + case StandardType.OffsetDateTimeType(formatter) => + OffsetDateTime.parse(stringDecoder(context), formatter) + case StandardType.ZonedDateTimeType(formatter) => + ZonedDateTime.parse(stringDecoder(context), formatter) + case st => throw ReadError(Cause.empty, s"Unsupported primitive type $st") + } + + override protected def startCreatingRecord(context: DecoderContext, record: Schema.Record[_]): DecoderContext = + context + + override protected def startReadingField( + context: DecoderContext, + record: Schema.Record[_], + index: Int + ): Option[(DecoderContext, Int)] = + if (index == record.fields.size) { + None + } else { + keyDecoder(context) match { + case (wt, fieldNumber) => + if (record.fields.isDefinedAt(fieldNumber - 1)) { + Some(wt match { + case LengthDelimited(width) => + (context.limitedTo(state, width), fieldNumber - 1) + case _ => + (context, fieldNumber - 1) + }) + } else { + throw ExtraFields( + "Unknown", + s"Failed to decode record. Schema does not contain field number $fieldNumber." + ) + } + } } - //scalafmt: { maxColumn = 120, optIn.configStyleArguments = true } - private val dynamicDecoder: ProtobufDecoder[DynamicValue] = - decoder(DynamicValueSchema.schema) + override protected def createRecord( + context: DecoderContext, + record: Schema.Record[_], + values: Chunk[(Int, Any)] + ): Any = + Unsafe.unsafe { implicit u => + record.construct(values.map(_._2)) match { + case Right(result) => result + case Left(message) => throw DecodeError.ReadError(Cause.empty, message) + } + } - private def enumDecoder[Z](cases: Schema.Case[Z, _]*): ProtobufDecoder[Z] = - keyDecoder.flatMap { + override protected def startCreatingEnum( + context: DecoderContext, + cases: Chunk[Schema.Case[_, _]] + ): (DecoderContext, Int) = + keyDecoder(context) match { case (wt, fieldNumber) if fieldNumber <= cases.length => - val subtypeCase = cases(fieldNumber - 1) wt match { case LengthDelimited(width) => - decoder(subtypeCase.schema) - .take(width) - .asInstanceOf[ProtobufDecoder[Z]] + (context.limitedTo(state, width), fieldNumber - 1) case _ => - decoder(subtypeCase.schema) - .asInstanceOf[ProtobufDecoder[Z]] + (context, fieldNumber - 1) } case (_, fieldNumber) => - fail( - MissingField( - cases(fieldNumber - 1).schema, - s"Failed to decode enumeration. Schema does not contain field number $fieldNumber." - ) + throw MissingField( + cases(fieldNumber - 1).schema, + s"Failed to decode enumeration. Schema does not contain field number $fieldNumber." ) } - private def recordDecoder[Z]( - fields: Seq[Schema.Field[Z, _]], - decoded: Int = 0 - ): ProtobufDecoder[ListMap[String, _]] = - if (fields.isEmpty || (fields.size == decoded)) - ProtobufDecoder.succeed(ListMap.empty) - else - keyDecoder.flatMap { - case (wt, fieldNumber) => - if (fields.isDefinedAt(fieldNumber - 1)) { - val Schema.Field(fieldName, schema, _, _, _, _) = fields(fieldNumber - 1) - - wt match { - case LengthDelimited(width) => - for { - fieldValue <- decoder(schema).take(width) - remainder <- recordDecoder(fields, decoded + 1) - } yield (remainder.updated(fieldName, fieldValue)) + override protected def createEnum( + context: DecoderContext, + cases: Chunk[Schema.Case[_, _]], + index: Int, + value: Any + ): Any = + value + + override protected def startCreatingSequence( + context: DecoderContext, + schema: Schema.Sequence[_, _, _] + ): Option[DecoderContext] = + keyDecoder(context) match { + case (LengthDelimited(0), 1) => + None + case (LengthDelimited(width), 2) => + Some(context.limitedTo(state, width).copy(packed = canBePacked(schema.elementSchema))) + case (wt, fieldNumber) => + throw MalformedField(schema, s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") + } - case _ => - for { - fieldValue <- decoder(schema) - remainder <- recordDecoder(fields, decoded + 1) - } yield (remainder.updated(fieldName, fieldValue)) - } - } else { - fail( - ExtraFields( - fieldNumber.toString, - s"Failed to decode record. Schema does not contain field number $fieldNumber." - ) - ) + override protected def startCreatingOneSequenceElement( + context: DecoderContext, + schema: Schema.Sequence[_, _, _] + ): DecoderContext = + if (context.packed) + context + else { + keyDecoder(context) match { + case (wt, _) => + wt match { + case LengthDelimited(elemWidth) => + context.limitedTo(state, elemWidth) + case _ => + throw MalformedField(schema, s"Unexpected wire type $wt for non-packed sequence") } } + } - private def packedSequenceDecoder[A](schema: Schema[A]): ProtobufDecoder[Chunk[A]] = - keyDecoder.flatMap { - case (LengthDelimited(0), 1) => succeed(Chunk.empty) + override protected def finishedCreatingOneSequenceElement( + context: DecoderContext, + schema: Schema.Sequence[_, _, _], + index: Int + ): Boolean = + state.length(context) > 0 + + override protected def createSequence( + context: DecoderContext, + schema: Schema.Sequence[_, _, _], + values: Chunk[Any] + ): Any = + schema.fromChunk.asInstanceOf[Chunk[Any] => Any](values) + + override protected def startCreatingDictionary( + context: DecoderContext, + schema: Schema.Map[_, _] + ): Option[DecoderContext] = + keyDecoder(context) match { + case (LengthDelimited(0), 1) => + None case (LengthDelimited(width), 2) => - schema match { - case lzy @ Schema.Lazy(_) => decoder(lzy.schema).loop.take(width) - case _ => decoder(schema).loop.take(width) - } + Some(context.limitedTo(state, width).copy(packed = canBePacked(schema.keySchema.zip(schema.valueSchema)))) case (wt, fieldNumber) => - fail(MalformedField(schema, s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence")) + throw MalformedField(schema, s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") } - private def nonPackedSequenceDecoder[A](schema: Schema[A]): ProtobufDecoder[Chunk[A]] = - keyDecoder.flatMap { - case (LengthDelimited(0), 1) => succeed(Chunk.empty) - case (LengthDelimited(width), 2) => - keyDecoder.flatMap { + override protected def startCreatingOneDictionaryElement( + context: DecoderContext, + schema: Schema.Map[_, _] + ): DecoderContext = { + val elemContext = + if (context.packed) { + context + } else { + keyDecoder(context) match { case (wt, _) => wt match { - case LengthDelimited(width) => decoder(schema).take(width) - case _ => fail(MalformedField(schema, s"Unexpected wire type $wt for non-packed sequence")) + case LengthDelimited(elemWidth) => + context.limitedTo(state, elemWidth) + case _ => + throw MalformedField(schema, s"Unexpected wire type $wt for non-packed sequence") } - }.loop.take(width) + } + } + enterFirstTupleElement(elemContext, schema).copy(dictionaryElementContext = Some(elemContext)) + } + + override protected def startCreatingOneDictionaryValue( + context: DecoderContext, + schema: Schema.Map[_, _] + ): DecoderContext = + enterSecondTupleElement(context.dictionaryElementContext.getOrElse(context), schema) + + override protected def finishedCreatingOneDictionaryElement( + context: DecoderContext, + schema: Schema.Map[_, _], + index: Int + ): Boolean = + state.length(context) > 0 + + override protected def createDictionary( + context: DecoderContext, + schema: Schema.Map[_, _], + values: Chunk[(Any, Any)] + ): Any = + values.toMap + + override protected def startCreatingSet(context: DecoderContext, schema: Schema.Set[_]): Option[DecoderContext] = + keyDecoder(context) match { + case (LengthDelimited(0), 1) => + None + case (LengthDelimited(width), 2) => + Some(context.limitedTo(state, width).copy(packed = canBePacked(schema.elementSchema))) case (wt, fieldNumber) => - fail( - MalformedField(schema, s"Invalid wire type ($wt) or field number ($fieldNumber) for non-packed sequence") - ) + throw MalformedField(schema, s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") } - private def tupleDecoder[A, B](left: Schema[A], right: Schema[B]): ProtobufDecoder[(A, B)] = { - def elementDecoder[A](schema: Schema[A], wt: WireType): ProtobufDecoder[A] = wt match { - case LengthDelimited(width) => decoder(schema).take(width) - case _ => decoder(schema) + override protected def startCreatingOneSetElement(context: DecoderContext, schema: Schema.Set[_]): DecoderContext = + if (context.packed) { + context + } else { + keyDecoder(context) match { + case (wt, _) => + wt match { + case LengthDelimited(elemWidth) => + context.limitedTo(state, elemWidth) + case _ => + throw MalformedField(schema, s"Unexpected wire type $wt for non-packed sequence") + } + } } - keyDecoder.flatMap { - case (wt, 1) => - elementDecoder(left, wt).flatMap { leftValue => - keyDecoder.flatMap { - case (wt, 2) => - elementDecoder(right, wt).map(rightValue => (leftValue, rightValue)) - case (_, fieldNumber) => fail(MalformedField(left, s"Invalid field number ($fieldNumber) for tuple")) - } - } - case (wt, 2) => - elementDecoder(right, wt).flatMap { rightValue => - keyDecoder.flatMap { - case (wt, 1) => - elementDecoder(left, wt).map(leftValue => (leftValue, rightValue)) - case (_, fieldNumber) => fail(MalformedField(right, s"Invalid field number ($fieldNumber) for tuple")) - } - } + override protected def finishedCreatingOneSetElement( + context: DecoderContext, + schema: Schema.Set[_], + index: Int + ): Boolean = + state.length(context) > 0 + + override protected def createSet(context: DecoderContext, schema: Schema.Set[_], values: Chunk[Any]): Any = + values.toSet + + override protected def startCreatingOptional( + context: DecoderContext, + schema: Schema.Optional[_] + ): Option[DecoderContext] = + keyDecoder(context) match { + case (LengthDelimited(0), 1) => None + case (LengthDelimited(width), 2) => Some(context.limitedTo(state, width)) + case (_, 2) => Some(context) case (_, fieldNumber) => - fail(ExtraFields(fieldNumber.toString, s"Invalid field number ($fieldNumber) for tuple")) + throw MalformedField(schema, s"Invalid field number $fieldNumber for option") } - } - private def eitherDecoder[A, B](left: Schema[A], right: Schema[B]): ProtobufDecoder[scala.util.Either[A, B]] = - keyDecoder.flatMap { - case (_, fieldNumber) if fieldNumber == 1 => decoder(left).map(Left(_)) - case (_, fieldNumber) if fieldNumber == 2 => decoder(right).map(Right(_)) + override protected def createOptional( + context: DecoderContext, + schema: Schema.Optional[_], + value: Option[Any] + ): Any = + value + + override protected def startCreatingEither( + context: DecoderContext, + schema: Schema.Either[_, _] + ): Either[DecoderContext, DecoderContext] = + keyDecoder(context) match { + case (_, fieldNumber) if fieldNumber == 1 => Left(context) + case (_, fieldNumber) if fieldNumber == 2 => Right(context) case (_, fieldNumber) => - fail(ExtraFields(fieldNumber.toString, s"Invalid field number ($fieldNumber) for either")) + throw ExtraFields(fieldNumber.toString, s"Invalid field number ($fieldNumber) for either") } - private def optionalDecoder[A](schema: Schema[A]): ProtobufDecoder[Option[A]] = - keyDecoder.flatMap { - case (LengthDelimited(0), 1) => succeed(None) - case (LengthDelimited(width), 2) => decoder(schema).take(width).map(Some(_)) - case (_, 2) => decoder(schema).map(Some(_)) - case (_, fieldNumber) => fail(MalformedField(schema, s"Invalid field number $fieldNumber for option")) - } + override protected def createEither( + context: DecoderContext, + schema: Schema.Either[_, _], + value: Either[Any, Any] + ): Any = + value - private def floatDecoder: ProtobufDecoder[Float] = - ProtobufDecoder(bytes => { - if (bytes.size < 4) { - Left( - MalformedField(Schema.primitive[Float], s"Invalid number of bytes for Float. Expected 4, got ${bytes.size}") - ) - } else { - Right((bytes, ByteBuffer.wrap(bytes.toArray).order(ByteOrder.LITTLE_ENDIAN).getFloat())) - } - }).take(4) - - private def doubleDecoder: ProtobufDecoder[Double] = - ProtobufDecoder(bytes => { - if (bytes.size < 8) { - Left( - MalformedField( - Schema.primitive[Double], - s"Invalid number of bytes for Double. Expected 8, got ${bytes.size}" - ) - ) - } else { - Right((bytes, ByteBuffer.wrap(bytes.toArray).order(ByteOrder.LITTLE_ENDIAN).getDouble())) - } - }).take(8) - - private def transformDecoder[A, B]( - schema: Schema[B], - f: B => scala.util.Either[String, A] - ): ProtobufDecoder[A] = - schema match { - case Schema.Primitive(typ, _) if typ == StandardType.UnitType => - ProtobufDecoder { (chunk: Chunk[Byte]) => - f(().asInstanceOf[B]) match { - case Left(err) => Left(MalformedField(schema, err)) - case Right(b) => Right(chunk -> b) - } + override protected def startCreatingTuple(context: DecoderContext, schema: Schema.Tuple2[_, _]): DecoderContext = + enterFirstTupleElement(context, schema) + + private def enterFirstTupleElement(context: DecoderContext, schema: Schema[_]): DecoderContext = + keyDecoder(context) match { + case (wt, 1) => + wt match { + case LengthDelimited(width) => context.limitedTo(state, width) + case _ => context } - case _ => - decoder(schema).flatMap( - a => - ProtobufDecoder( - chunk => - f(a).map(b => (chunk, b)) match { - case Left(err) => Left(MalformedField(schema, err)) - case Right(value) => Right(value) - } - ) - ) + case (_, fieldNumber) => + throw MalformedField(schema, s"Invalid field number $fieldNumber for tuple's first field") } - private def primitiveDecoder[A](standardType: StandardType[A]): ProtobufDecoder[A] = - standardType match { - case StandardType.UnitType => ProtobufDecoder((chunk: Chunk[Byte]) => Right((chunk, ()))) - case StandardType.StringType => stringDecoder - case StandardType.BoolType => varIntDecoder.map(_ != 0) - case StandardType.ShortType => varIntDecoder.map(_.shortValue()) - case StandardType.ByteType => varIntDecoder.map(_.byteValue()) - case StandardType.IntType => varIntDecoder.map(_.intValue()) - case StandardType.LongType => varIntDecoder - case StandardType.FloatType => floatDecoder - case StandardType.DoubleType => doubleDecoder - case StandardType.BigIntegerType => - binaryDecoder.map { bytes => - new java.math.BigInteger(bytes.toArray) - } - case StandardType.BigDecimalType => - recordDecoder(bigDecimalStructure).flatMap { data => - val opt = for { - unscaled <- data.get("unscaled").asInstanceOf[Option[java.math.BigInteger]] - scale <- data.get("scale").asInstanceOf[Option[Int]] - precision <- data.get("precision").asInstanceOf[Option[Int]] - ctx = new java.math.MathContext(precision) - } yield new java.math.BigDecimal(unscaled, scale, ctx) - - opt match { - case Some(value) => succeedNow(value) - case None => - fail(MalformedField(Schema.primitive[java.math.BigDecimal], s"Invalid big decimal record $data")) - } - } - case StandardType.BinaryType => binaryDecoder - case StandardType.CharType => stringDecoder.map(_.charAt(0)) - case StandardType.UUIDType => - stringDecoder.flatMap { uuid => - try succeedNow(UUID.fromString(uuid)) - catch { - case NonFatal(_) => fail(MalformedField(Schema.primitive[UUID], s"Invalid UUID string $uuid")) - } + override protected def startReadingSecondTupleElement( + context: DecoderContext, + schema: Schema.Tuple2[_, _] + ): DecoderContext = + enterSecondTupleElement(context, schema) + + private def enterSecondTupleElement(context: DecoderContext, schema: Schema[_]): DecoderContext = + keyDecoder(context) match { + case (wt, 2) => + wt match { + case LengthDelimited(width) => context.limitedTo(state, width) + case _ => context } - case StandardType.DayOfWeekType => - varIntDecoder.map(_.intValue).map(DayOfWeek.of) - case StandardType.MonthType => - varIntDecoder.map(_.intValue).map(Month.of) - case StandardType.MonthDayType => - recordDecoder(monthDayStructure) - .map( - data => - MonthDay.of(data.getOrElse("month", 0).asInstanceOf[Int], data.getOrElse("day", 0).asInstanceOf[Int]) - ) - case StandardType.PeriodType => - recordDecoder(periodStructure) - .map( - data => - Period.of( - data.getOrElse("years", 0).asInstanceOf[Int], - data.getOrElse("months", 0).asInstanceOf[Int], - data.getOrElse("days", 0).asInstanceOf[Int] - ) - ) - case StandardType.YearType => - varIntDecoder.map(_.intValue).map(Year.of) - case StandardType.YearMonthType => - recordDecoder(yearMonthStructure) - .map( - data => - YearMonth.of(data.getOrElse("year", 0).asInstanceOf[Int], data.getOrElse("month", 0).asInstanceOf[Int]) - ) - case StandardType.ZoneIdType => stringDecoder.map(ZoneId.of) - case StandardType.ZoneOffsetType => - varIntDecoder - .map(_.intValue) - .map(ZoneOffset.ofTotalSeconds) - case StandardType.DurationType => - recordDecoder(durationStructure) - .map( - data => - Duration.ofSeconds( - data.getOrElse("seconds", 0).asInstanceOf[Long], - data.getOrElse("nanos", 0).asInstanceOf[Int].toLong - ) - ) - case StandardType.InstantType(formatter) => - stringDecoder.map(v => Instant.from(formatter.parse(v))) - case StandardType.LocalDateType(formatter) => - stringDecoder.map(LocalDate.parse(_, formatter)) - case StandardType.LocalTimeType(formatter) => - stringDecoder.map(LocalTime.parse(_, formatter)) - case StandardType.LocalDateTimeType(formatter) => - stringDecoder.map(LocalDateTime.parse(_, formatter)) - case StandardType.OffsetTimeType(formatter) => - stringDecoder.map(OffsetTime.parse(_, formatter)) - case StandardType.OffsetDateTimeType(formatter) => - stringDecoder.map(OffsetDateTime.parse(_, formatter)) - case StandardType.ZonedDateTimeType(formatter) => - stringDecoder.map(ZonedDateTime.parse(_, formatter)) - case st => fail(ReadError(Cause.empty, s"Unsupported primitive type $st")) + case (_, fieldNumber) => + throw MalformedField(schema, s"Invalid field number $fieldNumber for tuple's second field") } + override protected def createTuple( + context: DecoderContext, + schema: Schema.Tuple2[_, _], + left: Any, + right: Any + ): Any = + (left, right) + + override protected def createDynamic(context: DecoderContext): Option[Any] = + None + + override protected def transform( + context: DecoderContext, + value: Any, + f: Any => Either[String, Any], + schema: Schema[_] + ): Any = + f(value) match { + case Left(value) => throw MalformedField(schema, value) + case Right(value) => value + } + + override protected def fail(context: DecoderContext, message: String): Any = + throw DecodeError.ReadError(Cause.empty, message) + + override protected val initialContext: DecoderContext = + DecoderContext(limit = None, packed = false, dictionaryElementContext = None) + /** * Decodes key which consist out of field type (wire type) and a field number. * * 8 >>> 3 => 1, 16 >>> 3 => 2, 24 >>> 3 => 3, 32 >>> 3 => 4 * 0 & 0x07 => 0, 1 & 0x07 => 1, 2 & 0x07 => 2, 9 & 0x07 => 1, 15 & 0x07 => 7 */ - private[codec] def keyDecoder: ProtobufDecoder[(WireType, Int)] = - varIntDecoder.flatMap { key => - val fieldNumber = (key >>> 3).toInt - if (fieldNumber < 1) { - fail(ExtraFields(fieldNumber.toString, s"Failed decoding key. Invalid field number $fieldNumber")) - } else { - key & 0x07 match { - case 0 => succeed((WireType.VarInt, fieldNumber)) - case 1 => succeed((WireType.Bit64, fieldNumber)) - case 2 => - varIntDecoder.map(length => (WireType.LengthDelimited(length.toInt), fieldNumber)) - case 3 => succeed((WireType.StartGroup, fieldNumber)) - case 4 => succeed((WireType.EndGroup, fieldNumber)) - case 5 => succeed((WireType.Bit32, fieldNumber)) - case n => fail(ExtraFields(fieldNumber.toString, s"Failed decoding key. Unknown wire type $n")) - } + private[codec] def keyDecoder(context: DecoderContext): (WireType, Int) = { + val key = varIntDecoder(context) + val fieldNumber = (key >>> 3).toInt + if (fieldNumber < 1) { + throw ExtraFields(fieldNumber.toString, s"Failed decoding key. Invalid field number $fieldNumber") + } else { + key & 0x07 match { + case 0 => (WireType.VarInt, fieldNumber) + case 1 => (WireType.Bit64, fieldNumber) + case 2 => + val length = varIntDecoder(context) + (WireType.LengthDelimited(length.toInt), fieldNumber) + case 3 => (WireType.StartGroup, fieldNumber) + case 4 => (WireType.EndGroup, fieldNumber) + case 5 => (WireType.Bit32, fieldNumber) + case n => + throw ExtraFields(fieldNumber.toString, s"Failed decoding key. Unknown wire type $n") } } + } + + private def rawFieldDecoder(context: DecoderContext, expectedFieldNumber: Int): DecoderContext = + keyDecoder(context) match { + case (wt, fieldNumber) if fieldNumber == expectedFieldNumber => + wt match { + case LengthDelimited(width) => + context.limitedTo(state, width) + case _ => + context + } + case _ => + throw ExtraFields( + "Unknown", + s"Failed to decode record. Schema does not contain field number $expectedFieldNumber." + ) + } + + private def floatDecoder(context: DecoderContext): Float = + if (state.length(context) < 4) + throw MalformedField( + Schema.primitive[Float], + s"Invalid number of bytes for Float. Expected 4, got ${state.length(context)}" + ) + else { + val bytes = state.read(4) + ByteBuffer.wrap(bytes.toArray).order(ByteOrder.LITTLE_ENDIAN).getFloat() + } + + private def doubleDecoder(context: DecoderContext): Double = + if (state.length(context) < 8) + throw MalformedField( + Schema.primitive[Double], + s"Invalid number of bytes for Double. Expected 8, got ${state.length(context)}" + ) + else { + val bytes = state.read(8) + ByteBuffer.wrap(bytes.toArray).order(ByteOrder.LITTLE_ENDIAN).getDouble() + } + + private def stringDecoder(context: DecoderContext): String = { + val bytes = state.all(context) + new String(bytes.toArray, StandardCharsets.UTF_8) + } /** * Decodes bytes to following types: int32, int64, uint32, uint64, sint32, sint64, bool, enumN. @@ -972,436 +943,28 @@ object ProtobufCodec extends BinaryCodec { * (0 << 7 => 0, 1 << 7 => 128, 2 << 7 => 256, 3 << 7 => 384 * 1 & 0X7F => 1, 127 & 0x7F => 127, 128 & 0x7F => 0, 129 & 0x7F => 1 */ - private def varIntDecoder: ProtobufDecoder[Long] = - ProtobufDecoder( - (chunk) => - if (chunk.isEmpty) { - Left(MalformedField(Schema.primitive[Long], "Failed to decode VarInt. Unexpected end of chunk")) - } else { - val length = chunk.indexWhere(octet => (octet.longValue() & 0x80) != 0x80) + 1 - if (length <= 0) { - Left( - MalformedField( - Schema.primitive[Long], - "Failed to decode VarInt. No byte within the range 0 - 127 are present" - ) - ) - } else { - val value = chunk.take(length).foldRight(0L)((octet, v) => (v << 7) + (octet & 0x7F)) - Right((chunk.drop(length), value)) - } - } - ) - } - - //scalafmt: { maxColumn = 400, optIn.configStyleArguments = false } - private[codec] object ProductEncoder { - - private[codec] def encodeCaseClass[Z](value: Z, fields: (Schema.Field[Z, _])*): Option[Int] => Chunk[Byte] = { (fieldNumber: Option[Int]) => - { - val encoded = Chunk - .fromIterable(fields.zipWithIndex.map { - case ((Schema.Field(_, schema, _, _, get, _)), fieldNumber) => - ProtobufEncoder.encode(Some(fieldNumber + 1), schema.asInstanceOf[Schema[Any]], get(value)) - }) - .flatten - ProtobufEncoder.encodeKey(Protobuf.WireType.LengthDelimited(encoded.size), fieldNumber) ++ encoded - } - } - } - - //scalafmt: { maxColumn = 400, optIn.configStyleArguments = false } - private[codec] object ProductDecoder { - import ProtobufDecoder.{ fail, keyDecoder, succeed } - import Protobuf.WireType._ - - private def unsafeDecodeFields[Z](buffer: Array[Any], fields: Schema.Field[Z, _]*): ProtobufDecoder[Array[Any]] = - keyDecoder.flatMap { - case (wt, fieldNumber) if fieldNumber == fields.length => - wt match { - case LengthDelimited(width) => - ProtobufDecoder - .decoder(fields(fieldNumber - 1).schema) - .take(width) - .map(fieldValue => buffer.updated(fieldNumber - 1, fieldValue)) - case _ => - ProtobufDecoder - .decoder(fields(fieldNumber - 1).schema) - .map(fieldValue => buffer.updated(fieldNumber - 1, fieldValue)) - } - case (wt, fieldNumber) => - if (fieldNumber <= fields.length) { - wt match { - case LengthDelimited(width) => - for { - fieldValue <- ProtobufDecoder.decoder(fields(fieldNumber - 1).schema).take(width) - remainder <- unsafeDecodeFields(buffer, fields: _*) - } yield remainder.updated(fieldNumber - 1, fieldValue) - case _ => - for { - fieldValue <- ProtobufDecoder.decoder(fields(fieldNumber - 1).schema) - remainder <- unsafeDecodeFields(buffer, fields: _*) - } yield remainder.updated(fieldNumber - 1, fieldValue) - } - } else { - fail(ExtraFields("Unknown", s"Failed to decode record. Schema does not contain field number $fieldNumber.")) - } + private def varIntDecoder(context: DecoderContext): Long = + if (state.length(context) == 0) { + throw MalformedField(Schema.primitive[Long], "Failed to decode VarInt. Unexpected end of chunk") + } else { + val chunk = state.peek(context) + val length = chunk.indexWhere(octet => (octet.longValue() & 0x80) != 0x80) + 1 + if (length <= 0) { + throw MalformedField( + Schema.primitive[Long], + "Failed to decode VarInt. No byte within the range 0 - 127 are present" + ) + } else { + state.move(length) + chunk.take(length).foldRight(0L)((octet, v) => (v << 7) + (octet & 0x7F)) + } } - @tailrec - private def validateBuffer(schema: Schema[_], index: Int, buffer: Array[Any]): ProtobufDecoder[Array[Any]] = - if (index == buffer.length - 1 && buffer(index) != null) - succeed(buffer) - else if (buffer(index) == null) - fail(MissingField(schema, s"Failed to decode record. Missing field number $index.")) - else - validateBuffer(schema, index + 1, buffer) - - private[codec] def caseClass0Decoder[Z](schema: Schema.CaseClass0[Z]): ProtobufDecoder[Z] = - succeed(schema.defaultConstruct()) - - private[codec] def caseClass1Decoder[A, Z](schema: Schema.CaseClass1[A, Z]): ProtobufDecoder[Z] = - unsafeDecodeFields(Array.ofDim[Any](1), schema.field).flatMap { buffer => - if (buffer(0) == null) - fail(RecordMissingField(schema, schema.field, "Failed to decode record. Missing field 1.")) - else - succeed(schema.defaultConstruct(buffer(0).asInstanceOf[A])) - } + private def binaryDecoder(context: DecoderContext): Chunk[Byte] = + state.all(context) - private[codec] def caseClass2Decoder[A1, A2, Z](schema: Schema.CaseClass2[A1, A2, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](2), schema.field1, schema.field2) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2]) - - private[codec] def caseClass3Decoder[A1, A2, A3, Z](schema: Schema.CaseClass3[A1, A2, A3, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](3), schema.field1, schema.field2, schema.field3) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3]) - - private[codec] def caseClass4Decoder[A1, A2, A3, A4, Z](schema: Schema.CaseClass4[A1, A2, A3, A4, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](4), schema.field1, schema.field2, schema.field3, schema.field4) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4]) - - private[codec] def caseClass5Decoder[A1, A2, A3, A4, A5, Z](schema: Schema.CaseClass5[A1, A2, A3, A4, A5, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](5), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5]) - - private[codec] def caseClass6Decoder[A1, A2, A3, A4, A5, A6, Z](schema: Schema.CaseClass6[A1, A2, A3, A4, A5, A6, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](6), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6]) - - private[codec] def caseClass7Decoder[A1, A2, A3, A4, A5, A6, A7, Z](schema: Schema.CaseClass7[A1, A2, A3, A4, A5, A6, A7, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](7), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7]) - - private[codec] def caseClass8Decoder[A1, A2, A3, A4, A5, A6, A7, A8, Z](schema: Schema.CaseClass8[A1, A2, A3, A4, A5, A6, A7, A8, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](8), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field8) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7], buffer(7).asInstanceOf[A8]) - - private[codec] def caseClass9Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, Z](schema: Schema.CaseClass9[A1, A2, A3, A4, A5, A6, A7, A8, A9, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](9), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7], buffer(7).asInstanceOf[A8], buffer(8).asInstanceOf[A9]) - - private[codec] def caseClass10Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, Z](schema: Schema.CaseClass10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](10), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7], buffer(7).asInstanceOf[A8], buffer(8).asInstanceOf[A9], buffer(9).asInstanceOf[A10]) - - private[codec] def caseClass11Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, Z](schema: Schema.CaseClass11[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](11), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7], buffer(7).asInstanceOf[A8], buffer(8).asInstanceOf[A9], buffer(9).asInstanceOf[A10], buffer(10).asInstanceOf[A11]) - - private[codec] def caseClass12Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, Z](schema: Schema.CaseClass12[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](12), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7], buffer(7).asInstanceOf[A8], buffer(8).asInstanceOf[A9], buffer(9).asInstanceOf[A10], buffer(10).asInstanceOf[A11], buffer(11).asInstanceOf[A12]) - - private[codec] def caseClass13Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, Z](schema: Schema.CaseClass13[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](13), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13] - ) - - private[codec] def caseClass14Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, Z](schema: Schema.CaseClass14[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](14), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14] - ) - - private[codec] def caseClass15Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, Z](schema: Schema.CaseClass15[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](15), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15] - ) - - private[codec] def caseClass16Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, Z](schema: Schema.CaseClass16[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](16), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16] - ) - - private[codec] def caseClass17Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, Z](schema: Schema.CaseClass17[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](17), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16, schema.field17) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17] - ) - - private[codec] def caseClass18Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, Z](schema: Schema.CaseClass18[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](18), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16, schema.field17, schema.field18) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17], - buffer(17).asInstanceOf[A18] - ) - - private[codec] def caseClass19Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, Z](schema: Schema.CaseClass19[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](19), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16, schema.field17, schema.field18, schema.field19) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17], - buffer(17).asInstanceOf[A18], - buffer(18).asInstanceOf[A19] - ) - - private[codec] def caseClass20Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, Z](schema: Schema.CaseClass20[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](20), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16, schema.field17, schema.field18, schema.field19, schema.field20) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17], - buffer(17).asInstanceOf[A18], - buffer(18).asInstanceOf[A19], - buffer(19).asInstanceOf[A20] - ) - - private[codec] def caseClass21Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, Z](schema: Schema.CaseClass21[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](21), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16, schema.field17, schema.field18, schema.field19, schema.field20, schema.field21) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17], - buffer(17).asInstanceOf[A18], - buffer(18).asInstanceOf[A19], - buffer(19).asInstanceOf[A20], - buffer(20).asInstanceOf[A21] - ) - - private[codec] def caseClass22Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, Z](schema: Schema.CaseClass22[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, Z]): ProtobufDecoder[Z] = - for { - buffer <- unsafeDecodeFields( - Array.ofDim[Any](22), - schema.field1, - schema.field2, - schema.field3, - schema.field4, - schema.field5, - schema.field6, - schema.field7, - schema.field9, - schema.field9, - schema.field10, - schema.field11, - schema.field12, - schema.field13, - schema.field14, - schema.field15, - schema.field16, - schema.field17, - schema.field18, - schema.field19, - schema.field20, - schema.field21, - schema.field22 - ) - _ <- validateBuffer(schema, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17], - buffer(17).asInstanceOf[A18], - buffer(18).asInstanceOf[A19], - buffer(19).asInstanceOf[A20], - buffer(20).asInstanceOf[A21], - buffer(21).asInstanceOf[A22] - ) + private[codec] def remainder: Chunk[Byte] = + state.peek(DecoderContext(None, packed = false, dictionaryElementContext = None)) } } diff --git a/zio-schema-protobuf/shared/src/test/scala-2/zio/schema/codec/ProtobufCodecSpec.scala b/zio-schema-protobuf/shared/src/test/scala-2/zio/schema/codec/ProtobufCodecSpec.scala index f3a93243a..bb98f3e14 100644 --- a/zio-schema-protobuf/shared/src/test/scala-2/zio/schema/codec/ProtobufCodecSpec.scala +++ b/zio-schema-protobuf/shared/src/test/scala-2/zio/schema/codec/ProtobufCodecSpec.scala @@ -86,9 +86,9 @@ object ProtobufCodecSpec extends ZIOSpecDefault { }, test("failure") { for { - e <- encode(schemaFail, StringValue("foo")).map(_.size) - e2 <- encodeNS(schemaFail, StringValue("foo")).map(_.size) - } yield assert(e)(equalTo(0)) && assert(e2)(equalTo(0)) + e <- encode(schemaFail, StringValue("foo")).map(_.size).exit + e2 <- encodeNS(schemaFail, StringValue("foo")).map(_.size).exit + } yield assert(e)(dies(anything)) && assert(e2)(dies(anything)) } ), suite("Should successfully encode and decode")( @@ -570,8 +570,8 @@ object ProtobufCodecSpec extends ZIOSpecDefault { val setSchema = Schema.set(Record.schemaRecord) for { - ed <- encodeAndDecode(setSchema, set) ed2 <- encodeAndDecodeNS(setSchema, set) + ed <- encodeAndDecode(setSchema, set) } yield assert(ed)(equalTo(Chunk.succeed(set))) && assert(ed2)(equalTo(set)) }, test("recursive data types") { @@ -582,7 +582,16 @@ object ProtobufCodecSpec extends ZIOSpecDefault { // ed2 <- encodeAndDecodeNS(schema, value) } yield assertTrue(ed == Right(Chunk(value))) //&& assert(ed2)(equalTo(value)) } - } + }, + test("deep recursive data types") { + check(SchemaGen.anyDeepRecursiveTypeAndValue) { + case (schema, value) => + for { + ed <- encodeAndDecode2(schema, value) + // ed2 <- encodeAndDecodeNS(schema, value) + } yield assertTrue(ed == Right(Chunk(value))) //&& assert(ed2)(equalTo(value)) + } + } @@ TestAspect.sized(200) ), suite("Should successfully decode")( test("empty input") { @@ -1002,7 +1011,7 @@ object ProtobufCodecSpec extends ZIOSpecDefault { .succeed(input) .tap(value => printLine(s"Input Value: $value").when(print).ignore) .map(a => ProtobufCodec.encode(schema)(a)) - .tap(encoded => printLine(s"\nEncoded Bytes:\n${toHex(encoded)}").when(print).ignore) + .tap(encoded => printLine(s"\nEncoded Bytes (${encoded.size}):\n${toHex(encoded)}").when(print).ignore) .map(ch => ProtobufCodec.decode(schema)(ch)) .absolve diff --git a/zio-schema-thrift/shared/src/main/scala/zio/schema/codec/ThriftCodec.scala b/zio-schema-thrift/shared/src/main/scala/zio/schema/codec/ThriftCodec.scala index da6e1d49b..67fd0be31 100644 --- a/zio-schema-thrift/shared/src/main/scala/zio/schema/codec/ThriftCodec.scala +++ b/zio-schema-thrift/shared/src/main/scala/zio/schema/codec/ThriftCodec.scala @@ -1,30 +1,22 @@ package zio.schema.codec -import java.math.{ BigInteger, MathContext } import java.nio.ByteBuffer import java.time._ import java.util.UUID -import scala.annotation.tailrec +import scala.annotation.{ nowarn, tailrec } import scala.collection.immutable.ListMap import scala.util.control.NonFatal -import scala.util.{ Failure, Success, Try } import org.apache.thrift.protocol._ +import zio.schema.MutableSchemaBasedValueBuilder.CreateValueFromSchemaError import zio.schema._ import zio.schema.annotation.optionalField -import zio.schema.codec.BinaryCodec._ -import zio.schema.codec.DecodeError.{ EmptyContent, MalformedFieldWithPath } -import zio.schema.codec.ThriftCodec.Thrift.{ - bigDecimalStructure, - durationStructure, - monthDayStructure, - periodStructure, - yearMonthStructure -} +import zio.schema.codec.BinaryCodec.{ BinaryDecoder, BinaryEncoder, BinaryStreamDecoder, BinaryStreamEncoder } +import zio.schema.codec.DecodeError.{ EmptyContent, MalformedFieldWithPath, ReadError, ReadErrorWithPath } import zio.stream.ZPipeline -import zio.{ Chunk, ChunkBuilder, ZIO } +import zio.{ Cause, Chunk, Unsafe, ZIO } object ThriftCodec extends BinaryCodec { @@ -32,10 +24,10 @@ object ThriftCodec extends BinaryCodec { new BinaryEncoder[A] { override def encode(value: A): Chunk[Byte] = - new ThriftEncoder().encode(schema, value) + new Encoder().encode(schema, value) override def streamEncoder: BinaryStreamEncoder[A] = { - val encoder = new ThriftEncoder() + val encoder = new Encoder() ZPipeline.mapChunks { chunk => chunk.flatMap(encoder.encode(schema, _)) } @@ -59,124 +51,320 @@ object ThriftCodec extends BinaryCodec { ) } - private def decodeChunk(chunk: Chunk[Byte]) = - new ThriftDecoder(chunk) - .decode(Chunk.empty, schema) - .left - .map(identity) + private def decodeChunk(chunk: Chunk[Byte]): Either[DecodeError, A] = + if (chunk.isEmpty) + Left(EmptyContent("No bytes to decode")) + else { + try { + Right( + new Decoder(chunk) + .create(schema) + .asInstanceOf[A] + ) + } catch { + case error: CreateValueFromSchemaError[DecoderContext] => + error.cause match { + case error: DecodeError => Left(error) + case _ => + Left( + ReadErrorWithPath(error.context.path, Cause.fail(error.cause), error.cause.getMessage) + ) + } + case NonFatal(err) => + Left(ReadError(Cause.fail(err), err.getMessage)) + } + }: @nowarn + + } + class Encoder extends MutableSchemaBasedValueProcessor[Unit, Encoder.Context] { + import Encoder._ + + override protected def processPrimitive(context: Context, value: Any, typ: StandardType[Any]): Unit = { + writeFieldBegin(context.fieldNumber, getPrimitiveType(typ)) + writePrimitiveType(typ, value) } - object Thrift { - - val bigDecimalStructure: Seq[Schema.Field[java.math.BigDecimal, _]] = - Seq( - Schema.Field( - "unscaled", - Schema.Primitive(StandardType.BigIntegerType), - get = _.unscaledValue(), - set = (a, b: BigInteger) => new java.math.BigDecimal(b, a.scale) - ), - Schema.Field( - "precision", - Schema.Primitive(StandardType.IntType), - get = _.precision(), - set = (a, b: Int) => new java.math.BigDecimal(a.unscaledValue, new MathContext(b)) - ), - Schema - .Field("scale", Schema.Primitive(StandardType.IntType), get = _.scale(), set = (a, b: Int) => a.setScale(b)) - ) - - val monthDayStructure: Seq[Schema.Field[MonthDay, Int]] = - Seq( - Schema.Field( - "month", - Schema.Primitive(StandardType.IntType), - get = (v: MonthDay) => v.getMonthValue, - set = (a, b: Int) => a.`with`(Month.of(b)) - ), - Schema - .Field( - "day", - Schema.Primitive(StandardType.IntType), - get = _.getDayOfMonth, - set = (a, b: Int) => a.withDayOfMonth(b) - ) - ) - - val periodStructure: Seq[Schema.Field[Period, Int]] = Seq( - Schema - .Field("years", Schema.Primitive(StandardType.IntType), get = _.getYears, set = (a, b: Int) => a.withYears(b)), - Schema.Field( - "months", - Schema.Primitive(StandardType.IntType), - get = _.getMonths, - set = (a, b: Int) => a.withMonths(b) - ), - Schema.Field("days", Schema.Primitive(StandardType.IntType), get = _.getDays, set = (a, b: Int) => a.withDays(b)) - ) - - val yearMonthStructure: Seq[Schema.Field[YearMonth, Int]] = - Seq( - Schema.Field( - "year", - Schema.Primitive(StandardType.IntType), - get = _.getYear, - set = (a, b: Int) => a.`with`(Year.of(b)) - ), - Schema.Field( - "month", - Schema.Primitive(StandardType.IntType), - get = _.getMonthValue, - set = (a, b: Int) => a.`with`(Month.of(b)) - ) - ) - - val durationStructure: Seq[Schema.Field[Duration, _]] = - Seq( - Schema.Field( - "seconds", - Schema.Primitive(StandardType.LongType), - get = _.getSeconds, - set = (a, b: Long) => a.plusSeconds(b) - ), - Schema - .Field( - "nanos", - Schema.Primitive(StandardType.IntType), - get = _.getNano, - set = (a, b: Int) => a.plusNanos(b.toLong) + override protected def startProcessingRecord(context: Context, schema: Schema.Record[_]): Unit = + writeFieldBegin(context.fieldNumber, TType.STRUCT) + + override protected def processRecord( + context: Context, + schema: Schema.Record[_], + value: ListMap[String, Unit] + ): Unit = + writeFieldEnd() + + override protected def startProcessingEnum(context: Context, schema: Schema.Enum[_]): Unit = + writeFieldBegin(context.fieldNumber, TType.STRUCT) + + override protected def processEnum(context: Context, schema: Schema.Enum[_], tuple: (String, Unit)): Unit = + writeFieldEnd() + + override protected def startProcessingSequence( + context: Context, + schema: Schema.Sequence[_, _, _], + size: Int + ): Unit = { + writeFieldBegin(context.fieldNumber, TType.LIST) + writeListBegin(getType(schema.elementSchema), size) + } + + override protected def processSequence( + context: Context, + schema: Schema.Sequence[_, _, _], + value: Chunk[Unit] + ): Unit = {} + + override protected def startProcessingDictionary(context: Context, schema: Schema.Map[_, _], size: Int): Unit = { + writeFieldBegin(context.fieldNumber, TType.MAP) + writeMapBegin(getType(schema.keySchema), getType(schema.valueSchema), size) + } + + override protected def processDictionary( + context: Context, + schema: Schema.Map[_, _], + value: Chunk[(Unit, Unit)] + ): Unit = {} + + override protected def startProcessingSet(context: Context, schema: Schema.Set[_], size: Int): Unit = { + writeFieldBegin(context.fieldNumber, TType.SET) + writeSetBegin(getType(schema.elementSchema), size) + } + + override protected def processSet(context: Context, schema: Schema.Set[_], value: Set[Unit]): Unit = {} + + override protected def startProcessingEither(context: Context, schema: Schema.Either[_, _]): Unit = + writeFieldBegin(context.fieldNumber, TType.STRUCT) + + override protected def processEither( + context: Context, + schema: Schema.Either[_, _], + value: Either[Unit, Unit] + ): Unit = + writeFieldEnd() + + override def startProcessingOption(context: Context, schema: Schema.Optional[_]): Unit = + writeFieldBegin(context.fieldNumber, TType.STRUCT) + + override protected def processOption(context: Context, schema: Schema.Optional[_], value: Option[Unit]): Unit = { + value match { + case None => + processPrimitive( + context.copy(fieldNumber = Some(1)), + (), + StandardType.UnitType.asInstanceOf[StandardType[Any]] ) - ) - } + case _ => + } + writeFieldEnd() + } + + override protected def startProcessingTuple(context: Context, schema: Schema.Tuple2[_, _]): Unit = + writeFieldBegin(context.fieldNumber, TType.STRUCT) + + override protected def processTuple( + context: Context, + schema: Schema.Tuple2[_, _], + left: Unit, + right: Unit + ): Unit = + writeFieldEnd() + + override protected def fail(context: Context, message: String): Unit = + fail(message) + + override protected def processDynamic(context: Context, value: DynamicValue): Option[Unit] = + None + + override protected val initialContext: Context = Context(None) + + override protected def contextForRecordField(context: Context, index: Int, field: Schema.Field[_, _]): Context = + context.copy(fieldNumber = Some((index + 1).toShort)) + + override protected def contextForEnumConstructor(context: Context, index: Int, c: Schema.Case[_, _]): Context = + context.copy(fieldNumber = Some((index + 1).toShort)) + + override protected def contextForEither(context: Context, e: Either[Unit, Unit]): Context = + e match { + case Left(_) => context.copy(fieldNumber = Some(1)) + case Right(_) => context.copy(fieldNumber = Some(2)) + } + + override protected def contextForOption(context: Context, o: Option[Unit]): Context = + o match { + case None => context.copy(fieldNumber = Some(1)) + case Some(_) => context.copy(fieldNumber = Some(2)) + } - final class ThriftEncoder { + override protected def contextForTuple(context: Context, index: Int): Context = + context.copy(fieldNumber = Some(index.toShort)) - val write = new ChunkTransport.Write() - val p = new TBinaryProtocol(write) + override protected def contextForSequence(context: Context, schema: Schema.Sequence[_, _, _], index: Int): Context = + context.copy(fieldNumber = None) - def encode[A](schema: Schema[A], value: A): Chunk[Byte] = { - encodeValue(None, schema, value) + override protected def contextForMap(context: Context, schema: Schema.Map[_, _], index: Int): Context = + context.copy(fieldNumber = None) + + override protected def contextForSet(context: Context, schema: Schema.Set[_], index: Int): Context = + context.copy(fieldNumber = None) + + private[codec] def encode[A](schema: Schema[A], value: A): Chunk[Byte] = { + process(schema, value) write.chunk } - @tailrec - def getType[A](schema: Schema[A]): Byte = schema match { - case _: Schema.Record[A] => TType.STRUCT - case Schema.Sequence(_, _, _, _, _) => TType.LIST - case Schema.Map(_, _, _) => TType.MAP - case Schema.Set(_, _) => TType.SET - case Schema.Transform(schema, _, _, _, _) => getType(schema) - case Schema.Primitive(standardType, _) => getPrimitiveType(standardType) - case Schema.Tuple2(_, _, _) => TType.STRUCT - case Schema.Optional(schema, _) => getType(schema) - case Schema.Either(_, _, _) => TType.STRUCT - case Schema.Lazy(lzy) => getType(lzy()) - case _: Schema.Enum[A] => TType.STRUCT - case _ => TType.VOID - } + private val write = new ChunkTransport.Write() + private val p = new TBinaryProtocol(write) + + private def writeFieldBegin(fieldNumber: Option[Short], ttype: Byte): Unit = + fieldNumber match { + case Some(num) => + p.writeFieldBegin( + new TField("", ttype, num) + ) + case None => + } + + private def writeFieldEnd(): Unit = + p.writeFieldStop() + + private def writeString(value: String): Unit = + p.writeString(value) + + private def writeBool(value: Boolean): Unit = + p.writeBool(value) + + private def writeByte(value: Byte): Unit = + p.writeByte(value) + + private def writeI16(value: Short): Unit = + p.writeI16(value) + + private def writeI32(value: Int): Unit = + p.writeI32(value) + + private def writeI64(value: Long): Unit = + p.writeI64(value) + + private def writeDouble(value: Double): Unit = + p.writeDouble(value) + + private def writeBinary(value: Chunk[Byte]): Unit = + p.writeBinary(ByteBuffer.wrap(value.toArray)) + + private def writeListBegin(ttype: Byte, count: Int): Unit = + p.writeListBegin(new TList(ttype, count)) + + private def writeSetBegin(ttype: Byte, count: Int): Unit = + p.writeSetBegin(new TSet(ttype, count)) + + private def writeMapBegin(keyType: Byte, valueType: Byte, count: Int): Unit = + p.writeMapBegin(new TMap(keyType, valueType, count)) + + private def fail(message: String): Unit = throw new RuntimeException(message) - def getPrimitiveType[A](standardType: StandardType[A]): Byte = + private def writePrimitiveType[A](standardType: StandardType[A], value: A): Unit = + (standardType, value) match { + case (StandardType.UnitType, _) => + case (StandardType.StringType, str: String) => + writeString(str) + case (StandardType.BoolType, b: Boolean) => + writeBool(b) + case (StandardType.ByteType, v: Byte) => + writeByte(v) + case (StandardType.ShortType, v: Short) => + writeI16(v) + case (StandardType.IntType, v: Int) => + writeI32(v) + case (StandardType.LongType, v: Long) => + writeI64(v) + case (StandardType.FloatType, v: Float) => + writeDouble(v.toDouble) + case (StandardType.DoubleType, v: Double) => + writeDouble(v.toDouble) + case (StandardType.BigIntegerType, v: java.math.BigInteger) => + writeBinary(Chunk.fromArray(v.toByteArray)) + case (StandardType.BigDecimalType, v: java.math.BigDecimal) => + val unscaled = v.unscaledValue() + val precision = v.precision() + val scale = v.scale() + writeFieldBegin(Some(1), getPrimitiveType(StandardType.BigIntegerType)) + writePrimitiveType(StandardType.BigIntegerType, unscaled) + writeFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) + writePrimitiveType(StandardType.IntType, precision) + writeFieldBegin(Some(3), getPrimitiveType(StandardType.IntType)) + writePrimitiveType(StandardType.IntType, scale) + writeFieldEnd() + + case (StandardType.BinaryType, bytes: Chunk[Byte]) => + writeBinary(Chunk.fromArray(bytes.toArray)) + case (StandardType.CharType, c: Char) => + writeString(c.toString) + case (StandardType.UUIDType, u: UUID) => + writeString(u.toString) + case (StandardType.DayOfWeekType, v: DayOfWeek) => + writeByte(v.getValue.toByte) + case (StandardType.MonthType, v: Month) => + writeByte(v.getValue.toByte) + case (StandardType.MonthDayType, v: MonthDay) => + writeFieldBegin(Some(1), getPrimitiveType(StandardType.IntType)) + writePrimitiveType(StandardType.IntType, v.getMonthValue) + writeFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) + writePrimitiveType(StandardType.IntType, v.getDayOfMonth) + writeFieldEnd() + + case (StandardType.PeriodType, v: Period) => + writeFieldBegin(Some(1), getPrimitiveType(StandardType.IntType)) + writePrimitiveType(StandardType.IntType, v.getYears) + writeFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) + writePrimitiveType(StandardType.IntType, v.getMonths) + writeFieldBegin(Some(3), getPrimitiveType(StandardType.IntType)) + writePrimitiveType(StandardType.IntType, v.getDays) + writeFieldEnd() + + case (StandardType.YearType, v: Year) => + writeI32(v.getValue) + case (StandardType.YearMonthType, v: YearMonth) => + writeFieldBegin(Some(1), getPrimitiveType(StandardType.IntType)) + writePrimitiveType(StandardType.IntType, v.getYear) + writeFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) + writePrimitiveType(StandardType.IntType, v.getMonthValue) + writeFieldEnd() + case (StandardType.ZoneIdType, v: ZoneId) => + writeString(v.getId) + case (StandardType.ZoneOffsetType, v: ZoneOffset) => + writeI32(v.getTotalSeconds) + case (StandardType.DurationType, v: Duration) => + writeFieldBegin(Some(1), getPrimitiveType(StandardType.LongType)) + writePrimitiveType(StandardType.LongType, v.getSeconds) + writeFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) + writePrimitiveType(StandardType.IntType, v.getNano) + writeFieldEnd() + + case (StandardType.InstantType(formatter), v: Instant) => + writeString(formatter.format(v)) + case (StandardType.LocalDateType(formatter), v: LocalDate) => + writeString(formatter.format(v)) + case (StandardType.LocalTimeType(formatter), v: LocalTime) => + writeString(formatter.format(v)) + case (StandardType.LocalDateTimeType(formatter), v: LocalDateTime) => + writeString(formatter.format(v)) + case (StandardType.OffsetTimeType(formatter), v: OffsetTime) => + writeString(formatter.format(v)) + case (StandardType.OffsetDateTimeType(formatter), v: OffsetDateTime) => + writeString(formatter.format(v)) + case (StandardType.ZonedDateTimeType(formatter), v: ZonedDateTime) => + writeString(formatter.format(v)) + case (_, _) => + fail(s"No encoder for $standardType") + } + } + + object Encoder { + final case class Context(fieldNumber: Option[Short]) + + private def getPrimitiveType[A](standardType: StandardType[A]): Byte = standardType match { case StandardType.UnitType => TType.VOID case StandardType.StringType => @@ -224,996 +412,414 @@ object ThriftCodec extends BinaryCodec { case _ => TType.VOID } - def writePrimitiveType[A](standardType: StandardType[A], value: A): Unit = - (standardType, value) match { - case (StandardType.UnitType, _) => () - case (StandardType.StringType, str: String) => - p.writeString(str) - case (StandardType.BoolType, b: Boolean) => - p.writeBool(b) - case (StandardType.ByteType, v: Byte) => - p.writeByte(v) - case (StandardType.ShortType, v: Short) => - p.writeI16(v) - case (StandardType.IntType, v: Int) => - p.writeI32(v) - case (StandardType.LongType, v: Long) => - p.writeI64(v) - case (StandardType.FloatType, v: Float) => - p.writeDouble(v.toDouble) - case (StandardType.DoubleType, v: Double) => - p.writeDouble(v.toDouble) - case (StandardType.BigIntegerType, v: java.math.BigInteger) => - p.writeBinary(ByteBuffer.wrap(v.toByteArray)) - case (StandardType.BigDecimalType, v: java.math.BigDecimal) => - val unscaled = v.unscaledValue() - val precision = v.precision() - val scale = v.scale() - encodeRecord( - None, - bigDecimalStructure, - ListMap("unscaled" -> unscaled, "precision" -> precision, "scale" -> scale) - ) - case (StandardType.BinaryType, bytes: Chunk[Byte]) => - p.writeBinary(ByteBuffer.wrap(bytes.toArray)) - case (StandardType.CharType, c: Char) => - p.writeString(c.toString) - case (StandardType.UUIDType, u: UUID) => - p.writeString(u.toString) - case (StandardType.DayOfWeekType, v: DayOfWeek) => - p.writeByte(v.getValue.toByte) - case (StandardType.MonthType, v: Month) => - p.writeByte(v.getValue.toByte) - case (StandardType.MonthDayType, v: MonthDay) => - encodeRecord(None, monthDayStructure, ListMap("month" -> v.getMonthValue, "day" -> v.getDayOfMonth)) - case (StandardType.PeriodType, v: Period) => - encodeRecord( - None, - periodStructure, - ListMap("years" -> v.getYears, "months" -> v.getMonths, "days" -> v.getDays) - ) - case (StandardType.YearType, v: Year) => - p.writeI32(v.getValue) - case (StandardType.YearMonthType, v: YearMonth) => - encodeRecord(None, yearMonthStructure, ListMap("year" -> v.getYear, "month" -> v.getMonthValue)) - case (StandardType.ZoneIdType, v: ZoneId) => - p.writeString(v.getId) - case (StandardType.ZoneOffsetType, v: ZoneOffset) => - p.writeI32(v.getTotalSeconds) - case (StandardType.DurationType, v: Duration) => - encodeRecord(None, durationStructure, ListMap("seconds" -> v.getSeconds, "nanos" -> v.getNano)) - case (StandardType.InstantType(formatter), v: Instant) => - p.writeString(formatter.format(v)) - case (StandardType.LocalDateType(formatter), v: LocalDate) => - p.writeString(formatter.format(v)) - case (StandardType.LocalTimeType(formatter), v: LocalTime) => - p.writeString(formatter.format(v)) - case (StandardType.LocalDateTimeType(formatter), v: LocalDateTime) => - p.writeString(formatter.format(v)) - case (StandardType.OffsetTimeType(formatter), v: OffsetTime) => - p.writeString(formatter.format(v)) - case (StandardType.OffsetDateTimeType(formatter), v: OffsetDateTime) => - p.writeString(formatter.format(v)) - case (StandardType.ZonedDateTimeType(formatter), v: ZonedDateTime) => - p.writeString(formatter.format(v)) - case (_, _) => - throw new NotImplementedError(s"No encoder for $standardType") - } - - def writeFieldBegin(fieldNumber: Option[Short], ttype: Byte): Unit = - fieldNumber.foreach( - num => - p.writeFieldBegin( - new TField("", ttype, num) - ) - ) - - def encodePrimitive[A](fieldNumber: Option[Short], standardType: StandardType[A], value: A): Unit = { - writeFieldBegin(fieldNumber, getPrimitiveType(standardType)) - writePrimitiveType(standardType, value) - } - - def encodeSequence[A](fieldNumber: Option[Short], schema: Schema[A], v: Chunk[A]): Unit = { - writeFieldBegin(fieldNumber, TType.LIST) - p.writeListBegin(new org.apache.thrift.protocol.TList(getType(schema), v.size)) - v.foreach(encodeValue(None, schema, _)) - } - - def encodeMap[K, V](fieldNumber: Option[Short], schema: Schema.Map[K, V], v: Map[K, V]): Unit = { - writeFieldBegin(fieldNumber, TType.MAP) - p.writeMapBegin( - new org.apache.thrift.protocol.TMap(getType(schema.keySchema), getType(schema.valueSchema), v.size) - ) - v.foreach { - case (k, v) => - encodeValue(None, schema.keySchema, k) - encodeValue(None, schema.valueSchema, v) - } - } - - def encodeSet[A](fieldNumber: Option[Short], schema: Schema[A], v: scala.collection.immutable.Set[A]): Unit = { - writeFieldBegin(fieldNumber, TType.SET) - p.writeSetBegin(new org.apache.thrift.protocol.TSet(getType(schema), v.size)) - v.foreach(encodeValue(None, schema, _)) - } - - def encodeOptional[A](fieldNumber: Option[Short], schema: Schema[A], v: Option[A]): Unit = { - writeFieldBegin(fieldNumber, TType.STRUCT) - v match { - case None => - encodeValue(Some(1), Schema.primitive(StandardType.UnitType), ()) - case Some(value) => - encodeValue(Some(2), schema, value) - } - p.writeFieldStop() - } - - //scalafmt: { maxColumn = 400, optIn.configStyleArguments = false } - def encodeValue[A](fieldNumber: Option[Short], schema: Schema[A], value: A): Unit = - (schema, value) match { - case (Schema.GenericRecord(_, structure, _), v: Map[String, _]) => encodeRecord(fieldNumber, structure.toChunk, v) - case (Schema.Sequence(element, _, g, _, _), v) => encodeSequence(fieldNumber, element, g(v)) - case (mapSchema: Schema.Map[_, _], map: Map[_, _]) => encodeMap(fieldNumber, mapSchema.asInstanceOf[Schema.Map[Any, Any]], map.asInstanceOf[scala.collection.immutable.Map[Any, Any]]) - case (setSchema: Schema.Set[_], set: Set[_]) => encodeSet(fieldNumber, setSchema.asInstanceOf[Schema.Set[Any]].elementSchema, set.asInstanceOf[scala.collection.immutable.Set[Any]]) - case (Schema.Transform(schema, _, g, _, _), _) => g(value).foreach(encodeValue(fieldNumber, schema, _)) - case (Schema.Primitive(standardType, _), v) => encodePrimitive(fieldNumber, standardType, v) - case (Schema.Tuple2(left, right, _), v @ (_, _)) => encodeTuple(fieldNumber, left, right, v) - case (optSchema: Schema.Optional[_], v: Option[_]) => encodeOptional(fieldNumber, optSchema.asInstanceOf[Schema.Optional[Any]].schema, v.asInstanceOf[Option[Any]]) - case (eitherSchema: Schema.Either[_, _], v: scala.util.Either[_, _]) => encodeEither(fieldNumber, eitherSchema.asInstanceOf[Schema.Either[Any, Any]].left, eitherSchema.asInstanceOf[Schema.Either[Any, Any]].right, v.asInstanceOf[scala.util.Either[Any, Any]]) - case (lzy @ Schema.Lazy(_), v) => encodeValue(fieldNumber, lzy.schema, v) - // case (Schema.Meta(ast, _), _) => encodeValue(fieldNumber, Schema[MetaSchema], ast) - case ProductEncoder(encode) => - writeFieldBegin(fieldNumber, TType.STRUCT) - encode() - case (Schema.Enum1(_, c, _), v) => encodeEnum(fieldNumber, v, c) - case (Schema.Enum2(_, c1, c2, _), v) => encodeEnum(fieldNumber, v, c1, c2) - case (Schema.Enum3(_, c1, c2, c3, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3) - case (Schema.Enum4(_, c1, c2, c3, c4, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4) - case (Schema.Enum5(_, c1, c2, c3, c4, c5, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5) - case (Schema.Enum6(_, c1, c2, c3, c4, c5, c6, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6) - case (Schema.Enum7(_, c1, c2, c3, c4, c5, c6, c7, _), v) => encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7) - case (Schema.Enum8(_, c1, c2, c3, c4, c5, c6, c7, c8, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8) - case (Schema.Enum9(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9) - case (Schema.Enum10(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) - case (Schema.Enum11(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) - case (Schema.Enum12(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) - case (Schema.Enum13(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) - case (Schema.Enum14(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14) - case (Schema.Enum15(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15) - case (Schema.Enum16(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16) - case (Schema.Enum17(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17) - case (Schema.Enum18(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18) - case (Schema.Enum19(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19) - case (Schema.Enum20(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20) - case (Schema.Enum21(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21) - case (Schema.Enum22(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22, _), v) => - encodeEnum(fieldNumber, v, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22) - case (Schema.EnumN(_, cs, _), v) => encodeEnum(fieldNumber, v, cs.toSeq: _*) - case (Schema.Dynamic(_), v) => encodeDynamic(fieldNumber, DynamicValueSchema.schema, v) - case (_, _) => () - } - - private def encodeDynamic(fieldNumber: Option[Short], schema: Schema[DynamicValue], v: DynamicValue): Unit = - encodeValue(fieldNumber, schema, v) - - private def encodeEnum[Z](fieldNumber: Option[Short], value: Z, cases: Schema.Case[Z, _]*): Unit = { - writeFieldBegin(fieldNumber, TType.STRUCT) - val fieldIndex = cases.indexWhere(c => c.deconstructOption(value).isDefined) - if (fieldIndex >= 0) { - val subtypeCase = cases(fieldIndex) - encodeValue(Some((fieldIndex + 1).shortValue), subtypeCase.schema.asInstanceOf[Schema[Any]], subtypeCase.deconstruct(value)) - } - p.writeFieldStop() - } - - private def encodeEither[A, B](fieldNumber: Option[Short], left: Schema[A], right: Schema[B], either: scala.util.Either[A, B]): Unit = { - writeFieldBegin(fieldNumber, TType.STRUCT) - either match { - case Left(value) => encodeValue(Some(1), left, value) - case Right(value) => encodeValue(Some(2), right, value) - } - } - - def tupleSchema[A, B](first: Schema[A], second: Schema[B]): Seq[Schema.Field[(A, B), _]] = - Seq(Schema.Field("first", first, get = _._1, set = (ab: (A, B), a: A) => (a, ab._2)), Schema.Field("second", second, get = _._2, set = (a: (A, B), b: B) => (a._1, b))) - - private def encodeTuple[A, B](fieldNumber: Option[Short], left: Schema[A], right: Schema[B], tuple: (A, B)): Unit = - encodeRecord(fieldNumber, tupleSchema(left, right), ListMap[String, Any]("first" -> tuple._1, "second" -> tuple._2)) - - private def writeStructure(fields: Seq[(Schema.Field[_, _], Any)]): Unit = { - fields.zipWithIndex.foreach { - case ((fieldSchema: Schema.Field[_, Any], value), fieldNumber) => - encodeValue(Some((fieldNumber + 1).shortValue), fieldSchema.schema, value) - } - p.writeFieldStop() - } - - //scalafmt: { maxColumn = 400, optIn.configStyleArguments = false } - private[codec] object ProductEncoder { - - def unapply[A](schemaAndValue: (Schema[A], A)): Option[() => Unit] = schemaAndValue match { - case (Schema.CaseClass1(_, f, _, _), v) => Some(encodeCaseClass(v, f)) - case (Schema.CaseClass2(_, f1, f2, _, _), v) => Some(encodeCaseClass(v, f1, f2)) - case (Schema.CaseClass3(_, f1, f2, f3, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3)) - case (Schema.CaseClass4(_, f1, f2, f3, f4, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4)) - case (Schema.CaseClass5(_, f1, f2, f3, f4, f5, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5)) - case (Schema.CaseClass6(_, f1, f2, f3, f4, f5, f6, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6)) - case (Schema.CaseClass7(_, f1, f2, f3, f4, f5, f6, f7, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7)) - case (Schema.CaseClass8(_, f1, f2, f3, f4, f5, f6, f7, f8, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8)) - case (Schema.CaseClass9(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9)) - case (Schema.CaseClass10(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10)) - case (Schema.CaseClass11(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11)) - case (Schema.CaseClass12(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12)) - case (Schema.CaseClass13(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13)) - case (Schema.CaseClass14(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14)) - case (Schema.CaseClass15(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15)) - case (Schema.CaseClass16(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16)) - case (Schema.CaseClass17(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17)) - case (Schema.CaseClass18(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18)) - case (Schema.CaseClass19(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19)) - case (Schema.CaseClass20(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20)) - case (Schema.CaseClass21(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21)) - case (Schema.CaseClass22(_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, f22, _, _), v) => - Some(encodeCaseClass(v, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, f22)) - case _ => None - } - - private def encodeCaseClass[Z](value: Z, fields: (Schema.Field[Z, _])*): () => Unit = () => writeStructure(fields.map { case field => (field, field.get(value)) }) - - object OptionalSchema { - - def unapply(schema: Schema[Any]): Option[Schema[Any]] = - if (schema.isInstanceOf[Schema.Optional[_]]) - Some(schema.asInstanceOf[Schema.Optional[Any]].schema) - else - None - } - - } - - def encodeRecord(fieldNumber: Option[Short], structure: Seq[Schema.Field[_, _]], data: ListMap[String, _]): Unit = { - writeFieldBegin(fieldNumber, TType.STRUCT) - writeStructure(structure.map(schema => (schema, data(schema.name)))) + @tailrec + final private def getType[A](schema: Schema[A]): Byte = schema match { + case _: Schema.Record[A] => TType.STRUCT + case Schema.Sequence(_, _, _, _, _) => TType.LIST + case Schema.Map(_, _, _) => TType.MAP + case Schema.Set(_, _) => TType.SET + case Schema.Transform(schema, _, _, _, _) => getType(schema) + case Schema.Primitive(standardType, _) => getPrimitiveType(standardType) + case Schema.Tuple2(_, _, _) => TType.STRUCT + case Schema.Optional(schema, _) => getType(schema) + case Schema.Either(_, _, _) => TType.STRUCT + case Schema.Lazy(lzy) => getType(lzy()) + case _: Schema.Enum[A] => TType.STRUCT + case _ => TType.VOID } } - final class ThriftDecoder(chunk: Chunk[Byte]) { + type Path = Chunk[String] + type PrimitiveDecoder[A] = Path => A - type Path = Chunk[String] + final case class DecoderContext(path: Path, expectedCount: Option[Int]) - type Result[A] = scala.util.Either[DecodeError, A] - type PrimitiveResult[A] = Path => Result[A] + class Decoder(chunk: Chunk[Byte]) extends MutableSchemaBasedValueBuilder[Any, DecoderContext] { val read = new ChunkTransport.Read(chunk) val p = new TBinaryProtocol(read) - def succeed[A](a: => A): Result[A] = Right(a) - - def fail(path: Path, failure: String): Result[Nothing] = Left(MalformedFieldWithPath(path, failure)) - - def decodePrimitive[A](f: TProtocol => A, name: String): PrimitiveResult[A] = + def decodePrimitive[A](f: TProtocol => A, name: String): PrimitiveDecoder[A] = path => - Try { + try { f(p) - }.toEither.left.map(_ => MalformedFieldWithPath(path, s"Unable to decode $name")) + } catch { + case NonFatal(_) => throw MalformedFieldWithPath(path, s"Unable to decode $name") + } - def decodeString: PrimitiveResult[String] = + def decodeString: PrimitiveDecoder[String] = decodePrimitive(_.readString(), "String") - def decodeByte: PrimitiveResult[Byte] = + def decodeUUID: PrimitiveDecoder[UUID] = + decodePrimitive(protocol => UUID.fromString(protocol.readString()), "UUID") + + def decodeByte: PrimitiveDecoder[Byte] = decodePrimitive(_.readByte(), "Byte") - def decodeBoolean: PrimitiveResult[Boolean] = + def decodeBoolean: PrimitiveDecoder[Boolean] = decodePrimitive(_.readBool(), "Boolean") - def decodeShort: PrimitiveResult[Short] = + def decodeShort: PrimitiveDecoder[Short] = decodePrimitive(_.readI16(), "Short") - def decodeInt: PrimitiveResult[Int] = + def decodeInt: PrimitiveDecoder[Int] = decodePrimitive(_.readI32(), "Int") - def decodeLong: PrimitiveResult[Long] = + def decodeLong: PrimitiveDecoder[Long] = decodePrimitive(_.readI64(), "Long") - def decodeFloat: PrimitiveResult[Float] = + def decodeFloat: PrimitiveDecoder[Float] = decodePrimitive(_.readDouble().toFloat, "Float") - def decodeDouble: PrimitiveResult[Double] = + def decodeDouble: PrimitiveDecoder[Double] = decodePrimitive(_.readDouble(), "Double") - def decodeBigInteger: PrimitiveResult[java.math.BigInteger] = + def decodeBigInteger: PrimitiveDecoder[java.math.BigInteger] = decodePrimitive(p => new java.math.BigInteger(p.readBinary().array()), "BigInteger") - def decodeBinary: PrimitiveResult[Chunk[Byte]] = + def decodeBinary: PrimitiveDecoder[Chunk[Byte]] = decodePrimitive(p => Chunk.fromByteBuffer(p.readBinary()), "Binary") - def decode[A](path: Path, schema: Schema[A]): Result[A] = - schema match { - case Schema.GenericRecord(_, structure, _) => { - val fields = structure.toChunk - decodeRecord(path, fields).map(_.map { case (index, value) => (fields(index - 1).name, value) }) - } - case seqSchema @ Schema.Sequence(_, _, _, _, _) => decodeSequence(path, seqSchema) - case mapSchema @ Schema.Map(_, _, _) => decodeMap(path, mapSchema) - case setSchema @ Schema.Set(_, _) => decodeSet(path, setSchema) - case Schema.Transform(schema, f, _, _, _) => transformDecoder(path, schema, f) - case Schema.Primitive(standardType, _) => primitiveDecoder(path, standardType) - case Schema.Tuple2(left, right, _) => tupleDecoder(path, left, right) - case optionalSchema @ Schema.Optional(_, _) => optionalDecoder(path, optionalSchema) - case Schema.Fail(message, _) => fail(path, message) - case Schema.Either(left, right, _) => eitherDecoder(path, left, right) - case lzy @ Schema.Lazy(_) => decode(path, lzy.schema) - //case Schema.Meta(_, _) => decode(path, Schema[MetaSchema]).map(_.toSchema) - case ProductDecoder(decoder) => decoder(path) - case Schema.Enum1(_, c, _) => enumDecoder(path, c) - case Schema.Enum2(_, c1, c2, _) => enumDecoder(path, c1, c2) - case Schema.Enum3(_, c1, c2, c3, _) => enumDecoder(path, c1, c2, c3) - case Schema.Enum4(_, c1, c2, c3, c4, _) => enumDecoder(path, c1, c2, c3, c4) - case Schema.Enum5(_, c1, c2, c3, c4, c5, _) => enumDecoder(path, c1, c2, c3, c4, c5) - case Schema.Enum6(_, c1, c2, c3, c4, c5, c6, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6) - case Schema.Enum7(_, c1, c2, c3, c4, c5, c6, c7, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7) - case Schema.Enum8(_, c1, c2, c3, c4, c5, c6, c7, c8, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8) - case Schema.Enum9(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9) - case Schema.Enum10(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) - case Schema.Enum11(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11) - case Schema.Enum12(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12) - case Schema.Enum13(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13) - case Schema.Enum14(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14) - case Schema.Enum15(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15) - case Schema.Enum16(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16) - case Schema.Enum17(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17) - case Schema.Enum18(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18) - case Schema.Enum19(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19) - case Schema.Enum20(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20) - case Schema.Enum21(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21) - case Schema.Enum22(_, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22, _) => enumDecoder(path, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22) - case Schema.EnumN(_, cs, _) => enumDecoder(path, cs.toSeq: _*) - case Schema.Dynamic(_) => dynamicDecoder(path, DynamicValueSchema.schema) - case _ => fail(path, s"Unknown schema ${schema.getClass.getName}") - } + override protected def createPrimitive(context: DecoderContext, typ: StandardType[_]): Any = + typ match { + case StandardType.UnitType => () + case StandardType.StringType => decodeString(context.path) + case StandardType.BoolType => decodeBoolean(context.path) + case StandardType.ByteType => decodeByte(context.path) + case StandardType.ShortType => decodeShort(context.path) + case StandardType.IntType => decodeInt(context.path) + case StandardType.LongType => decodeLong(context.path) + case StandardType.FloatType => decodeFloat(context.path) + case StandardType.DoubleType => decodeDouble(context.path) + case StandardType.BigIntegerType => decodeBigInteger(context.path) + case StandardType.BigDecimalType => + p.readFieldBegin() + val unscaled = decodeBigInteger(context.path) + p.readFieldBegin() + val precision = decodeInt(context.path) + p.readFieldBegin() + val scale = decodeInt(context.path) + p.readFieldBegin() + new java.math.BigDecimal(unscaled, scale, new java.math.MathContext(precision)) + + case StandardType.BinaryType => decodeBinary(context.path) + case StandardType.CharType => + val decoded = decodeString(context.path) - private def dynamicDecoder(path: Path, schema: Schema[DynamicValue]): Result[DynamicValue] = - decode(path, schema) - - private def optionalDecoder[A](path: Path, schema: Schema.Optional[A]): Result[Option[A]] = - Try { - val readField = p.readFieldBegin() - val res = readField.id match { - case 1 => succeed(None) - case 2 => decode(path :+ "Some", schema.schema).map(Some(_)) - case _ => - fail(path, s"Error decoding optional, wrong field id ${readField.id}") - } - p.readFieldBegin() - res.asInstanceOf[Result[Option[A]]] - }.fold(err => fail(path, s"Error decoding optional ${err.getMessage}"), identity) - - private def enumDecoder[Z, A](path: Path, cases: Schema.Case[Z, _]*): Result[Z] = - Try { - val readField = p.readFieldBegin() - if (readField.id > cases.length) - fail(path, s"Error decoding enum with cases ${cases.map(_.id).mkString(", ")}, enum id out of range: ${readField.id}") - else { - val subtypeCase = cases(readField.id - 1) - val res = decode(path :+ s"[case:${subtypeCase.id}]", subtypeCase.schema) - res.foreach { _ => - p.readFieldBegin() + if (decoded.length == 1) + decoded.charAt(0) + else { + fail(context, s"""Expected character, found string "$decoded"""") } - res.asInstanceOf[Result[Z]] - } - }.fold(err => fail(path, s"Error decoding enum with cases ${cases.map(_.id).mkString(", ")}: ${err.getMessage}"), identity) - - private def eitherDecoder[A, B](path: Path, left: Schema[A], right: Schema[B]): Result[scala.util.Either[A, B]] = { - val readField = p.readFieldBegin() - readField.id match { - case 1 => decode(path :+ "either:left", left).map(Left(_)) - case 2 => decode(path :+ "either:right", right).map(Right(_)) - case _ => fail(path, "Failed to decode either.") - } - } - - private def tupleDecoder[A, B](path: Path, left: Schema[A], right: Schema[B]): Result[(A, B)] = - structDecoder(Seq(left, right), path) - .flatMap( - record => - (record.get(1), record.get(2)) match { - case (Some(first), Some(second)) => Right((first.asInstanceOf[A], second.asInstanceOf[B])) - case _ => fail(path, "Error while decoding tuple.") - } - ) - - private def transformDecoder[A, B](path: Path, schema: Schema[B], f: B => scala.util.Either[String, A]): Result[A] = - decode(path, schema).flatMap(a => f(a).left.map(msg => MalformedFieldWithPath(path, msg))) - private def primitiveDecoder[A](path: Path, standardType: StandardType[A]): Result[A] = - standardType match { - case StandardType.UnitType => Right(()) - case StandardType.StringType => decodeString(path) - case StandardType.BoolType => decodeBoolean(path) - case StandardType.ByteType => decodeByte(path) - case StandardType.ShortType => decodeShort(path) - case StandardType.IntType => decodeInt(path) - case StandardType.LongType => decodeLong(path) - case StandardType.FloatType => decodeFloat(path) - case StandardType.DoubleType => decodeDouble(path) - case StandardType.BigIntegerType => decodeBigInteger(path) - case StandardType.BigDecimalType => - decodeRecord(path, bigDecimalStructure).flatMap { data => - val opt = for { - unscaled <- data.get(1).asInstanceOf[Option[java.math.BigInteger]] - precision <- data.get(2).asInstanceOf[Option[Int]] - scale <- data.get(3).asInstanceOf[Option[Int]] - ctx = new java.math.MathContext(precision) - } yield new java.math.BigDecimal(unscaled, scale, ctx) - - opt match { - case Some(value) => Right(value) - case None => fail(path, s"Invalid big decimal record $data") - } - } - case StandardType.BinaryType => decodeBinary(path) - case StandardType.CharType => - decodeString(path).flatMap( - decoded => - if (decoded.size == 1) - succeed(decoded.charAt(0)) - else { - fail(path, s"""Expected character, found string "$decoded"""") - } - ) case StandardType.UUIDType => - decodeString(path).flatMap { uuid => - try succeed(UUID.fromString(uuid)) - catch { - case NonFatal(_) => fail(path, "Invalid UUID string") - } - } + decodeUUID(context.path) case StandardType.DayOfWeekType => - decodeByte(path).map(_.toInt).map(DayOfWeek.of) + DayOfWeek.of(decodeByte(context.path).toInt) case StandardType.MonthType => - decodeByte(path).map(_.toInt).map(Month.of) + Month.of(decodeByte(context.path).toInt) case StandardType.MonthDayType => - decodeRecord(path, monthDayStructure) - .map(data => MonthDay.of(data.getOrElse(1, 0).asInstanceOf[Int], data.getOrElse(2, 0).asInstanceOf[Int])) + p.readFieldBegin() + val month = decodeInt(context.path) + p.readFieldBegin() + val day = decodeInt(context.path) + p.readFieldBegin() + MonthDay.of(month, day) + case StandardType.PeriodType => - decodeRecord(path, periodStructure) - .map(data => Period.of(data.getOrElse(1, 0).asInstanceOf[Int], data.getOrElse(2, 0).asInstanceOf[Int], data.getOrElse(3, 0).asInstanceOf[Int])) + p.readFieldBegin() + val year = decodeInt(context.path) + p.readFieldBegin() + val month = decodeInt(context.path) + p.readFieldBegin() + val day = decodeInt(context.path) + p.readFieldBegin() + Period.of(year, month, day) + case StandardType.YearType => - decodeInt(path).map(_.intValue).map(Year.of) + Year.of(decodeInt(context.path).intValue) case StandardType.YearMonthType => - decodeRecord(path, yearMonthStructure) - .map(data => YearMonth.of(data.getOrElse(1, 0).asInstanceOf[Int], data.getOrElse(2, 0).asInstanceOf[Int])) - case StandardType.ZoneIdType => decodeString(path).map(ZoneId.of) + p.readFieldBegin() + val year = decodeInt(context.path) + p.readFieldBegin() + val month = decodeInt(context.path) + p.readFieldBegin() + YearMonth.of(year, month) + + case StandardType.ZoneIdType => + ZoneId.of(decodeString(context.path)) + case StandardType.ZoneOffsetType => - decodeInt(path) - .map(_.intValue) - .map(ZoneOffset.ofTotalSeconds) + ZoneOffset.ofTotalSeconds(decodeInt(context.path).intValue) case StandardType.DurationType => - decodeRecord(path, durationStructure) - .map(data => Duration.ofSeconds(data.getOrElse(1, 0L).asInstanceOf[Long], data.getOrElse(2, 0L).asInstanceOf[Int].toLong)) + p.readFieldBegin() + val seconds = decodeLong(context.path) + p.readFieldBegin() + val nano = decodeInt(context.path) + p.readFieldBegin() + Duration.ofSeconds(seconds, nano.toLong) + case StandardType.InstantType(formatter) => - decodeString(path).map(v => Instant.from(formatter.parse(v))) + Instant.from(formatter.parse(decodeString(context.path))) case StandardType.LocalDateType(formatter) => - decodeString(path).map(LocalDate.parse(_, formatter)) + LocalDate.parse(decodeString(context.path), formatter) case StandardType.LocalTimeType(formatter) => - decodeString(path).map(LocalTime.parse(_, formatter)) + LocalTime.parse(decodeString(context.path), formatter) case StandardType.LocalDateTimeType(formatter) => - decodeString(path).map(LocalDateTime.parse(_, formatter)) + LocalDateTime.parse(decodeString(context.path), formatter) case StandardType.OffsetTimeType(formatter) => - decodeString(path).map(OffsetTime.parse(_, formatter)) + OffsetTime.parse(decodeString(context.path), formatter) case StandardType.OffsetDateTimeType(formatter) => - decodeString(path).map(OffsetDateTime.parse(_, formatter)) + OffsetDateTime.parse(decodeString(context.path), formatter) case StandardType.ZonedDateTimeType(formatter) => - decodeString(path).map(ZonedDateTime.parse(_, formatter)) - case _ => fail(path, s"Unsupported primitive type $standardType") + ZonedDateTime.parse(decodeString(context.path), formatter) + case _ => fail(context, s"Unsupported primitive type $typ") } - private def emptyValue[A](schema: Schema[A]): Option[A] = schema match { - case Schema.Lazy(s) => emptyValue(s()) - case Schema.Optional(_, _) => Some(None) - case Schema.Sequence(_, fromChunk, _, _, _) => Some(fromChunk(Chunk.empty)) - case Schema.Primitive(StandardType.UnitType, _) => Some(()) - case _ => None + override protected def startCreatingRecord(context: DecoderContext, record: Schema.Record[_]): DecoderContext = + context + + override protected def startReadingField( + context: DecoderContext, + record: Schema.Record[_], + index: Int + ): Option[(DecoderContext, Int)] = { + val tfield = p.readFieldBegin() + if (tfield.`type` == TType.STOP) None + else Some((context.copy(path = context.path :+ s"fieldId:${tfield.id}"), tfield.id - 1)) } - private def decodeRecord[Z](path: Path, fields: Seq[Schema.Field[Z, _]]): Result[ListMap[Short, _]] = - structDecoder(fields.map(_.schema), path) - - def structDecoder(fields: Seq[Schema[_]], path: Path): Result[ListMap[Short, Any]] = { - val fieldSchemas = fields.zipWithIndex.map { case (schema, idx) => (idx + 1) -> schema }.toMap[Int, Schema[_]] - - @tailrec - def readFields(m: ListMap[Short, Any]): Result[ListMap[Short, Any]] = - Try { p.readFieldBegin() } match { - case Failure(err) => fail(path, s"Error reading field begin: ${err.getMessage}") - case Success(readField) => { - if (readField.`type` == TType.STOP) - succeed(m) - else { - val actualPath = path :+ s"fieldId:${readField.id}" - fieldSchemas.get(readField.id.toInt) match { - case Some(fieldSchema) => - decode(actualPath, fieldSchema) match { - case Left(err) => Left(err) - case Right(value) => readFields(m.updated(readField.id, value)) - } - case None => - fail(actualPath, s"Could not find schema for field ID ${readField.id}") - } + override protected def createRecord( + context: DecoderContext, + record: Schema.Record[_], + values: Chunk[(Int, Any)] + ): Any = { + val valuesMap = values.toMap + val allValues = + record.fields.zipWithIndex.map { + case (field, idx) => + valuesMap.get(idx) match { + case Some(value) => value + case None => + emptyValue(field.schema) match { + case Some(value) => + value + case None => + val optionalFieldAnnotation = field.annotations.collectFirst({ case a: optionalField => a }) + if (optionalFieldAnnotation.isDefined) { + field.schema.defaultValue.toOption.get + } else { + fail(context.copy(path = context.path :+ field.name), s"Missing value") + } + } } - } } + Unsafe.unsafe { implicit u => + record.construct(allValues) match { + case Left(message) => fail(context, message) + case Right(value) => value + } + } + } + + override protected def startCreatingEnum( + context: DecoderContext, + cases: Chunk[Schema.Case[_, _]] + ): (DecoderContext, Int) = { + val readField = p.readFieldBegin() + val consIdx = readField.id - 1 + val subtypeCase = cases(consIdx) + (context.copy(path = context.path :+ s"[case:${subtypeCase.id}]"), consIdx) + } - readFields(ListMap.empty) + override protected def createEnum( + context: DecoderContext, + cases: Chunk[Schema.Case[_, _]], + index: Int, + value: Any + ): Any = { + p.readFieldBegin() + value } - def decodeSequence[Col, Elem](path: Path, schema: Schema.Sequence[Col, Elem, _]): Result[Col] = { - @tailrec - def decodeElements(n: Int, cb: ChunkBuilder[Elem]): Result[Chunk[Elem]] = - if (n > 0) - decode(path, schema.elementSchema) match { - case Right(elem) => decodeElements(n - 1, cb += (elem)) - case Left(failure) => fail(path, s"Error decoding Sequence element: $failure") - } else - succeed(cb.result()) - - Try { p.readListBegin() }.fold(_ => fail(path, "Can not decode Sequence begin"), begin => decodeElements(begin.size, ChunkBuilder.make[Elem]()).map(schema.fromChunk)) + override protected def startCreatingSequence( + context: DecoderContext, + schema: Schema.Sequence[_, _, _] + ): Option[DecoderContext] = { + val begin = p.readListBegin() + if (begin.size == 0) None + else + Some(context.copy(expectedCount = Some(begin.size))) } - def decodeMap[K, V](path: Path, schema: Schema.Map[K, V]): Result[scala.collection.immutable.Map[K, V]] = { - @tailrec - def decodeElements(n: Int, m: scala.collection.mutable.Map[K, V]): Result[scala.collection.immutable.Map[K, V]] = - if (n > 0) - (decode(path, schema.keySchema), decode(path, schema.valueSchema)) match { - case (Right(key), Right(value)) => decodeElements(n - 1, m += ((key, value))) - case (l, r) => - val key = l.fold(_.message, _.toString) - val value = r.fold(_.message, _.toString) - fail(path, s"Error decoding Map element (key: $key; value: $value)") - } else - succeed(m.toMap) - - Try { - p.readMapBegin() - }.fold(_ => fail(path, "Can not decode Map begin"), begin => decodeElements(begin.size, scala.collection.mutable.Map.empty[K, V])) + override protected def startCreatingOneSequenceElement( + context: DecoderContext, + schema: Schema.Sequence[_, _, _] + ): DecoderContext = + context + + override protected def finishedCreatingOneSequenceElement( + context: DecoderContext, + schema: Schema.Sequence[_, _, _], + index: Int + ): Boolean = + context.expectedCount.map(_ - (index + 1)).exists(_ > 0) + + override protected def createSequence( + context: DecoderContext, + schema: Schema.Sequence[_, _, _], + values: Chunk[Any] + ): Any = + schema.fromChunk.asInstanceOf[Chunk[Any] => Any](values) + + override protected def startCreatingDictionary( + context: DecoderContext, + schema: Schema.Map[_, _] + ): Option[DecoderContext] = { + val begin = p.readMapBegin() + if (begin.size == 0) None + else + Some(context.copy(expectedCount = Some(begin.size))) } - def decodeSet[A](path: Path, schema: Schema.Set[A]): Result[scala.collection.immutable.Set[A]] = { - @tailrec - def decodeElements(n: Int, cb: ChunkBuilder[A]): Result[Chunk[A]] = - if (n > 0) - decode(path, schema.elementSchema) match { - case Right(elem) => decodeElements(n - 1, cb += (elem)) - case Left(_) => fail(path, "Error decoding Set element") - } else - succeed(cb.result()) - - Try { p.readSetBegin() }.fold(_ => fail(path, "Can not decode Set begin"), begin => decodeElements(begin.size, ChunkBuilder.make[A]()).map(_.toSet)) + override protected def startCreatingOneDictionaryElement( + context: DecoderContext, + schema: Schema.Map[_, _] + ): DecoderContext = + context + + override protected def startCreatingOneDictionaryValue( + context: DecoderContext, + schema: Schema.Map[_, _] + ): DecoderContext = + context + + override protected def finishedCreatingOneDictionaryElement( + context: DecoderContext, + schema: Schema.Map[_, _], + index: Int + ): Boolean = + context.expectedCount.map(_ - (index + 1)).exists(_ > 0) + + override protected def createDictionary( + context: DecoderContext, + schema: Schema.Map[_, _], + values: Chunk[(Any, Any)] + ): Any = + values.toMap + + override protected def startCreatingSet(context: DecoderContext, schema: Schema.Set[_]): Option[DecoderContext] = { + val begin = p.readSetBegin() + if (begin.size == 0) None + else Some(context.copy(expectedCount = Some(begin.size))) } - private[codec] object ProductDecoder { - - def unapply[A](schema: Schema[A]): Option[Path => Result[A]] = schema match { - case s: Schema.CaseClass1[_, A] => Some(caseClass1Decoder(s)) - case s: Schema.CaseClass2[_, _, A] => Some(caseClass2Decoder(s)) - case s: Schema.CaseClass3[_, _, _, A] => Some(caseClass3Decoder(s)) - case s: Schema.CaseClass4[_, _, _, _, A] => Some(caseClass4Decoder(s)) - case s: Schema.CaseClass5[_, _, _, _, _, A] => Some(caseClass5Decoder(s)) - case s: Schema.CaseClass6[_, _, _, _, _, _, A] => Some(caseClass6Decoder(s)) - case s: Schema.CaseClass7[_, _, _, _, _, _, _, A] => Some(caseClass7Decoder(s)) - case s: Schema.CaseClass8[_, _, _, _, _, _, _, _, A] => Some(caseClass8Decoder(s)) - case s: Schema.CaseClass9[_, _, _, _, _, _, _, _, _, A] => Some(caseClass9Decoder(s)) - case s: Schema.CaseClass10[_, _, _, _, _, _, _, _, _, _, A] => Some(caseClass10Decoder(s)) - case s: Schema.CaseClass11[_, _, _, _, _, _, _, _, _, _, _, A] => Some(caseClass11Decoder(s)) - case s: Schema.CaseClass12[_, _, _, _, _, _, _, _, _, _, _, _, A] => Some(caseClass12Decoder(s)) - case s: Schema.CaseClass13[_, _, _, _, _, _, _, _, _, _, _, _, _, A] => Some(caseClass13Decoder(s)) - case s: Schema.CaseClass14[_, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => Some(caseClass14Decoder(s)) - case s: Schema.CaseClass15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => Some(caseClass15Decoder(s)) - case s: Schema.CaseClass16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => Some(caseClass16Decoder(s)) - case s: Schema.CaseClass17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => Some(caseClass17Decoder(s)) - case s: Schema.CaseClass18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => Some(caseClass18Decoder(s)) - case s: Schema.CaseClass19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => Some(caseClass19Decoder(s)) - case s: Schema.CaseClass20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => - Some(caseClass20Decoder(s)) - case s: Schema.CaseClass21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => - Some(caseClass21Decoder(s)) - case s: Schema.CaseClass22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, A] => - Some(caseClass22Decoder(s)) - case _ => None + override protected def startCreatingOneSetElement(context: DecoderContext, schema: Schema.Set[_]): DecoderContext = + context + + override protected def finishedCreatingOneSetElement( + context: DecoderContext, + schema: Schema.Set[_], + index: Int + ): Boolean = + context.expectedCount.map(_ - (index + 1)).exists(_ > 0) + + override protected def createSet( + context: DecoderContext, + schema: Schema.Set[_], + values: Chunk[Any] + ): Any = + values.toSet + + override protected def startCreatingOptional( + context: DecoderContext, + schema: Schema.Optional[_] + ): Option[DecoderContext] = { + val field = p.readFieldBegin() + field.id match { + case 1 => None + case 2 => Some(context.copy(path = context.path :+ "Some")) + case id => + fail(context, s"Error decoding optional, wrong field id $id").asInstanceOf[Option[DecoderContext]] } + } - private def unsafeDecodeFields[Z](path: Path, fields: Schema.Field[Z, _]*): Result[Array[Any]] = { - val buffer = Array.ofDim[Any](fields.size) + override protected def createOptional( + context: DecoderContext, + schema: Schema.Optional[_], + value: Option[Any] + ): Any = { + p.readFieldBegin() + value + } - @tailrec - def addFields(values: ListMap[Short, Any], index: Int): Result[Array[Any]] = - if (index >= fields.size) Right(buffer) - else { - val Schema.Field(label, schema, annotations, _, _, _) = fields(index) - val rawValue = values.get((index + 1).toShort) - rawValue match { - case Some(value) => - buffer.update(index, value) - addFields(values, index + 1) - case None => - emptyValue(schema) match { - case Some(value) => - buffer.update(index, value) - addFields(values, index + 1) - case None => - val optionalFieldAnnotation = annotations.collectFirst({ case a: optionalField => a }) - if (optionalFieldAnnotation.isDefined) { - buffer.update(index, schema.defaultValue.toOption.get) - addFields(values, index + 1) - } else fail(path :+ label, "Missing value") - } - } - } + override protected def startCreatingEither( + context: DecoderContext, + schema: Schema.Either[_, _] + ): Either[DecoderContext, DecoderContext] = { + val readField = p.readFieldBegin() + readField.id match { + case 1 => Left(context.copy(path = context.path :+ "either:left")) + case 2 => Right(context.copy(path = context.path :+ "either:right")) + case _ => fail(context, "Failed to decode either.").asInstanceOf[Either[DecoderContext, DecoderContext]] + } + } + + override protected def createEither( + context: DecoderContext, + schema: Schema.Either[_, _], + value: Either[Any, Any] + ): Any = + value - structDecoder(fields.map(_.schema), path).flatMap(addFields(_, 0)) + override protected def startCreatingTuple(context: DecoderContext, schema: Schema.Tuple2[_, _]): DecoderContext = { + p.readFieldBegin() + context + } + + override protected def startReadingSecondTupleElement( + context: DecoderContext, + schema: Schema.Tuple2[_, _] + ): DecoderContext = { + p.readFieldBegin() + context + } + + override protected def createTuple( + context: DecoderContext, + schema: Schema.Tuple2[_, _], + left: Any, + right: Any + ): Any = { + p.readFieldBegin() + (left, right) + } + + override protected def createDynamic(context: DecoderContext): Option[Any] = + None + + override protected def transform( + context: DecoderContext, + value: Any, + f: Any => Either[String, Any], + schema: Schema[_] + ): Any = + f(value) match { + case Left(value) => fail(context, value) + case Right(value) => value } - @tailrec - private def validateBuffer(path: Path, index: Int, buffer: Array[Any]): Result[Array[Any]] = - if (index == buffer.length - 1 && buffer(index) != null) - succeed(buffer) - else if (buffer(index) == null) - fail(path, s"Missing field number $index.") - else - validateBuffer(path, index + 1, buffer) - - private def caseClass1Decoder[A, Z](schema: Schema.CaseClass1[A, Z])(path: Path): Result[Z] = - unsafeDecodeFields(path, schema.field).flatMap { buffer => - if (buffer(0) == null) - fail(path, "Missing field 1.") - else - succeed(schema.defaultConstruct(buffer(0).asInstanceOf[A])) - } + override protected def fail(context: DecoderContext, message: String): Any = + throw MalformedFieldWithPath(context.path, message) + + override protected val initialContext: DecoderContext = DecoderContext(Chunk.empty, None) - private def caseClass2Decoder[A1, A2, Z](schema: Schema.CaseClass2[A1, A2, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2]) - - private def caseClass3Decoder[A1, A2, A3, Z](schema: Schema.CaseClass3[A1, A2, A3, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3]) - - private def caseClass4Decoder[A1, A2, A3, A4, Z](schema: Schema.CaseClass4[A1, A2, A3, A4, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4]) - - private def caseClass5Decoder[A1, A2, A3, A4, A5, Z](schema: Schema.CaseClass5[A1, A2, A3, A4, A5, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5]) - - private def caseClass6Decoder[A1, A2, A3, A4, A5, A6, Z](schema: Schema.CaseClass6[A1, A2, A3, A4, A5, A6, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6]) - - private def caseClass7Decoder[A1, A2, A3, A4, A5, A6, A7, Z](schema: Schema.CaseClass7[A1, A2, A3, A4, A5, A6, A7, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7]) - - private def caseClass8Decoder[A1, A2, A3, A4, A5, A6, A7, A8, Z](schema: Schema.CaseClass8[A1, A2, A3, A4, A5, A6, A7, A8, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field8) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7], buffer(7).asInstanceOf[A8]) - - private def caseClass9Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, Z](schema: Schema.CaseClass9[A1, A2, A3, A4, A5, A6, A7, A8, A9, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7], buffer(7).asInstanceOf[A8], buffer(8).asInstanceOf[A9]) - - private def caseClass10Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, Z](schema: Schema.CaseClass10[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7], buffer(7).asInstanceOf[A8], buffer(8).asInstanceOf[A9], buffer(9).asInstanceOf[A10]) - - private def caseClass11Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, Z](schema: Schema.CaseClass11[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7], buffer(7).asInstanceOf[A8], buffer(8).asInstanceOf[A9], buffer(9).asInstanceOf[A10], buffer(10).asInstanceOf[A11]) - - private def caseClass12Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, Z](schema: Schema.CaseClass12[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct(buffer(0).asInstanceOf[A1], buffer(1).asInstanceOf[A2], buffer(2).asInstanceOf[A3], buffer(3).asInstanceOf[A4], buffer(4).asInstanceOf[A5], buffer(5).asInstanceOf[A6], buffer(6).asInstanceOf[A7], buffer(7).asInstanceOf[A8], buffer(8).asInstanceOf[A9], buffer(9).asInstanceOf[A10], buffer(10).asInstanceOf[A11], buffer(11).asInstanceOf[A12]) - - private def caseClass13Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, Z](schema: Schema.CaseClass13[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13] - ) - - private def caseClass14Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, Z](schema: Schema.CaseClass14[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14] - ) - - private def caseClass15Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, Z](schema: Schema.CaseClass15[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15] - ) - - private def caseClass16Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, Z](schema: Schema.CaseClass16[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16] - ) - - private def caseClass17Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, Z](schema: Schema.CaseClass17[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16, schema.field17) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17] - ) - - private def caseClass18Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, Z](schema: Schema.CaseClass18[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16, schema.field17, schema.field18) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17], - buffer(17).asInstanceOf[A18] - ) - - private def caseClass19Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, Z](schema: Schema.CaseClass19[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16, schema.field17, schema.field18, schema.field19) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17], - buffer(17).asInstanceOf[A18], - buffer(18).asInstanceOf[A19] - ) - - private def caseClass20Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, Z](schema: Schema.CaseClass20[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16, schema.field17, schema.field18, schema.field19, schema.field20) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17], - buffer(17).asInstanceOf[A18], - buffer(18).asInstanceOf[A19], - buffer(19).asInstanceOf[A20] - ) - - private def caseClass21Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, Z](schema: Schema.CaseClass21[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16, schema.field17, schema.field18, schema.field19, schema.field20, schema.field21) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17], - buffer(17).asInstanceOf[A18], - buffer(18).asInstanceOf[A19], - buffer(19).asInstanceOf[A20], - buffer(20).asInstanceOf[A21] - ) - - private def caseClass22Decoder[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, Z](schema: Schema.CaseClass22[A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, Z])(path: Path): Result[Z] = - for { - buffer <- unsafeDecodeFields(path, schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7, schema.field9, schema.field9, schema.field10, schema.field11, schema.field12, schema.field13, schema.field14, schema.field15, schema.field16, schema.field17, schema.field18, schema.field19, schema.field20, schema.field21, schema.field22) - _ <- validateBuffer(path, 0, buffer) - } yield schema.construct( - buffer(0).asInstanceOf[A1], - buffer(1).asInstanceOf[A2], - buffer(2).asInstanceOf[A3], - buffer(3).asInstanceOf[A4], - buffer(4).asInstanceOf[A5], - buffer(5).asInstanceOf[A6], - buffer(6).asInstanceOf[A7], - buffer(7).asInstanceOf[A8], - buffer(8).asInstanceOf[A9], - buffer(9).asInstanceOf[A10], - buffer(10).asInstanceOf[A11], - buffer(11).asInstanceOf[A12], - buffer(12).asInstanceOf[A13], - buffer(13).asInstanceOf[A14], - buffer(14).asInstanceOf[A15], - buffer(15).asInstanceOf[A16], - buffer(16).asInstanceOf[A17], - buffer(17).asInstanceOf[A18], - buffer(18).asInstanceOf[A19], - buffer(19).asInstanceOf[A20], - buffer(20).asInstanceOf[A21], - buffer(21).asInstanceOf[A22] - ) + private def emptyValue[A](schema: Schema[A]): Option[A] = schema match { + case Schema.Lazy(s) => emptyValue(s()) + case Schema.Optional(_, _) => Some(None) + case Schema.Sequence(_, fromChunk, _, _, _) => Some(fromChunk(Chunk.empty)) + case Schema.Primitive(StandardType.UnitType, _) => Some(()) + case _ => None } } - } diff --git a/zio-schema-thrift/shared/src/test/scala-2/zio/schema/codec/ThriftCodecSpec.scala b/zio-schema-thrift/shared/src/test/scala-2/zio/schema/codec/ThriftCodecSpec.scala index a82843317..f22176240 100644 --- a/zio-schema-thrift/shared/src/test/scala-2/zio/schema/codec/ThriftCodecSpec.scala +++ b/zio-schema-thrift/shared/src/test/scala-2/zio/schema/codec/ThriftCodecSpec.scala @@ -147,9 +147,9 @@ object ThriftCodecSpec extends ZIOSpecDefault { }, test("failure") { for { - e <- encode(schemaFail, StringValue("foo")).map(_.size) - e2 <- encodeNS(schemaFail, StringValue("foo")).map(_.size) - } yield assert(e)(equalTo(0)) && assert(e2)(equalTo(0)) + e <- encode(schemaFail, StringValue("foo")).map(_.size).exit + e2 <- encodeNS(schemaFail, StringValue("foo")).map(_.size).exit + } yield assert(e)(dies(anything)) && assert(e2)(dies(anything)) } ), suite("Should successfully encode and decode")( @@ -510,9 +510,12 @@ object ThriftCodecSpec extends ZIOSpecDefault { edSomeNone2 <- encodeAndDecodeNS(Schema.option(Schema.option(Schema[Boolean])), valueSomeNone) edNone <- encodeAndDecode(Schema.option(Schema.option(Schema[Boolean])), valueNone) edNone2 <- encodeAndDecodeNS(Schema.option(Schema.option(Schema[Boolean])), valueNone) - } yield assert(ed)(equalTo(Chunk(value))) && assert(ed2)(equalTo(value)) && - assert(edSomeNone)(equalTo(Chunk(valueSomeNone))) && assert(edSomeNone2)(equalTo(valueSomeNone)) && - assert(edNone)(equalTo(Chunk(valueNone))) && assert(edNone2)(equalTo(valueNone)) + } yield assert(ed)(equalTo(Chunk(value))) && + assert(ed2)(equalTo(value)) && + assert(edSomeNone)(equalTo(Chunk(valueSomeNone))) && + assert(edSomeNone2)(equalTo(valueSomeNone)) && + assert(edNone)(equalTo(Chunk(valueNone))) && + assert(edNone2)(equalTo(valueNone)) }, test("option with omitted field") { // this behavior is useful for decoding classes which were generated by thrift compiler @@ -662,6 +665,15 @@ object ThriftCodecSpec extends ZIOSpecDefault { } yield assert(ed)(equalTo(Chunk(value))) && assert(ed2)(equalTo(value)) } }, + test("deep recursive data types") { + check(SchemaGen.anyDeepRecursiveTypeAndValue) { + case (schema, value) => + for { + ed <- encodeAndDecode(schema, value) + ed2 <- encodeAndDecodeNS(schema, value) + } yield assert(ed)(equalTo(Chunk(value))) && assert(ed2)(equalTo(value)) + } + } @@ TestAspect.sized(200), suite("dynamic")( test("dynamic int") { check( diff --git a/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala b/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala index 71f08bb1d..f94acefba 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala @@ -117,1613 +117,62 @@ sealed trait DynamicValue { } object DynamicValue { + private object FromSchemaAndValue extends SimpleMutableSchemaBasedValueProcessor[DynamicValue] { + override protected def processPrimitive(value: Any, typ: StandardType[Any]): DynamicValue = + DynamicValue.Primitive(value, typ) + + override protected def processRecord(schema: Schema.Record[_], value: ListMap[String, DynamicValue]): DynamicValue = + DynamicValue.Record(schema.id, value) + + override protected def processEnum(schema: Schema.Enum[_], tuple: (String, DynamicValue)): DynamicValue = + DynamicValue.Enumeration(schema.id, tuple) + + override protected def processSequence(schema: Schema.Sequence[_, _, _], value: Chunk[DynamicValue]): DynamicValue = + DynamicValue.Sequence(value) + + override protected def processDictionary( + schema: Schema.Map[_, _], + value: Chunk[(DynamicValue, DynamicValue)] + ): DynamicValue = + DynamicValue.Dictionary(value) + + override protected def processSet(schema: Schema.Set[_], value: Set[DynamicValue]): DynamicValue = + DynamicValue.SetValue(value) + + override protected def processEither( + schema: Schema.Either[_, _], + value: Either[DynamicValue, DynamicValue] + ): DynamicValue = + value match { + case Left(value) => DynamicValue.LeftValue(value) + case Right(value) => DynamicValue.RightValue(value) + } - def apply[A](a: A)(implicit ev: Schema[A]): DynamicValue = ev.toDynamic(a) - - //scalafmt: { maxColumn = 400 } - def fromSchemaAndValue[A](schema: Schema[A], value: A): DynamicValue = - schema match { - - case l @ Schema.Lazy(_) => fromSchemaAndValue(l.schema, value) - - case Schema.Primitive(p, _) => DynamicValue.Primitive(value, p) - - case Schema.GenericRecord(id, structure, _) => - val map: ListMap[String, _] = value - DynamicValue.Record( - id, - ListMap.empty ++ structure.toChunk.map { - case Schema.Field(key, schema: Schema[a], _, _, _, _) => - key -> fromSchemaAndValue(schema, map(key).asInstanceOf[a]) - } - ) - - case Schema.Enum1(id, case1, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, case1.deconstruct(value))) - - case Schema.Enum2(id, case1, case2, _) => - (case1.deconstructOption(value), case2.deconstructOption(value)) match { - case (Some(v1), _) => DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2)) => DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum3(id, case1, case2, case3, _) => - (case1.deconstructOption(value), case2.deconstructOption(value), case3.deconstructOption(value)) match { - case (Some(v1), _, _) => DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _) => DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3)) => DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum4(id, case1, case2, case3, case4, _) => - (case1.deconstructOption(value), case2.deconstructOption(value), case3.deconstructOption(value), case4.deconstructOption(value)) match { - case (Some(v1), _, _, _) => DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _) => DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _) => DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4)) => DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum5(id, case1, case2, case3, case4, case5, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _) => DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _) => DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _) => DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _) => DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5)) => DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum6(id, case1, case2, case3, case4, case5, case6, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _) => DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _) => DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _) => DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _) => DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _) => DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6)) => DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum7(id, case1, case2, case3, case4, case5, case6, case7, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _) => DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _) => DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _) => DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _) => DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _) => DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _) => DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7)) => DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum8(id, case1, case2, case3, case4, case5, case6, case7, case8, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8)) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum9(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9)) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum10(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10)) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum11(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11)) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum12(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value), - case12.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _, _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11), _) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - case (_, _, _, _, _, _, _, _, _, _, _, Some(v12)) => - DynamicValue.Enumeration(id, case12.id -> fromSchemaAndValue(case12.schema, v12)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum13(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value), - case12.deconstructOption(value), - case13.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _, _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _, _, _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11), _, _) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - case (_, _, _, _, _, _, _, _, _, _, _, Some(v12), _) => - DynamicValue.Enumeration(id, case12.id -> fromSchemaAndValue(case12.schema, v12)) - case (_, _, _, _, _, _, _, _, _, _, _, _, Some(v13)) => - DynamicValue.Enumeration(id, case13.id -> fromSchemaAndValue(case13.schema, v13)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum14(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value), - case12.deconstructOption(value), - case13.deconstructOption(value), - case14.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _, _, _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _, _, _, _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11), _, _, _) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - case (_, _, _, _, _, _, _, _, _, _, _, Some(v12), _, _) => - DynamicValue.Enumeration(id, case12.id -> fromSchemaAndValue(case12.schema, v12)) - case (_, _, _, _, _, _, _, _, _, _, _, _, Some(v13), _) => - DynamicValue.Enumeration(id, case13.id -> fromSchemaAndValue(case13.schema, v13)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, Some(v14)) => - DynamicValue.Enumeration(id, case14.id -> fromSchemaAndValue(case14.schema, v14)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum15(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value), - case12.deconstructOption(value), - case13.deconstructOption(value), - case14.deconstructOption(value), - case15.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _, _, _, _, _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11), _, _, _, _) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - case (_, _, _, _, _, _, _, _, _, _, _, Some(v12), _, _, _) => - DynamicValue.Enumeration(id, case12.id -> fromSchemaAndValue(case12.schema, v12)) - case (_, _, _, _, _, _, _, _, _, _, _, _, Some(v13), _, _) => - DynamicValue.Enumeration(id, case13.id -> fromSchemaAndValue(case13.schema, v13)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, Some(v14), _) => - DynamicValue.Enumeration(id, case14.id -> fromSchemaAndValue(case14.schema, v14)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v15)) => - DynamicValue.Enumeration(id, case15.id -> fromSchemaAndValue(case15.schema, v15)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum16(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value), - case12.deconstructOption(value), - case13.deconstructOption(value), - case14.deconstructOption(value), - case15.deconstructOption(value), - case16.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11), _, _, _, _, _) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - case (_, _, _, _, _, _, _, _, _, _, _, Some(v12), _, _, _, _) => - DynamicValue.Enumeration(id, case12.id -> fromSchemaAndValue(case12.schema, v12)) - case (_, _, _, _, _, _, _, _, _, _, _, _, Some(v13), _, _, _) => - DynamicValue.Enumeration(id, case13.id -> fromSchemaAndValue(case13.schema, v13)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, Some(v14), _, _) => - DynamicValue.Enumeration(id, case14.id -> fromSchemaAndValue(case14.schema, v14)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v15), _) => - DynamicValue.Enumeration(id, case15.id -> fromSchemaAndValue(case15.schema, v15)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v16)) => - DynamicValue.Enumeration(id, case16.id -> fromSchemaAndValue(case16.schema, v16)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum17(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value), - case12.deconstructOption(value), - case13.deconstructOption(value), - case14.deconstructOption(value), - case15.deconstructOption(value), - case16.deconstructOption(value), - case17.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - case (_, _, _, _, _, _, _, _, _, _, _, Some(v12), _, _, _, _, _) => - DynamicValue.Enumeration(id, case12.id -> fromSchemaAndValue(case12.schema, v12)) - case (_, _, _, _, _, _, _, _, _, _, _, _, Some(v13), _, _, _, _) => - DynamicValue.Enumeration(id, case13.id -> fromSchemaAndValue(case13.schema, v13)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, Some(v14), _, _, _) => - DynamicValue.Enumeration(id, case14.id -> fromSchemaAndValue(case14.schema, v14)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v15), _, _) => - DynamicValue.Enumeration(id, case15.id -> fromSchemaAndValue(case15.schema, v15)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v16), _) => - DynamicValue.Enumeration(id, case16.id -> fromSchemaAndValue(case16.schema, v16)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v17)) => - DynamicValue.Enumeration(id, case17.id -> fromSchemaAndValue(case17.schema, v17)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum18(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value), - case12.deconstructOption(value), - case13.deconstructOption(value), - case14.deconstructOption(value), - case15.deconstructOption(value), - case16.deconstructOption(value), - case17.deconstructOption(value), - case18.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - case (_, _, _, _, _, _, _, _, _, _, _, Some(v12), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case12.id -> fromSchemaAndValue(case12.schema, v12)) - case (_, _, _, _, _, _, _, _, _, _, _, _, Some(v13), _, _, _, _, _) => - DynamicValue.Enumeration(id, case13.id -> fromSchemaAndValue(case13.schema, v13)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, Some(v14), _, _, _, _) => - DynamicValue.Enumeration(id, case14.id -> fromSchemaAndValue(case14.schema, v14)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v15), _, _, _) => - DynamicValue.Enumeration(id, case15.id -> fromSchemaAndValue(case15.schema, v15)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v16), _, _) => - DynamicValue.Enumeration(id, case16.id -> fromSchemaAndValue(case16.schema, v16)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v17), _) => - DynamicValue.Enumeration(id, case17.id -> fromSchemaAndValue(case17.schema, v17)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v18)) => - DynamicValue.Enumeration(id, case18.id -> fromSchemaAndValue(case18.schema, v18)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum19(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value), - case12.deconstructOption(value), - case13.deconstructOption(value), - case14.deconstructOption(value), - case15.deconstructOption(value), - case16.deconstructOption(value), - case17.deconstructOption(value), - case18.deconstructOption(value), - case19.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - case (_, _, _, _, _, _, _, _, _, _, _, Some(v12), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case12.id -> fromSchemaAndValue(case12.schema, v12)) - case (_, _, _, _, _, _, _, _, _, _, _, _, Some(v13), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case13.id -> fromSchemaAndValue(case13.schema, v13)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, Some(v14), _, _, _, _, _) => - DynamicValue.Enumeration(id, case14.id -> fromSchemaAndValue(case14.schema, v14)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v15), _, _, _, _) => - DynamicValue.Enumeration(id, case15.id -> fromSchemaAndValue(case15.schema, v15)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v16), _, _, _) => - DynamicValue.Enumeration(id, case16.id -> fromSchemaAndValue(case16.schema, v16)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v17), _, _) => - DynamicValue.Enumeration(id, case17.id -> fromSchemaAndValue(case17.schema, v17)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v18), _) => - DynamicValue.Enumeration(id, case18.id -> fromSchemaAndValue(case18.schema, v18)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v19)) => - DynamicValue.Enumeration(id, case19.id -> fromSchemaAndValue(case19.schema, v19)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum20(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, case20, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value), - case12.deconstructOption(value), - case13.deconstructOption(value), - case14.deconstructOption(value), - case15.deconstructOption(value), - case16.deconstructOption(value), - case17.deconstructOption(value), - case18.deconstructOption(value), - case19.deconstructOption(value), - case20.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - case (_, _, _, _, _, _, _, _, _, _, _, Some(v12), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case12.id -> fromSchemaAndValue(case12.schema, v12)) - case (_, _, _, _, _, _, _, _, _, _, _, _, Some(v13), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case13.id -> fromSchemaAndValue(case13.schema, v13)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, Some(v14), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case14.id -> fromSchemaAndValue(case14.schema, v14)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v15), _, _, _, _, _) => - DynamicValue.Enumeration(id, case15.id -> fromSchemaAndValue(case15.schema, v15)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v16), _, _, _, _) => - DynamicValue.Enumeration(id, case16.id -> fromSchemaAndValue(case16.schema, v16)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v17), _, _, _) => - DynamicValue.Enumeration(id, case17.id -> fromSchemaAndValue(case17.schema, v17)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v18), _, _) => - DynamicValue.Enumeration(id, case18.id -> fromSchemaAndValue(case18.schema, v18)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v19), _) => - DynamicValue.Enumeration(id, case19.id -> fromSchemaAndValue(case19.schema, v19)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v20)) => - DynamicValue.Enumeration(id, case20.id -> fromSchemaAndValue(case20.schema, v20)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum21(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, case20, case21, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value), - case12.deconstructOption(value), - case13.deconstructOption(value), - case14.deconstructOption(value), - case15.deconstructOption(value), - case16.deconstructOption(value), - case17.deconstructOption(value), - case18.deconstructOption(value), - case19.deconstructOption(value), - case20.deconstructOption(value), - case21.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - case (_, _, _, _, _, _, _, _, _, _, _, Some(v12), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case12.id -> fromSchemaAndValue(case12.schema, v12)) - case (_, _, _, _, _, _, _, _, _, _, _, _, Some(v13), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case13.id -> fromSchemaAndValue(case13.schema, v13)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, Some(v14), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case14.id -> fromSchemaAndValue(case14.schema, v14)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v15), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case15.id -> fromSchemaAndValue(case15.schema, v15)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v16), _, _, _, _, _) => - DynamicValue.Enumeration(id, case16.id -> fromSchemaAndValue(case16.schema, v16)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v17), _, _, _, _) => - DynamicValue.Enumeration(id, case17.id -> fromSchemaAndValue(case17.schema, v17)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v18), _, _, _) => - DynamicValue.Enumeration(id, case18.id -> fromSchemaAndValue(case18.schema, v18)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v19), _, _) => - DynamicValue.Enumeration(id, case19.id -> fromSchemaAndValue(case19.schema, v19)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v20), _) => - DynamicValue.Enumeration(id, case20.id -> fromSchemaAndValue(case20.schema, v20)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v21)) => - DynamicValue.Enumeration(id, case21.id -> fromSchemaAndValue(case21.schema, v21)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - - case Schema.Enum22(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, case20, case21, case22, _) => - ( - case1.deconstructOption(value), - case2.deconstructOption(value), - case3.deconstructOption(value), - case4.deconstructOption(value), - case5.deconstructOption(value), - case6.deconstructOption(value), - case7.deconstructOption(value), - case8.deconstructOption(value), - case9.deconstructOption(value), - case10.deconstructOption(value), - case11.deconstructOption(value), - case12.deconstructOption(value), - case13.deconstructOption(value), - case14.deconstructOption(value), - case15.deconstructOption(value), - case16.deconstructOption(value), - case17.deconstructOption(value), - case18.deconstructOption(value), - case19.deconstructOption(value), - case20.deconstructOption(value), - case21.deconstructOption(value), - case22.deconstructOption(value) - ) match { - case (Some(v1), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case1.id -> fromSchemaAndValue(case1.schema, v1)) - case (_, Some(v2), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case2.id -> fromSchemaAndValue(case2.schema, v2)) - case (_, _, Some(v3), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case3.id -> fromSchemaAndValue(case3.schema, v3)) - case (_, _, _, Some(v4), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case4.id -> fromSchemaAndValue(case4.schema, v4)) - case (_, _, _, _, Some(v5), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case5.id -> fromSchemaAndValue(case5.schema, v5)) - case (_, _, _, _, _, Some(v6), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case6.id -> fromSchemaAndValue(case6.schema, v6)) - case (_, _, _, _, _, _, Some(v7), _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case7.id -> fromSchemaAndValue(case7.schema, v7)) - case (_, _, _, _, _, _, _, Some(v8), _, _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case8.id -> fromSchemaAndValue(case8.schema, v8)) - case (_, _, _, _, _, _, _, _, Some(v9), _, _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case9.id -> fromSchemaAndValue(case9.schema, v9)) - case (_, _, _, _, _, _, _, _, _, Some(v10), _, _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case10.id -> fromSchemaAndValue(case10.schema, v10)) - case (_, _, _, _, _, _, _, _, _, _, Some(v11), _, _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case11.id -> fromSchemaAndValue(case11.schema, v11)) - case (_, _, _, _, _, _, _, _, _, _, _, Some(v12), _, _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case12.id -> fromSchemaAndValue(case12.schema, v12)) - case (_, _, _, _, _, _, _, _, _, _, _, _, Some(v13), _, _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case13.id -> fromSchemaAndValue(case13.schema, v13)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, Some(v14), _, _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case14.id -> fromSchemaAndValue(case14.schema, v14)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v15), _, _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case15.id -> fromSchemaAndValue(case15.schema, v15)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v16), _, _, _, _, _, _) => - DynamicValue.Enumeration(id, case16.id -> fromSchemaAndValue(case16.schema, v16)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v17), _, _, _, _, _) => - DynamicValue.Enumeration(id, case17.id -> fromSchemaAndValue(case17.schema, v17)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v18), _, _, _, _) => - DynamicValue.Enumeration(id, case18.id -> fromSchemaAndValue(case18.schema, v18)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v19), _, _, _) => - DynamicValue.Enumeration(id, case19.id -> fromSchemaAndValue(case19.schema, v19)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v20), _, _) => - DynamicValue.Enumeration(id, case20.id -> fromSchemaAndValue(case20.schema, v20)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v21), _) => - DynamicValue.Enumeration(id, case21.id -> fromSchemaAndValue(case21.schema, v21)) - case (_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, Some(v22)) => - DynamicValue.Enumeration(id, case22.id -> fromSchemaAndValue(case22.schema, v22)) - //This should never happen unless someone manually builds an Enum and doesn't include all cases - case _ => DynamicValue.NoneValue - } - //scalafmt: { maxColumn = 120 } - - case Schema.EnumN(id, cases, _) => - cases.toSeq - .find(_.deconstructOption(value).isDefined) match { - case Some(c) => - DynamicValue.Enumeration( - id, - c.id -> fromSchemaAndValue(c.schema.asInstanceOf[Schema[Any]], c.deconstruct(value)) - ) - case None => DynamicValue.NoneValue - } - - case Schema.Fail(message, _) => DynamicValue.Error(message) - - case Schema.Sequence(schema, _, toChunk, _, _) => - DynamicValue.Sequence(toChunk(value).map(fromSchemaAndValue(schema, _))) - - case Schema.Map(ks: Schema[k], vs: Schema[v], _) => - val entries = value.asInstanceOf[Map[k, v]].map { - case (key, value) => (fromSchemaAndValue(ks, key), fromSchemaAndValue(vs, value)) - } - DynamicValue.Dictionary(Chunk.fromIterable(entries)) - - case Schema.Set(as: Schema[a], _) => - DynamicValue.SetValue(value.asInstanceOf[Set[a]].map(fromSchemaAndValue(as, _))) + override protected def processOption(schema: Schema.Optional[_], value: Option[DynamicValue]): DynamicValue = + value match { + case Some(value) => DynamicValue.SomeValue(value) + case None => DynamicValue.NoneValue + } - case schema: Schema.Either[l, r] => - value.asInstanceOf[Either[l, r]] match { - case Left(value: l) => DynamicValue.LeftValue(fromSchemaAndValue(schema.left, value)) - case Right(value: r) => DynamicValue.RightValue(fromSchemaAndValue(schema.right, value)) - } + override protected def processTuple( + schema: Schema.Tuple2[_, _], + left: DynamicValue, + right: DynamicValue + ): DynamicValue = + DynamicValue.Tuple(left, right) - case schema: Schema.Tuple2[a, b] => - val (a: a, b: b) = value.asInstanceOf[(a, b)] - DynamicValue.Tuple(fromSchemaAndValue(schema.left, a), fromSchemaAndValue(schema.right, b)) + override protected def processDynamic(value: DynamicValue): Option[DynamicValue] = + Some(value) - case schema: Schema.Optional[a] => - value.asInstanceOf[Option[a]] match { - case Some(value: a) => DynamicValue.SomeValue(fromSchemaAndValue(schema.schema, value)) - case None => DynamicValue.NoneValue - } + override protected def fail(message: String): DynamicValue = + DynamicValue.Error(message) + } - case Schema.Transform(schema, _, g, _, _) => - g(value) match { - case Left(message) => DynamicValue.Error(message) - case Right(a) => fromSchemaAndValue(schema, a) - } + def apply[A](a: A)(implicit ev: Schema[A]): DynamicValue = ev.toDynamic(a) - case Schema.CaseClass0(id, _, _) => - DynamicValue.Record(id, ListMap()) - - case Schema.CaseClass1(id, f, _, _) => - DynamicValue.Record(id, ListMap(f.name -> fromSchemaAndValue(f.schema, f.get(value)))) - - case Schema.CaseClass2(id, f1, f2, _, _) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)) - ) - ) - case Schema.CaseClass3(id, f1, f2, f3, _, _) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)) - ) - ) - case Schema.CaseClass4(id, f1, f2, f3, f4, _, _) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)) - ) - ) - case Schema.CaseClass5(id, f1, f2, f3, f4, f5, _, _) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)) - ) - ) - case Schema.CaseClass6(id, f1, f2, f3, f4, f5, f6, _, _) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)) - ) - ) - case Schema.CaseClass7(id, f1, f2, f3, f4, f5, f6, f7, _, _) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)) - ) - ) - case Schema.CaseClass8( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)) - ) - ) - case Schema.CaseClass9( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)) - ) - ) - case Schema.CaseClass10( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)) - ) - ) - case Schema.CaseClass11( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)) - ) - ) - case Schema.CaseClass12( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)), - f12.name -> fromSchemaAndValue(f12.schema, f12.get(value)) - ) - ) - case Schema.CaseClass13( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)), - f12.name -> fromSchemaAndValue(f12.schema, f12.get(value)), - f13.name -> fromSchemaAndValue(f13.schema, f13.get(value)) - ) - ) - case Schema.CaseClass14( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)), - f12.name -> fromSchemaAndValue(f12.schema, f12.get(value)), - f13.name -> fromSchemaAndValue(f13.schema, f13.get(value)), - f14.name -> fromSchemaAndValue(f14.schema, f14.get(value)) - ) - ) - case Schema.CaseClass15( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)), - f12.name -> fromSchemaAndValue(f12.schema, f12.get(value)), - f13.name -> fromSchemaAndValue(f13.schema, f13.get(value)), - f14.name -> fromSchemaAndValue(f14.schema, f14.get(value)), - f15.name -> fromSchemaAndValue(f15.schema, f15.get(value)) - ) - ) - case Schema.CaseClass16( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)), - f12.name -> fromSchemaAndValue(f12.schema, f12.get(value)), - f13.name -> fromSchemaAndValue(f13.schema, f13.get(value)), - f14.name -> fromSchemaAndValue(f14.schema, f14.get(value)), - f15.name -> fromSchemaAndValue(f15.schema, f15.get(value)), - f16.name -> fromSchemaAndValue(f16.schema, f16.get(value)) - ) - ) - case Schema.CaseClass17( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)), - f12.name -> fromSchemaAndValue(f12.schema, f12.get(value)), - f13.name -> fromSchemaAndValue(f13.schema, f13.get(value)), - f14.name -> fromSchemaAndValue(f14.schema, f14.get(value)), - f15.name -> fromSchemaAndValue(f15.schema, f15.get(value)), - f16.name -> fromSchemaAndValue(f16.schema, f16.get(value)), - f17.name -> fromSchemaAndValue(f17.schema, f17.get(value)) - ) - ) - case Schema.CaseClass18( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)), - f12.name -> fromSchemaAndValue(f12.schema, f12.get(value)), - f13.name -> fromSchemaAndValue(f13.schema, f13.get(value)), - f14.name -> fromSchemaAndValue(f14.schema, f14.get(value)), - f15.name -> fromSchemaAndValue(f15.schema, f15.get(value)), - f16.name -> fromSchemaAndValue(f16.schema, f16.get(value)), - f17.name -> fromSchemaAndValue(f17.schema, f17.get(value)), - f18.name -> fromSchemaAndValue(f18.schema, f18.get(value)) - ) - ) - case Schema.CaseClass19( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)), - f12.name -> fromSchemaAndValue(f12.schema, f12.get(value)), - f13.name -> fromSchemaAndValue(f13.schema, f13.get(value)), - f14.name -> fromSchemaAndValue(f14.schema, f14.get(value)), - f15.name -> fromSchemaAndValue(f15.schema, f15.get(value)), - f16.name -> fromSchemaAndValue(f16.schema, f16.get(value)), - f17.name -> fromSchemaAndValue(f17.schema, f17.get(value)), - f18.name -> fromSchemaAndValue(f18.schema, f18.get(value)), - f19.name -> fromSchemaAndValue(f19.schema, f19.get(value)) - ) - ) - case Schema.CaseClass20( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - f20, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)), - f12.name -> fromSchemaAndValue(f12.schema, f12.get(value)), - f13.name -> fromSchemaAndValue(f13.schema, f13.get(value)), - f14.name -> fromSchemaAndValue(f14.schema, f14.get(value)), - f15.name -> fromSchemaAndValue(f15.schema, f15.get(value)), - f16.name -> fromSchemaAndValue(f16.schema, f16.get(value)), - f17.name -> fromSchemaAndValue(f17.schema, f17.get(value)), - f18.name -> fromSchemaAndValue(f18.schema, f18.get(value)), - f19.name -> fromSchemaAndValue(f19.schema, f19.get(value)), - f20.name -> fromSchemaAndValue(f20.schema, f20.get(value)) - ) - ) - case Schema.CaseClass21( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - f20, - f21, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)), - f12.name -> fromSchemaAndValue(f12.schema, f12.get(value)), - f13.name -> fromSchemaAndValue(f13.schema, f13.get(value)), - f14.name -> fromSchemaAndValue(f14.schema, f14.get(value)), - f15.name -> fromSchemaAndValue(f15.schema, f15.get(value)), - f16.name -> fromSchemaAndValue(f16.schema, f16.get(value)), - f17.name -> fromSchemaAndValue(f17.schema, f17.get(value)), - f18.name -> fromSchemaAndValue(f18.schema, f18.get(value)), - f19.name -> fromSchemaAndValue(f19.schema, f19.get(value)), - f20.name -> fromSchemaAndValue(f20.schema, f20.get(value)), - f21.name -> fromSchemaAndValue(f21.schema, f21.get(value)) - ) - ) - case Schema.CaseClass22( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - f20, - f21, - f22, - _, - _ - ) => - DynamicValue.Record( - id, - ListMap( - f1.name -> fromSchemaAndValue(f1.schema, f1.get(value)), - f2.name -> fromSchemaAndValue(f2.schema, f2.get(value)), - f3.name -> fromSchemaAndValue(f3.schema, f3.get(value)), - f4.name -> fromSchemaAndValue(f4.schema, f4.get(value)), - f5.name -> fromSchemaAndValue(f5.schema, f5.get(value)), - f6.name -> fromSchemaAndValue(f6.schema, f6.get(value)), - f7.name -> fromSchemaAndValue(f7.schema, f7.get(value)), - f8.name -> fromSchemaAndValue(f8.schema, f8.get(value)), - f9.name -> fromSchemaAndValue(f9.schema, f9.get(value)), - f10.name -> fromSchemaAndValue(f10.schema, f10.get(value)), - f11.name -> fromSchemaAndValue(f11.schema, f11.get(value)), - f12.name -> fromSchemaAndValue(f12.schema, f12.get(value)), - f13.name -> fromSchemaAndValue(f13.schema, f13.get(value)), - f14.name -> fromSchemaAndValue(f14.schema, f14.get(value)), - f15.name -> fromSchemaAndValue(f15.schema, f15.get(value)), - f16.name -> fromSchemaAndValue(f16.schema, f16.get(value)), - f17.name -> fromSchemaAndValue(f17.schema, f17.get(value)), - f18.name -> fromSchemaAndValue(f18.schema, f18.get(value)), - f19.name -> fromSchemaAndValue(f19.schema, f19.get(value)), - f20.name -> fromSchemaAndValue(f20.schema, f20.get(value)), - f21.name -> fromSchemaAndValue(f21.schema, f21.get(value)), - f22.name -> fromSchemaAndValue(f22.schema, f22.get(value)) - ) - ) - case Schema.Dynamic(_) => value - } + //scalafmt: { maxColumn = 400 } + def fromSchemaAndValue[A](schema: Schema[A], value: A): DynamicValue = + FromSchemaAndValue.process(schema, value) def decodeStructure( values: ListMap[String, DynamicValue], @@ -2042,8 +491,7 @@ private[schema] object DynamicValueSchema { "values", Schema.defer(Schema.chunk(Schema.tuple2(Schema.primitive[String], DynamicValueSchema()))), get = record => Chunk.fromIterable(record.values), - set = (record, values) => - record.copy(values = values.foldRight(ListMap.empty[String, DynamicValue])((a, b) => b + a)) + set = (record, values) => record.copy(values = values.foldRight(ListMap.empty[String, DynamicValue])((a, b) => b + a)) ), (id, chunk) => DynamicValue.Record(id, ListMap(chunk.toSeq: _*)) ), diff --git a/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueBuilder.scala b/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueBuilder.scala new file mode 100644 index 000000000..a7ac2d11e --- /dev/null +++ b/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueBuilder.scala @@ -0,0 +1,1168 @@ +package zio.schema + +import scala.util.control.NonFatal + +import zio.schema.MutableSchemaBasedValueBuilder.CreateValueFromSchemaError +import zio.{ Chunk, ChunkBuilder } + +/** + * Base trait for mutable builders producing a value based on a schema, such as codec decoders. + * + * The implementation is stack safe and consists of a series of invocations of the protected methods + * the trait defines. Maintaining the state of the builder, such as stream position etc. is the responsibility + * of the implementation class via mutable state. + * + * The Target type parameter is the base type for the generated values - this in many cases can be Any but + * potentially could be used to track errors in value level as well - although failure in the context handler + * manipulation methods cannot be expressed this way. + * + * The Context type parameter is a use-case dependent type that is managed in a stack during the execution of the builder. + * The implementation can generate new context values for the value's subtrees and it can be used to track local state + * required for gathering all information for the value to be created. The current context value is also propagated to + * any exception thrown so it can be used to provide detailed location information for decoder errors. + */ +trait MutableSchemaBasedValueBuilder[Target, Context] { + + /** Creates a primitive value of the given standard type */ + protected def createPrimitive(context: Context, typ: StandardType[_]): Target + + /** The next value to build is a record with the given schema */ + protected def startCreatingRecord(context: Context, record: Schema.Record[_]): Context + + /** Called for each field of a record. The resulting tuple is either None indicating there are no more fields to read, + * or it contains an updated context belonging to the field and the next field's index in the schema. This allows + * the implementation to instantiate fields in a different order than what the schema defines. + * + * The index parameter is a 0-based index, incremented by one for each field read within a record. + */ + protected def startReadingField(context: Context, record: Schema.Record[_], index: Int): Option[(Context, Int)] + + /** Creates a record value from the gathered field values */ + protected def createRecord(context: Context, record: Schema.Record[_], values: Chunk[(Int, Target)]): Target + + /** The next value to build is an enum with the given schema */ + protected def startCreatingEnum(context: Context, cases: Chunk[Schema.Case[_, _]]): (Context, Int) + + /** Creates an enum value from the read constructor value */ + protected def createEnum(context: Context, cases: Chunk[Schema.Case[_, _]], index: Int, value: Target): Target + + /** The next value to build is a sequence. If the returned value is None, the builder creates an empty sequence, + * otherwise it calls startCreatingOneSequenceElement and finishedCreatingOneSequenceElement for + * each element with the returned context. + */ + protected def startCreatingSequence(context: Context, schema: Schema.Sequence[_, _, _]): Option[Context] + + /** Called before constructing a next sequence element. The returned context is used for constructing + * that single element.*/ + protected def startCreatingOneSequenceElement(context: Context, schema: Schema.Sequence[_, _, _]): Context + + /** Called after constructing a single sequence element. The context is the context of the whole sequence. + * If the returned value is true, a next element will be read otherwise the sequence is completed and createSequence + * is called. */ + protected def finishedCreatingOneSequenceElement( + context: Context, + schema: Schema.Sequence[_, _, _], + index: Int + ): Boolean + + /** Creates the sequence value from the chunk of element values */ + protected def createSequence(context: Context, schema: Schema.Sequence[_, _, _], values: Chunk[Target]): Target + + /** The next value to build is a dictionary. If the returned value is None, the builder creates an empty dictionary, + * otherwise it calls startCreatingOneDictionaryElement, startCreatingOneDictionaryValue and + * finishedCreatingOneDictionaryElement for each element with the returned context. + */ + protected def startCreatingDictionary(context: Context, schema: Schema.Map[_, _]): Option[Context] + + /** Called before constructing a next dictionary element. The returned context is used for constructing + * that single element's key. */ + protected def startCreatingOneDictionaryElement(context: Context, schema: Schema.Map[_, _]): Context + + /** Called after the key of a single element was created, before the value gets created. + * The returned context is for constructing the element's value part. */ + protected def startCreatingOneDictionaryValue(context: Context, schema: Schema.Map[_, _]): Context + + /** Called after constructing a single dictionary element. The context is the context of the whole dictionary. + * If the returned value is true, a next element will be read otherwise the dictionary is completed and createDictionary + * is called. */ + protected def finishedCreatingOneDictionaryElement( + context: Context, + schema: Schema.Map[_, _], + index: Int + ): Boolean + + /** Creates the dictionary value from the chunk of key-value pairs */ + protected def createDictionary(context: Context, schema: Schema.Map[_, _], values: Chunk[(Target, Target)]): Target + + /** The next value to build is a set. If the returned value is None, the builder creates an empty set, + * otherwise it calls startCreatingOneSetElement and finishedCreatingOneSetElement for + * each element with the returned context. + */ + protected def startCreatingSet(context: Context, schema: Schema.Set[_]): Option[Context] + + /** Called before constructing a next set element. The returned context is used for constructing + * that single element. */ + protected def startCreatingOneSetElement(context: Context, schema: Schema.Set[_]): Context + + /** Called after constructing a single set element. The context is the context of the whole set. + * If the returned value is true, a next element will be read otherwise the set is completed and createSet + * is called. */ + protected def finishedCreatingOneSetElement(context: Context, schema: Schema.Set[_], index: Int): Boolean + + /** Creates the set value from the chunk of element values */ + protected def createSet(context: Context, schema: Schema.Set[_], values: Chunk[Target]): Target + + /** The next value to be created is an optional value. If the result is None, the optional value created + * will be None. If it is a context, that context will be used to create the optional value. */ + protected def startCreatingOptional(context: Context, schema: Schema.Optional[_]): Option[Context] + + /** Creates the optional value from the inner value */ + protected def createOptional(context: Context, schema: Schema.Optional[_], value: Option[Target]): Target + + /** The next value to be created is an either value with the given schema. Similarly to optional values, + * this method is responsible for gathering enough information to decide whether the created value will + * be a Left or a Right. The result value represents this, and for each case allows specifying a context + * that will be used to create the inner value. */ + protected def startCreatingEither(context: Context, schema: Schema.Either[_, _]): Either[Context, Context] + + /** Create the either value from an inner value */ + protected def createEither(context: Context, schema: Schema.Either[_, _], value: Either[Target, Target]): Target + + /** The next value to be created is a tuple with the given schema. The returned context is used to + * construct the first element of the tuple. */ + protected def startCreatingTuple(context: Context, schema: Schema.Tuple2[_, _]): Context + + /** Called after finished constructing the first element, before constructing the second. The returned + * context is used to construct the second value. + */ + protected def startReadingSecondTupleElement(context: Context, schema: Schema.Tuple2[_, _]): Context + + /** Creates the tuple from the constructed first and second values */ + protected def createTuple(context: Context, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target + + /** Creates a Dynamic value. If the returned value is None, it indicates that the builder does + * not have any built-in support for Dynamic values, and it will be built using Dynamic's schema. */ + protected def createDynamic(context: Context): Option[Target] + + /** Transforms a value with the given function that can fail. Making this customizable allows encoding the failure + * in Target. + */ + protected def transform(context: Context, value: Target, f: Any => Either[String, Any], schema: Schema[_]): Target + + /** Fail the builder with the given message */ + protected def fail(context: Context, message: String): Target + + /** The initial (top-level) context value */ + protected val initialContext: Context + + /** Create a value of type A with the provided schema using this builder */ + def create[A](schema: Schema[A]): Target = { + var currentSchema: Schema[_] = schema + var result: Option[Target] = None + var stack: List[Target => Unit] = List.empty[Target => Unit] + var contextStack: List[Context] = List(initialContext) + + def finishWith(resultValue: Target): Unit = + if (stack.nonEmpty) { + val head = stack.head + stack = stack.tail + head(resultValue) + } else { + result = Some(resultValue) + } + + def push(f: Target => Unit): Unit = + stack = f :: stack + + def pushContext(s: Context): Unit = + contextStack = s :: contextStack + + def record(record: Schema.Record[_]): Unit = { + val values = ChunkBuilder.make[(Int, Target)](record.fields.size) + + def readField(index: Int): Unit = { + contextStack = contextStack.tail + startReadingField(contextStack.head, record, index) match { + case Some((updatedState, idx)) => + pushContext(updatedState) + currentSchema = record.fields(idx).schema + push { field => + val elem = (idx, field) + values += elem + readField(index + 1) + } + case None => + finishWith(createRecord(contextStack.head, record, values.result())) + } + } + + pushContext(startCreatingRecord(contextStack.head, record)) + readField(0) + } + + def enumCases(casesChunk: Chunk[Schema.Case[_, _]]): Unit = { + val (newState, index) = startCreatingEnum(contextStack.head, casesChunk) + currentSchema = casesChunk(index).schema + pushContext(newState) + push { value => + contextStack = contextStack.tail + finishWith(createEnum(contextStack.head, casesChunk, index, value)) + } + } + + try { + while (result.isEmpty) { + val currentContext = contextStack.head + currentSchema match { + + case l @ Schema.Lazy(_) => + currentSchema = l.schema + + case Schema.Primitive(p, _) => + finishWith(createPrimitive(currentContext, p.asInstanceOf[StandardType[Any]])) + + case s @ Schema.GenericRecord(_, _, _) => + record(s) + + case s @ Schema.Enum1(_, _, _) => + enumCases(s.cases) + + case s @ Schema.Enum2(_, _, _, _) => + enumCases(s.cases) + + case s @ Schema.Enum3(_, _, _, _, _) => + enumCases(s.cases) + + case s @ Schema.Enum4(_, _, _, _, _, _) => + enumCases(s.cases) + + case s @ Schema.Enum5(_, _, _, _, _, _, _) => + enumCases(s.cases) + + case s @ Schema.Enum6(_, _, _, _, _, _, _, _) => + enumCases(s.cases) + + case s @ Schema.Enum7(_, _, _, _, _, _, _, _, _) => + enumCases(s.cases) + + case s @ Schema.Enum8(_, _, _, _, _, _, _, _, _, _) => + enumCases(s.cases) + + case s @ Schema.Enum9(_, _, _, _, _, _, _, _, _, _, _) => + enumCases(s.cases) + + case s @ Schema.Enum10(_, _, _, _, _, _, _, _, _, _, _, _) => + enumCases(s.cases) + + case s @ Schema.Enum11(_, _, _, _, _, _, _, _, _, _, _, _, _) => + enumCases(s.cases) + + case s @ Schema.Enum12( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + enumCases(s.cases) + + case s @ Schema.Enum13( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + enumCases(s.cases) + + case s @ Schema.Enum14( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + enumCases(s.cases) + + case s @ Schema.Enum15( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + enumCases(s.cases) + + case s @ Schema.Enum16( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + enumCases(s.cases) + + case s @ Schema.Enum17( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + enumCases(s.cases) + + case s @ Schema.Enum18( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + enumCases(s.cases) + + case s @ Schema.Enum19( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + enumCases(s.cases) + + case s @ Schema.Enum20( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + enumCases(s.cases) + + case s @ Schema.Enum21( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + enumCases(s.cases) + + case s @ Schema.Enum22( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + enumCases(s.cases) + //scalafmt: { maxColumn = 120 } + + case s @ Schema.EnumN(_, _, _) => + enumCases(s.cases) + + case Schema.Fail(message, _) => + finishWith(fail(currentContext, message)) + + case s @ Schema.Sequence(elementSchema, _, _, _, _) => + val elems = ChunkBuilder.make[Target]() + + def readOne(index: Int): Unit = + push { elem => + elems += elem + + contextStack = contextStack.tail + val continue = finishedCreatingOneSequenceElement(contextStack.head, s, index) + + if (continue) { + currentSchema = elementSchema + pushContext(startCreatingOneSequenceElement(contextStack.head, s)) + readOne(index + 1) + } else { + contextStack = contextStack.tail + finishWith(createSequence(contextStack.head, s, elems.result())) + } + } + + currentSchema = elementSchema + startCreatingSequence(currentContext, s) match { + case Some(startingState) => + pushContext(startingState) + pushContext(startCreatingOneSequenceElement(startingState, s)) + readOne(0) + case None => + finishWith(createSequence(currentContext, s, Chunk.empty)) + } + + case s @ Schema.Map(ks: Schema[k], vs: Schema[v], _) => + val elems = ChunkBuilder.make[(Target, Target)]() + + def readOne(index: Int): Unit = + push { key => + currentSchema = vs + pushContext(startCreatingOneDictionaryValue(currentContext, s)) + + push { value => + val elem = (key, value) + elems += elem + + contextStack = contextStack.tail.tail + val continue = finishedCreatingOneDictionaryElement(contextStack.head, s, index) + + if (continue) { + currentSchema = ks + pushContext(startCreatingOneDictionaryElement(contextStack.head, s)) + readOne(index + 1) + } else { + val state = contextStack.head + contextStack = contextStack.tail + finishWith(createDictionary(state, s, elems.result())) + } + } + } + + startCreatingDictionary(currentContext, s) match { + case Some(startingState) => + currentSchema = ks + pushContext(startingState) + pushContext(startCreatingOneDictionaryElement(startingState, s)) + readOne(0) + case None => + finishWith(createDictionary(contextStack.head, s, Chunk.empty)) + } + + case s @ Schema.Set(as: Schema[a], _) => + val elems = ChunkBuilder.make[Target]() + + def readOne(index: Int): Unit = + push { elem => + elems += elem + + contextStack = contextStack.tail + val continue = finishedCreatingOneSetElement(contextStack.head, s, index) + + if (continue) { + currentSchema = as + pushContext(startCreatingOneSetElement(contextStack.head, s)) + readOne(index + 1) + } else { + val state = contextStack.head + contextStack = contextStack.tail + finishWith(createSet(state, s, elems.result())) + } + } + + startCreatingSet(currentContext, s) match { + case Some(startingState) => + currentSchema = as + pushContext(startingState) + pushContext(startCreatingOneSetElement(startingState, s)) + readOne(0) + case None => + finishWith(createSet(contextStack.head, s, Chunk.empty)) + } + + case s: Schema.Either[l, r] => + startCreatingEither(currentContext, s) match { + case Left(newState) => + currentSchema = s.left + pushContext(newState) + push { value => + contextStack = contextStack.tail + finishWith(createEither(contextStack.head, s, Left(value))) + } + case Right(newState) => + currentSchema = s.right + pushContext(newState) + push { value => + contextStack = contextStack.tail + finishWith(createEither(contextStack.head, s, Right(value))) + } + } + + case s: Schema.Tuple2[a, b] => + currentSchema = s.left + pushContext(startCreatingTuple(currentContext, s)) + push { left => + contextStack = contextStack.tail + val newState = startReadingSecondTupleElement(contextStack.head, s) + currentSchema = s.right + pushContext(newState) + push { right => + contextStack = contextStack.tail + finishWith(createTuple(contextStack.head, s, left, right)) + } + } + + case s: Schema.Optional[a] => + startCreatingOptional(currentContext, s) match { + case Some(newState) => + currentSchema = s.schema + pushContext(newState) + push { value => + contextStack = contextStack.tail + finishWith(createOptional(contextStack.head, s, Some(value))) + } + case None => + finishWith(createOptional(contextStack.head, s, None)) + } + + case s @ Schema.Transform(schema, f, _, _, _) => + currentSchema = schema + push { result => + finishWith(transform(currentContext, result, f.asInstanceOf[Any => Either[String, Any]], s)) + } + + case s @ Schema.CaseClass0(_, _, _) => + finishWith(createRecord(currentContext, s, Chunk.empty)) + + case s @ Schema.CaseClass1(_, _, _, _) => + record(s) + + case s @ Schema.CaseClass2(_, _, _, _, _) => + record(s) + case s @ Schema.CaseClass3(_, _, _, _, _, _) => + record(s) + case s @ Schema.CaseClass4(_, _, _, _, _, _, _) => + record(s) + case s @ Schema.CaseClass5(_, _, _, _, _, _, _, _) => + record(s) + case s @ Schema.CaseClass6(_, _, _, _, _, _, _, _, _) => + record(s) + case s @ Schema.CaseClass7(_, _, _, _, _, _, _, _, _, _) => + record(s) + case s @ Schema.CaseClass8( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass9( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass10( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass11( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass12( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass13( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass14( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass15( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass16( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass17( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass18( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass19( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass20( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass21( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case s @ Schema.CaseClass22( + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _, + _ + ) => + record(s) + case Schema.Dynamic(_) => + createDynamic(currentContext) match { + case Some(value) => + finishWith(value) + case None => + currentSchema = Schema.dynamicValue + } + } + } + } catch { + case NonFatal(reason) => + throw CreateValueFromSchemaError(contextStack.head, reason) + } + result.get + } +} + +object MutableSchemaBasedValueBuilder { + case class CreateValueFromSchemaError[Context](context: Context, cause: Throwable) extends RuntimeException +} + +/** A simpler version of SimpleMutableSchemaBasedValueBuilder without using any Context */ +trait SimpleMutableSchemaBasedValueBuilder[Target] extends MutableSchemaBasedValueBuilder[Target, Unit] { + override protected def createPrimitive(context: Unit, typ: StandardType[_]): Target = + createPrimitive(typ) + protected def createPrimitive(typ: StandardType[_]): Target + + override protected def startCreatingRecord(context: Unit, record: Schema.Record[_]): Unit = + startCreatingRecord(record) + protected def startCreatingRecord(record: Schema.Record[_]): Unit + + override protected def startReadingField(context: Unit, record: Schema.Record[_], index: Int): Option[(Unit, Int)] = + startReadingField(record, index).map(((), _)) + protected def startReadingField(record: Schema.Record[_], index: Int): Option[Int] + + override protected def createRecord(context: Unit, record: Schema.Record[_], values: Chunk[(Int, Target)]): Target = + createRecord(record, values) + protected def createRecord(record: Schema.Record[_], values: Chunk[(Int, Target)]): Target + + override protected def startCreatingEnum(context: Unit, cases: Chunk[Schema.Case[_, _]]): (Unit, Int) = + ((), startCreatingEnum(cases)) + + protected def startCreatingEnum(cases: Chunk[Schema.Case[_, _]]): Int + + override protected def createEnum(context: Unit, cases: Chunk[Schema.Case[_, _]], index: Int, value: Target): Target = + createEnum(cases, index, value) + + protected def createEnum(cases: Chunk[Schema.Case[_, _]], index: Int, value: Target): Target + + override protected def startCreatingSequence(context: Unit, schema: Schema.Sequence[_, _, _]): Option[Unit] = + startCreatingSequence(schema) + + protected def startCreatingSequence(schema: Schema.Sequence[_, _, _]): Option[Unit] + + override protected def startCreatingOneSequenceElement(state: Unit, schema: Schema.Sequence[_, _, _]): Unit = + startReadingOneSequenceElement(schema) + + protected def startReadingOneSequenceElement(schema: Schema.Sequence[_, _, _]): Unit + + override protected def createSequence( + context: Unit, + schema: Schema.Sequence[_, _, _], + values: Chunk[Target] + ): Target = + createSequence(schema, values) + + protected def createSequence(schema: Schema.Sequence[_, _, _], values: Chunk[Target]): Target + + override protected def startCreatingDictionary(context: Unit, schema: Schema.Map[_, _]): Option[Unit] = + startCreatingDictionary(schema) + + protected def startCreatingDictionary(schema: Schema.Map[_, _]): Option[Unit] + + override protected def startCreatingOneDictionaryElement(state: Unit, schema: Schema.Map[_, _]): Unit = + startReadingOneDictionaryKey(schema) + + protected def startReadingOneDictionaryKey(schema: Schema.Map[_, _]): Unit + + override protected def startCreatingOneDictionaryValue(state: Unit, schema: Schema.Map[_, _]): Unit = + startReadingOneDictionaryValue(schema) + + protected def startReadingOneDictionaryValue(schema: Schema.Map[_, _]): Unit + + override protected def createDictionary( + context: Unit, + schema: Schema.Map[_, _], + values: Chunk[(Target, Target)] + ): Target = + createDictionary(schema, values) + + protected def createDictionary(schema: Schema.Map[_, _], values: Chunk[(Target, Target)]): Target + + override protected def startCreatingSet(context: Unit, schema: Schema.Set[_]): Option[Unit] = + startCreatingSet(schema) + + protected def startCreatingSet(schema: Schema.Set[_]): Option[Unit] + + override protected def startCreatingOneSetElement(state: Unit, schema: Schema.Set[_]): Unit = + startReadingOneSetElement(schema) + + protected def startReadingOneSetElement(schema: Schema.Set[_]): Unit + + override protected def createSet(context: Unit, schema: Schema.Set[_], values: Chunk[Target]): Target = + createSet(schema, values) + + protected def createSet(schema: Schema.Set[_], values: Chunk[Target]): Target + + override protected def startCreatingOptional(context: Unit, schema: Schema.Optional[_]): Option[Unit] = + startCreatingOptional(schema) + + protected def startCreatingOptional(schema: Schema.Optional[_]): Option[Unit] + + override protected def createOptional(context: Unit, schema: Schema.Optional[_], value: Option[Target]): Target = + createOptional(schema, value) + + protected def createOptional(schema: Schema.Optional[_], value: Option[Target]): Target + + override protected def startCreatingEither(context: Unit, schema: Schema.Either[_, _]): Either[Unit, Unit] = + startCreatingEither(schema) + protected def startCreatingEither(schema: Schema.Either[_, _]): Either[Unit, Unit] + + override protected def createEither( + context: Unit, + schema: Schema.Either[_, _], + value: Either[Target, Target] + ): Target = + createEither(schema, value) + + protected def createEither(schema: Schema.Either[_, _], value: Either[Target, Target]): Target + + override protected def startCreatingTuple(context: Unit, schema: Schema.Tuple2[_, _]): Unit = + startCreatingTuple(schema) + + protected def startCreatingTuple(schema: Schema.Tuple2[_, _]): Unit + + override protected def startReadingSecondTupleElement(context: Unit, schema: Schema.Tuple2[_, _]): Unit = + startReadingSecondTupleElement(schema) + + protected def startReadingSecondTupleElement(schema: Schema.Tuple2[_, _]): Unit + + override protected def createTuple(context: Unit, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target = + createTuple(schema, left, right) + + protected def createTuple(schema: Schema.Tuple2[_, _], left: Target, right: Target): Target + + override protected def createDynamic(context: Unit): Option[Target] = + createDynamic() + + protected def createDynamic(): Option[Target] + + override protected def transform( + context: Unit, + value: Target, + f: Any => Either[String, Any], + schema: Schema[_] + ): Target = + transform(value, f, schema) + + protected def transform(value: Target, f: Any => Either[String, Any], schema: Schema[_]): Target + + override protected def fail(context: Unit, message: String): Target = + fail(message) + + protected def fail(message: String): Target + + override protected val initialContext: Unit = () +} diff --git a/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueProcessor.scala b/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueProcessor.scala new file mode 100644 index 000000000..e2e3d78f6 --- /dev/null +++ b/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueProcessor.scala @@ -0,0 +1,1431 @@ +package zio.schema + +import scala.annotation.nowarn +import scala.collection.immutable.ListMap + +import zio.{ Chunk, ChunkBuilder } + +/** Base trait for mutable value processors, processing a value with a known schema. An example + * is protocol encoders. + * + * The implementation is stack safe and consists of invocations of a series of processXYZ methods, as well + * as built-in support for a context value which is handled in a stacked way. + * + * Maintaining any global state (per process) such as stream writers etc. is the responsibility of the implementation class. + * + * The Target type parameter is the base type for the process function's output value. In case the process is + * built entirely using side effects (such as calls to a mutable writer interface) this type can be Unit. + * + * The Context type parameter is the use-case specific context type which is passed for each process invocation, and + * can be manipulated before each process call achieving a local state. + */ +trait MutableSchemaBasedValueProcessor[Target, Context] { + + /** Process a primitive value */ + protected def processPrimitive(context: Context, value: Any, typ: StandardType[Any]): Target + + /** Called before processing a record (before calling processXYZ for the record's fields) */ + @nowarn protected def startProcessingRecord(context: Context, schema: Schema.Record[_]): Unit = {} + + /** Process a record in the given context with the given schema, using the already processed values of its fields. */ + protected def processRecord(context: Context, schema: Schema.Record[_], value: ListMap[String, Target]): Target + + /** Called before processing an enum */ + @nowarn protected def startProcessingEnum(context: Context, schema: Schema.Enum[_]): Unit = {} + + /** Process an enum in the given context with the given schema using the processed constructor value and it's name */ + protected def processEnum(context: Context, schema: Schema.Enum[_], tuple: (String, Target)): Target + + /** Called before processing a sequence */ + @nowarn protected def startProcessingSequence(context: Context, schema: Schema.Sequence[_, _, _], size: Int): Unit = {} + + /** Process a sequence using its already processed elements */ + protected def processSequence(context: Context, schema: Schema.Sequence[_, _, _], value: Chunk[Target]): Target + + /** Called before processing a dictionary */ + @nowarn protected def startProcessingDictionary(context: Context, schema: Schema.Map[_, _], size: Int): Unit = {} + + /*** Process a dictionary using its already processed key-value pairs */ + protected def processDictionary(context: Context, schema: Schema.Map[_, _], value: Chunk[(Target, Target)]): Target + + /** Called before processing a set */ + @nowarn protected def startProcessingSet(context: Context, schema: Schema.Set[_], size: Int): Unit = {} + + /** Process a set using its already processed elements */ + protected def processSet(context: Context, schema: Schema.Set[_], value: Set[Target]): Target + + /** Called before processing and either value */ + @nowarn protected def startProcessingEither(context: Context, schema: Schema.Either[_, _]): Unit = {} + + /** Process an either value using its already processed left or right value */ + protected def processEither(context: Context, schema: Schema.Either[_, _], value: Either[Target, Target]): Target + + /** Called before processing an option value */ + @nowarn protected def startProcessingOption(context: Context, schema: Schema.Optional[_]): Unit = {} + + /** Process an optional value using its already processed inner value, or None */ + protected def processOption(context: Context, schema: Schema.Optional[_], value: Option[Target]): Target + + /** Called before processing a pair of values */ + @nowarn protected def startProcessingTuple(context: Context, schema: Schema.Tuple2[_, _]): Unit = {} + + /** Process a tuple using its already processed left and right values */ + protected def processTuple(context: Context, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target + + /** Process a dynamic value. If the result is None it indicates that the processor has no + * built-in support for dynamic values, and the Dynamic value's schema should be used instead. */ + protected def processDynamic(context: Context, value: DynamicValue): Option[Target] + + /** Fails the processing */ + protected def fail(context: Context, message: String): Target + + /** The initial (top-level) context value */ + protected val initialContext: Context + + /** Gets the context for a record's given field within the parent context */ + protected def contextForRecordField(context: Context, index: Int, field: Schema.Field[_, _]): Context + + /** Gets the context for a tuple's given field within the parent context */ + protected def contextForTuple(context: Context, index: Int): Context + + /** Gets the context for an enum's given constructor within the parent context */ + protected def contextForEnumConstructor(context: Context, index: Int, c: Schema.Case[_, _]): Context + + /** Gets the context for an either's left or right value within the parent context */ + protected def contextForEither(context: Context, e: Either[Unit, Unit]): Context + + /** Gets the context for an option's inner value within the parent context */ + protected def contextForOption(context: Context, o: Option[Unit]): Context + + /** Gets the context for a sequence's given element within the parent context */ + protected def contextForSequence(context: Context, schema: Schema.Sequence[_, _, _], index: Int): Context + + /** Gets the context for a dictionary's given element within the parent context */ + protected def contextForMap(context: Context, schema: Schema.Map[_, _], index: Int): Context + + /** Gets the context for a set's given element within the parent context */ + protected def contextForSet(context: Context, schema: Schema.Set[_], index: Int): Context + + /** Process a value based on it's schema */ + def process[A](schema: Schema[A], value: A): Target = { + var currentSchema: Schema[_] = schema + var currentValue: Any = value + var result: Option[Target] = None + var stack: List[Target => Unit] = List.empty[Target => Unit] + var contextStack: List[Context] = List(initialContext) + + def push(f: Target => Unit): Unit = + stack = f :: stack + + def pushContext(s: Context): Unit = + contextStack = s :: contextStack + + def finishWith(resultValue: Target): Unit = + if (stack.nonEmpty) { + val head = stack.head + stack = stack.tail + head(resultValue) + } else { + result = Some(resultValue) + } + + def fields(s: Schema.Record[_], record: Any, fs: Schema.Field[_, _]*): Unit = { + val values = ChunkBuilder.make[Target](fs.size) + + def processNext(index: Int, remaining: List[Schema.Field[_, _]]): Unit = + remaining match { + case next :: _ => + currentSchema = next.schema + currentValue = next.asInstanceOf[Schema.Field[Any, Any]].get(record) + pushContext(contextForRecordField(contextStack.head, index, next)) + push(processField(index, remaining, _)) + case Nil => + finishWith( + processRecord( + contextStack.head, + s, + fs.map(_.name).zip(values.result()).foldLeft(ListMap.empty[String, Target]) { + case (lm, pair) => + lm.updated(pair._1, pair._2) + } + ) + ) + } + + def processField(index: Int, currentStructure: List[Schema.Field[_, _]], fieldResult: Target): Unit = { + contextStack = contextStack.tail + values += fieldResult + val remaining = currentStructure.tail + processNext(index + 1, remaining) + } + + startProcessingRecord(contextStack.head, s) + processNext(0, fs.toList) + } + + def enumCases(s: Schema.Enum[_], cs: Schema.Case[_, _]*): Unit = { + startProcessingEnum(contextStack.head, s) + + var found = false + val it = cs.iterator + var index = 0 + while (!found && it.hasNext) { + val c = it.next().asInstanceOf[Schema.Case[Any, Any]] + c.deconstructOption(currentValue) match { + case Some(v) => + currentValue = v + currentSchema = c.schema + pushContext(contextForEnumConstructor(contextStack.head, index, c)) + push { dv => + contextStack = contextStack.tail + finishWith(processEnum(contextStack.head, s, c.id -> dv)) + } + found = true + case None => + index = index + 1 + } + } + + if (!found) { + //This should never happen unless someone manually builds an Enum and doesn't include all cases + finishWith(fail(contextStack.head, "Invalid enum constructor")) + } + } + + while (result.isEmpty) { + val currentContext = contextStack.head + + currentSchema match { + + case l @ Schema.Lazy(_) => + currentSchema = l.schema + + case Schema.Primitive(p, _) => + finishWith(processPrimitive(currentContext, currentValue, p.asInstanceOf[StandardType[Any]])) + + case s @ Schema.GenericRecord(_, structure, _) => + val map = currentValue.asInstanceOf[ListMap[String, _]] + val structureChunk = structure.toChunk + val values = ChunkBuilder.make[Target](structureChunk.size) + + def processNext(index: Int, remaining: List[Schema.Field[ListMap[String, _], _]]): Unit = + remaining match { + case next :: _ => + currentSchema = next.schema + currentValue = map(next.name) + pushContext(contextForRecordField(currentContext, index, next)) + push(processField(index, remaining, _)) + case Nil => + finishWith( + processRecord( + currentContext, + s, + structureChunk.map(_.name).zip(values.result()).foldLeft(ListMap.empty[String, Target]) { + case (lm, pair) => + lm.updated(pair._1, pair._2) + } + ) + ) + } + + def processField( + index: Int, + currentStructure: List[Schema.Field[ListMap[String, _], _]], + fieldResult: Target + ): Unit = { + contextStack = contextStack.tail + values += fieldResult + val remaining = currentStructure.tail + processNext(index + 1, remaining) + } + + startProcessingRecord(currentContext, s) + processNext(0, structureChunk.toList) + + case s @ Schema.Enum1(_, case1, _) => + enumCases(s, case1) + + case s @ Schema.Enum2(_, case1, case2, _) => + enumCases(s, case1, case2) + + case s @ Schema.Enum3(_, case1, case2, case3, _) => + enumCases(s, case1, case2, case3) + + case s @ Schema.Enum4(_, case1, case2, case3, case4, _) => + enumCases(s, case1, case2, case3, case4) + + case s @ Schema.Enum5(_, case1, case2, case3, case4, case5, _) => + enumCases(s, case1, case2, case3, case4, case5) + + case s @ Schema.Enum6(_, case1, case2, case3, case4, case5, case6, _) => + enumCases(s, case1, case2, case3, case4, case5, case6) + + case s @ Schema.Enum7(_, case1, case2, case3, case4, case5, case6, case7, _) => + enumCases(s, case1, case2, case3, case4, case5, case6, case7) + + case s @ Schema.Enum8(_, case1, case2, case3, case4, case5, case6, case7, case8, _) => + enumCases(s, case1, case2, case3, case4, case5, case6, case7, case8) + + case s @ Schema.Enum9(_, case1, case2, case3, case4, case5, case6, case7, case8, case9, _) => + enumCases(s, case1, case2, case3, case4, case5, case6, case7, case8, case9) + + case s @ Schema.Enum10(_, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, _) => + enumCases(s, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10) + + case s @ Schema.Enum11(_, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, _) => + enumCases(s, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11) + + case s @ Schema.Enum12( + _, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + _ + ) => + enumCases(s, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12) + + case s @ Schema.Enum13( + _, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + _ + ) => + enumCases( + s, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13 + ) + + case s @ Schema.Enum14( + _, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + _ + ) => + enumCases( + s, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14 + ) + + case s @ Schema.Enum15( + _, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + _ + ) => + enumCases( + s, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15 + ) + + case s @ Schema.Enum16( + _, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + _ + ) => + enumCases( + s, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16 + ) + + case s @ Schema.Enum17( + _, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17, + _ + ) => + enumCases( + s, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17 + ) + + case s @ Schema.Enum18( + _, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17, + case18, + _ + ) => + enumCases( + s, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17, + case18 + ) + + case s @ Schema.Enum19( + _, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17, + case18, + case19, + _ + ) => + enumCases( + s, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17, + case18, + case19 + ) + + case s @ Schema.Enum20( + _, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17, + case18, + case19, + case20, + _ + ) => + enumCases( + s, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17, + case18, + case19, + case20 + ) + + case s @ Schema.Enum21( + _, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17, + case18, + case19, + case20, + case21, + _ + ) => + enumCases( + s, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17, + case18, + case19, + case20, + case21 + ) + + case s @ Schema.Enum22( + _, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17, + case18, + case19, + case20, + case21, + case22, + _ + ) => + enumCases( + s, + case1, + case2, + case3, + case4, + case5, + case6, + case7, + case8, + case9, + case10, + case11, + case12, + case13, + case14, + case15, + case16, + case17, + case18, + case19, + case20, + case21, + case22 + ) + //scalafmt: { maxColumn = 120 } + + case s @ Schema.EnumN(_, cases, _) => + enumCases(s, cases.toSeq: _*) + + case Schema.Fail(message, _) => + finishWith(fail(currentContext, message)) + + case s @ Schema.Sequence(schema, _, toChunk, _, _) => + val inputChunk = toChunk.asInstanceOf[Any => Chunk[Any]](currentValue) + val resultChunk = ChunkBuilder.make[Target](inputChunk.size) + + def processNext(inputIdx: Int): Unit = { + contextStack = contextStack.tail + if (inputIdx == inputChunk.size) { + finishWith(processSequence(currentContext, s, resultChunk.result())) + } else { + currentSchema = schema + currentValue = inputChunk(inputIdx) + pushContext(contextForSequence(currentContext, s, inputIdx)) + push { dv => + resultChunk += dv + processNext(inputIdx + 1) + } + } + } + + startProcessingSequence(currentContext, s, inputChunk.size) + pushContext(contextForSequence(currentContext, s, 0)) + processNext(0) + + case s @ Schema.Map(ks: Schema[k], vs: Schema[v], _) => + val inputChunk = Chunk.fromIterable(currentValue.asInstanceOf[Map[k, v]]) + val resultChunk = ChunkBuilder.make[(Target, Target)](inputChunk.size) + + def processNext(inputIdx: Int): Unit = + if (inputIdx == inputChunk.size) { + finishWith(processDictionary(currentContext, s, resultChunk.result())) + } else { + currentSchema = ks + val currentTuple = inputChunk(inputIdx) + currentValue = currentTuple._1 + + pushContext(contextForMap(currentContext, s, inputIdx)) + push { (a: Target) => + contextStack = contextStack.tail + + currentSchema = vs + currentValue = currentTuple._2 + pushContext(contextForMap(currentContext, s, inputIdx)) + push { (b: Target) => + contextStack = contextStack.tail + val pair = (a, b) + resultChunk += pair + processNext(inputIdx + 1) + } + } + } + + startProcessingDictionary(currentContext, s, inputChunk.size) + processNext(0) + + case s @ Schema.Set(as: Schema[a], _) => + val inputChunk = Chunk.fromIterable(currentValue.asInstanceOf[Set[a]]) + val resultChunk = ChunkBuilder.make[Target](inputChunk.size) + + def processNext(inputIdx: Int): Unit = { + contextStack = contextStack.tail + if (inputIdx == inputChunk.size) { + finishWith(processSet(currentContext, s, resultChunk.result().toSet)) + } else { + currentSchema = as + currentValue = inputChunk(inputIdx) + pushContext(contextForSet(currentContext, s, inputIdx)) + push { dv => + resultChunk += dv + processNext(inputIdx + 1) + } + } + } + + startProcessingSet(currentContext, s, inputChunk.size) + pushContext(contextForSet(currentContext, s, 0)) + processNext(0) + + case s: Schema.Either[l, r] => + startProcessingEither(currentContext, s) + currentValue.asInstanceOf[Either[l, r]] match { + case Left(value: l) => + currentValue = value + currentSchema = s.left + pushContext(contextForEither(currentContext, Left(()))) + push { dyn => + contextStack = contextStack.tail + finishWith(processEither(currentContext, s, Left(dyn))) + } + case Right(value: r) => + currentValue = value + currentSchema = s.right + pushContext(contextForEither(currentContext, Right(()))) + push { dyn => + contextStack = contextStack.tail + finishWith(processEither(currentContext, s, Right(dyn))) + } + } + + case s: Schema.Tuple2[a, b] => + startProcessingTuple(currentContext, s) + val (a: a, b: b) = currentValue.asInstanceOf[(a, b)] + currentValue = a + currentSchema = s.left + pushContext(contextForTuple(currentContext, 1)) + push { dynA => + currentValue = b + currentSchema = s.right + contextStack = contextStack.tail + pushContext(contextForTuple(currentContext, 2)) + push { dynB => + contextStack = contextStack.tail + finishWith(processTuple(currentContext, s, dynA, dynB)) + } + } + + case s: Schema.Optional[a] => + startProcessingOption(currentContext, s) + currentValue.asInstanceOf[Option[a]] match { + case Some(value: a) => + currentValue = value + currentSchema = s.schema + pushContext(contextForOption(currentContext, Some(()))) + push { dyn => + contextStack = contextStack.tail + finishWith(processOption(currentContext, s, Some(dyn))) + } + case None => + finishWith(processOption(currentContext, s, None)) + } + + case Schema.Transform(schema, _, g, _, _) => + g.asInstanceOf[Any => Either[String, Any]](currentValue) match { + case Left(message) => + finishWith(fail(currentContext, message)) + case Right(a) => + currentValue = a + currentSchema = schema + } + + case s @ Schema.CaseClass0(_, _, _) => + finishWith(processRecord(currentContext, s, ListMap())) + + case s @ Schema.CaseClass1(_, f, _, _) => + fields(s, currentValue, f) + + case s @ Schema.CaseClass2(_, f1, f2, _, _) => + fields(s, currentValue, f1, f2) + case s @ Schema.CaseClass3(_, f1, f2, f3, _, _) => + fields(s, currentValue, f1, f2, f3) + case s @ Schema.CaseClass4(_, f1, f2, f3, f4, _, _) => + fields(s, currentValue, f1, f2, f3, f4) + case s @ Schema.CaseClass5(_, f1, f2, f3, f4, f5, _, _) => + fields(s, currentValue, f1, f2, f3, f4, f5) + case s @ Schema.CaseClass6(_, f1, f2, f3, f4, f5, f6, _, _) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6) + case s @ Schema.CaseClass7(_, f1, f2, f3, f4, f5, f6, f7, _, _) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6, f7) + case s @ Schema.CaseClass8( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + _, + _ + ) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6, f7, f8) + case s @ Schema.CaseClass9( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + _, + _ + ) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9) + case s @ Schema.CaseClass10( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + _, + _ + ) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) + case s @ Schema.CaseClass11( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + _, + _ + ) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11) + case s @ Schema.CaseClass12( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + _, + _ + ) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12) + case s @ Schema.CaseClass13( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + _, + _ + ) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13) + case s @ Schema.CaseClass14( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + _, + _ + ) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14) + case s @ Schema.CaseClass15( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + _, + _ + ) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15) + case s @ Schema.CaseClass16( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + _, + _ + ) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16) + case s @ Schema.CaseClass17( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + _, + _ + ) => + fields(s, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17) + case s @ Schema.CaseClass18( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + _, + _ + ) => + fields( + s, + currentValue, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18 + ) + case s @ Schema.CaseClass19( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + _, + _ + ) => + fields( + s, + currentValue, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19 + ) + case s @ Schema.CaseClass20( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + _, + _ + ) => + fields( + s, + currentValue, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20 + ) + case s @ Schema.CaseClass21( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + _, + _ + ) => + fields( + s, + currentValue, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21 + ) + case s @ Schema.CaseClass22( + _, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + _, + _ + ) => + fields( + s, + currentValue, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22 + ) + case Schema.Dynamic(_) => + processDynamic(currentContext, currentValue.asInstanceOf[DynamicValue]) match { + case Some(target) => finishWith(target) + case None => + currentSchema = Schema.dynamicValue + } + } + } + result.get + } +} + +/** A simpler version of MutableSchemaBasedValueProcessor without using any Context */ +trait SimpleMutableSchemaBasedValueProcessor[Target] extends MutableSchemaBasedValueProcessor[Target, Unit] { + + protected def processPrimitive(value: Any, typ: StandardType[Any]): Target + + protected def processRecord(schema: Schema.Record[_], value: ListMap[String, Target]): Target + + protected def processEnum(schema: Schema.Enum[_], tuple: (String, Target)): Target + + protected def processSequence(schema: Schema.Sequence[_, _, _], value: Chunk[Target]): Target + + protected def processDictionary(schema: Schema.Map[_, _], value: Chunk[(Target, Target)]): Target + + protected def processSet(schema: Schema.Set[_], value: Set[Target]): Target + + protected def processEither(schema: Schema.Either[_, _], value: Either[Target, Target]): Target + + protected def processOption(schema: Schema.Optional[_], value: Option[Target]): Target + + protected def processTuple(schema: Schema.Tuple2[_, _], left: Target, right: Target): Target + + protected def processDynamic(value: DynamicValue): Option[Target] + + protected def fail(message: String): Target + + override protected def processPrimitive(context: Unit, value: Any, typ: StandardType[Any]): Target = + processPrimitive(value, typ) + + override protected def processRecord( + context: Unit, + schema: Schema.Record[_], + value: ListMap[String, Target] + ): Target = + processRecord(schema, value) + + override protected def processEnum(context: Unit, schema: Schema.Enum[_], tuple: (String, Target)): Target = + processEnum(schema, tuple) + + override protected def processSequence( + context: Unit, + schema: Schema.Sequence[_, _, _], + value: Chunk[Target] + ): Target = + processSequence(schema, value) + + override protected def processDictionary( + context: Unit, + schema: Schema.Map[_, _], + value: Chunk[(Target, Target)] + ): Target = + processDictionary(schema, value) + + override protected def processSet(context: Unit, schema: Schema.Set[_], value: Set[Target]): Target = + processSet(schema, value) + + override protected def processEither( + context: Unit, + schema: Schema.Either[_, _], + value: Either[Target, Target] + ): Target = + processEither(schema, value) + + override protected def processOption(context: Unit, schema: Schema.Optional[_], value: Option[Target]): Target = + processOption(schema, value) + + override protected def processTuple(context: Unit, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target = + processTuple(schema, left, right) + + override protected def fail(context: Unit, message: String): Target = + fail(message) + + override protected def processDynamic(context: Unit, value: DynamicValue): Option[Target] = + processDynamic(value) + + override protected val initialContext: Unit = () + + override protected def contextForRecordField(context: Unit, index: Int, field: Schema.Field[_, _]): Unit = + () + + override protected def contextForEnumConstructor(context: Unit, index: Int, c: Schema.Case[_, _]): Unit = + () + + override protected def contextForEither(context: Unit, e: Either[Unit, Unit]): Unit = + () + + override protected def contextForOption(context: Unit, o: Option[Unit]): Unit = + () + + override protected def contextForTuple(context: Unit, index: Int): Unit = + () + + override protected def contextForSequence(context: Unit, schema: Schema.Sequence[_, _, _], index: Int): Unit = + () + + override protected def contextForMap(context: Unit, schema: Schema.Map[_, _], index: Int): Unit = + () + + override protected def contextForSet(context: Unit, schema: Schema.Set[_], index: Int): Unit = + () +} diff --git a/zio-schema/shared/src/main/scala/zio/schema/codec/DecodeError.scala b/zio-schema/shared/src/main/scala/zio/schema/codec/DecodeError.scala index aae039250..bede3e0d9 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/codec/DecodeError.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/codec/DecodeError.scala @@ -37,6 +37,8 @@ object DecodeError { final case class ReadError(cause: Cause[Any], message: String) extends DecodeError + final case class ReadErrorWithPath(path: Chunk[String], cause: Cause[Any], message: String) extends DecodeError + final case class ValidationError(validation: Validation[_], field: Field[_, _], message: String) extends DecodeError final case class ExtraFields(fieldName: String, message: String) extends DecodeError