From e528dbc2e5a17f9d0a61f636467d3435d0b5aced Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Thu, 27 Oct 2022 11:48:12 -0400 Subject: [PATCH 01/18] Make DynamicValue.fromSchemaAndValue stack safe --- .sbtopts | 2 - .../scala-2/zio/schema/DynamicValueSpec.scala | 163 +- .../test/scala-2/zio/schema/SchemaGen.scala | 18 + .../zio/schema/codec/JsonCodecSpec.scala | 6 + .../zio/schema/codec/ProtobufCodecSpec.scala | 9 + .../zio/schema/codec/ThriftCodecSpec.scala | 9 + .../main/scala/zio/schema/DynamicValue.scala | 2233 +++++------------ 7 files changed, 790 insertions(+), 1650 deletions(-) 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 6ef170234..cf4e5975a 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 @@ -555,6 +555,12 @@ object JsonCodecSpec extends ZIOSpecDefault { assertEncodesThenDecodes(schema, value) } }, + test("deep recursive data type") { + check(SchemaGen.anyDeepRecursiveTypeAndValue) { + case (schema, value) => + assertEncodesThenDecodes(schema, value) + } + }, suite("dynamic")( test("dynamic int") { check( 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 613627971..2baf57030 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 @@ -582,6 +582,15 @@ 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)) + } } ), suite("Should successfully decode")( 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 e2952aabc..a9270c676 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 @@ -661,6 +661,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)) + } + }, 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 3bb05e5e9..32313d81c 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala @@ -4,11 +4,11 @@ import java.math.{ BigDecimal, BigInteger } import java.time._ import java.time.format.DateTimeFormatter import java.util.UUID - import scala.collection.immutable.ListMap - import zio.schema.meta.{ MetaSchema, Migration } -import zio.{ Chunk, Unsafe } +import zio.{ Chunk, ChunkBuilder, Unsafe } + +import scala.collection.mutable sealed trait DynamicValue { self => @@ -119,1609 +119,692 @@ sealed trait DynamicValue { object DynamicValue { //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 - } + def fromSchemaAndValue[A](schema: Schema[A], value: A): DynamicValue = { + var currentSchema: Schema[_] = schema + var currentValue: Any = value + var result: DynamicValue = null + val stack: mutable.Stack[DynamicValue => Unit] = mutable.Stack.empty + + def finishWith(resultValue: DynamicValue): Unit = + if (stack.nonEmpty) { + stack.pop()(resultValue) + } else { + result = resultValue + } - 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 + def fields(id: TypeId, record: Any, fs: Schema.Field[_, _]*): Unit = { + val values = ChunkBuilder.make[DynamicValue](fs.size) + + def processNext(remaining: List[Schema.Field[_, _]]): Unit = + remaining match { + case next :: _ => + currentSchema = next.schema + currentValue = next.asInstanceOf[Schema.Field[Any, Any]].get(record) + stack.push(processField(remaining, _)) + case Nil => + finishWith( + DynamicValue.Record( + id, + ListMap.from(fs.map(_.name).zip(values.result())) + ) + ) } - 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 - } + def processField(currentStructure: List[Schema.Field[_, _]], fieldResult: DynamicValue): Unit = { + values += fieldResult + val remaining = currentStructure.tail + processNext(remaining) + } - 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 - } + processNext(fs.toList) + } - 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 + def enumCases(id: TypeId, cs: Schema.Case[_, _]*): Unit = { + var found = false + val it = cs.iterator + 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 + stack.push(dv => finishWith(DynamicValue.Enumeration(id, c.id -> dv))) + found = true + case None => } + } - 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 - } + if (!found) { + //This should never happen unless someone manually builds an Enum and doesn't include all cases + finishWith(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 - } + while (result eq null) { + currentSchema match { + + case l @ Schema.Lazy(_) => + currentSchema = l.schema + + case Schema.Primitive(p, _) => + finishWith(DynamicValue.Primitive(currentValue, p.asInstanceOf[StandardType[Any]])) + + case Schema.GenericRecord(id, structure, _) => + val map = currentValue.asInstanceOf[ListMap[String, _]] + val structureChunk = structure.toChunk + val values = ChunkBuilder.make[DynamicValue](structureChunk.size) + + def processNext(remaining: List[Schema.Field[ListMap[String, _], _]]): Unit = + remaining match { + case next :: _ => + currentSchema = next.schema + currentValue = map(next.name) + stack.push(processField(remaining, _)) + case Nil => + finishWith( + DynamicValue.Record( + id, + ListMap.from(structureChunk.map(_.name).zip(values.result())) + ) + ) + } + + def processField(currentStructure: List[Schema.Field[ListMap[String, _], _]], fieldResult: DynamicValue): Unit = { + values += fieldResult + val remaining = currentStructure.tail + processNext(remaining) + } - 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 - } + processNext(structureChunk.toList) - 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.Enum1(id, case1, _) => + enumCases(id, case1) - 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.Enum2(id, case1, case2, _) => + enumCases(id, case1, case2) - 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.Enum3(id, case1, case2, case3, _) => + enumCases(id, case1, case2, case3) - 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.Enum4(id, case1, case2, case3, case4, _) => + enumCases(id, case1, case2, case3, case4) - 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.Enum5(id, case1, case2, case3, case4, case5, _) => + enumCases(id, case1, case2, case3, case4, case5) - 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.Enum6(id, case1, case2, case3, case4, case5, case6, _) => + enumCases(id, case1, case2, case3, case4, case5, case6) - 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.Enum7(id, case1, case2, case3, case4, case5, case6, case7, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7) - case Schema.Fail(message, _) => DynamicValue.Error(message) + case Schema.Enum8(id, case1, case2, case3, case4, case5, case6, case7, case8, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8) - case Schema.Sequence(schema, _, toChunk, _, _) => - DynamicValue.Sequence(toChunk(value).map(fromSchemaAndValue(schema, _))) + case Schema.Enum9(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9) - 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.Enum10(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10) - case Schema.Set(as: Schema[a], _) => - DynamicValue.SetValue(value.asInstanceOf[Set[a]].map(fromSchemaAndValue(as, _))) + case Schema.Enum11(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11) - 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)) - } + case Schema.Enum12(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12) - 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)) + case Schema.Enum13(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13) - case schema: Schema.Optional[a] => - value.asInstanceOf[Option[a]] match { - case Some(value: a) => DynamicValue.SomeValue(fromSchemaAndValue(schema.schema, value)) - case None => DynamicValue.NoneValue - } + case Schema.Enum14(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14) + + case Schema.Enum15(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15) + + case Schema.Enum16(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16) + + case Schema.Enum17(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17) + + case Schema.Enum18(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18) + + case Schema.Enum19(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19) + + case Schema.Enum20(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, case20, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, case20) + + 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, _) => + enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, case20, case21) + + 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, _) => + enumCases(id, 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 Schema.EnumN(id, cases, _) => + enumCases(id, cases.toSeq: _*) + + case Schema.Fail(message, _) => + finishWith(DynamicValue.Error(message)) + + case Schema.Sequence(schema, _, toChunk, _, _) => + val inputChunk = toChunk.asInstanceOf[Any => Chunk[Any]](currentValue) + val resultChunk = ChunkBuilder.make[DynamicValue](inputChunk.size) + + def processNext(inputIdx: Int): Unit = + if (inputIdx == inputChunk.size) + finishWith(DynamicValue.Sequence(resultChunk.result())) + else { + currentSchema = schema + currentValue = inputChunk(inputIdx) + stack.push { dv => + resultChunk += dv + processNext(inputIdx + 1) + } + } + + processNext(0) + + case Schema.Map(ks: Schema[k], vs: Schema[v], _) => + val inputChunk = Chunk.fromIterable(currentValue.asInstanceOf[Map[k, v]]) + val resultChunk = ChunkBuilder.make[(DynamicValue, DynamicValue)](inputChunk.size) + val kvSchema = Schema.tuple2(ks, vs) + + def processNext(inputIdx: Int): Unit = + if (inputIdx == inputChunk.size) + finishWith(DynamicValue.Dictionary(resultChunk.result())) + else { + currentSchema = kvSchema + currentValue = inputChunk(inputIdx) + stack.push { + case DynamicValue.Tuple(a, b) => + val pair = (a, b) + resultChunk += pair + processNext(inputIdx + 1) + case _ => + finishWith(DynamicValue.Error("Unexpected dynamic value when converting a Map")) + } + } + + processNext(0) + + case Schema.Set(as: Schema[a], _) => + val inputChunk = Chunk.fromIterable(currentValue.asInstanceOf[Set[a]]) + val resultChunk = ChunkBuilder.make[DynamicValue](inputChunk.size) + + def processNext(inputIdx: Int): Unit = + if (inputIdx == inputChunk.size) + finishWith(DynamicValue.SetValue(resultChunk.result().toSet)) + else { + currentSchema = as + currentValue = inputChunk(inputIdx) + stack.push { dv => + resultChunk += dv + processNext(inputIdx + 1) + } + } + + processNext(0) + + case schema: Schema.Either[l, r] => + currentValue.asInstanceOf[Either[l, r]] match { + case Left(value: l) => + currentValue = value + currentSchema = schema.left + stack.push(dyn => finishWith(DynamicValue.LeftValue(dyn))) + case Right(value: r) => + currentValue = value + currentSchema = schema.right + stack.push(dyn => finishWith(DynamicValue.RightValue(dyn))) + } - case Schema.Transform(schema, _, g, _, _) => - g(value) match { - case Left(message) => DynamicValue.Error(message) - case Right(a) => fromSchemaAndValue(schema, a) - } + case schema: Schema.Tuple2[a, b] => + val (a: a, b: b) = currentValue.asInstanceOf[(a, b)] + currentValue = a + currentSchema = schema.left + stack.push { dynA => + currentValue = b + currentSchema = schema.right + stack.push { dynB => + finishWith(DynamicValue.Tuple(dynA, dynB)) + } + } - case Schema.CaseClass0(id, _, _) => - DynamicValue.Record(id, ListMap()) + case schema: Schema.Optional[a] => + currentValue.asInstanceOf[Option[a]] match { + case Some(value: a) => + currentValue = value + currentSchema = schema.schema + stack.push { dyn => + finishWith(DynamicValue.SomeValue(dyn)) + } + case None => + finishWith(DynamicValue.NoneValue) + } - case Schema.CaseClass1(id, f, _, _) => - DynamicValue.Record(id, ListMap(f.name -> fromSchemaAndValue(f.schema, f.get(value)))) + case Schema.Transform(schema, _, g, _, _) => + g.asInstanceOf[Any => Either[String, Any]](currentValue) match { + case Left(message) => + finishWith(DynamicValue.Error(message)) + case Right(a) => + currentValue = a + currentSchema = schema + } - 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.CaseClass0(id, _, _) => + finishWith(DynamicValue.Record(id, ListMap())) + + case Schema.CaseClass1(id, f, _, _) => + fields(id, currentValue, f) + + case Schema.CaseClass2(id, f1, f2, _, _) => + fields(id, currentValue, f1, f2) + case Schema.CaseClass3(id, f1, f2, f3, _, _) => + fields(id, currentValue, f1, f2, f3) + case Schema.CaseClass4(id, f1, f2, f3, f4, _, _) => + fields(id, currentValue, f1, f2, f3, f4) + case Schema.CaseClass5(id, f1, f2, f3, f4, f5, _, _) => + fields(id, currentValue, f1, f2, f3, f4, f5) + case Schema.CaseClass6(id, f1, f2, f3, f4, f5, f6, _, _) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6) + case Schema.CaseClass7(id, f1, f2, f3, f4, f5, f6, f7, _, _) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7) + case Schema.CaseClass8( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8) + case Schema.CaseClass9( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9) + case Schema.CaseClass10( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) + case Schema.CaseClass11( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11) + case Schema.CaseClass12( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12) + case Schema.CaseClass13( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13) + case Schema.CaseClass14( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14) + case Schema.CaseClass15( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15) + case Schema.CaseClass16( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16) + case Schema.CaseClass17( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17) + case Schema.CaseClass18( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18) + case Schema.CaseClass19( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + _, + _ + ) => + fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19) + case Schema.CaseClass20( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + _, + _ + ) => + fields( + id, + currentValue, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20 ) - ) - 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.CaseClass21( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + _, + _ + ) => + fields( + id, + currentValue, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21 ) - ) - 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.CaseClass22( + id, + f1, + f2, + f3, + f4, + f5, + f6, + f7, + f8, + f9, + f10, + f11, + f12, + f13, + f14, + f15, + f16, + f17, + f18, + f19, + f20, + f21, + f22, + _, + _ + ) => + fields( + id, + 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.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 + case Schema.Dynamic(_) => + finishWith(currentValue.asInstanceOf[DynamicValue]) + } } + result + } def decodeStructure( values: ListMap[String, DynamicValue], From a8ba213d33e85f4bb4ef67ebc1c89bddd831b8f2 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Thu, 27 Oct 2022 12:03:46 -0400 Subject: [PATCH 02/18] ScalaFix --- .../shared/src/main/scala/zio/schema/DynamicValue.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 32313d81c..ef724b1c0 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala @@ -4,12 +4,13 @@ import java.math.{ BigDecimal, BigInteger } import java.time._ import java.time.format.DateTimeFormatter import java.util.UUID + import scala.collection.immutable.ListMap +import scala.collection.mutable + import zio.schema.meta.{ MetaSchema, Migration } import zio.{ Chunk, ChunkBuilder, Unsafe } -import scala.collection.mutable - sealed trait DynamicValue { self => From 9592330e77287486ff1dca842b2a0e48c6177f66 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Thu, 27 Oct 2022 12:19:06 -0400 Subject: [PATCH 03/18] 2.12 fix --- .../src/main/scala/zio/schema/DynamicValue.scala | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) 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 ef724b1c0..64c585acf 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala @@ -124,7 +124,7 @@ object DynamicValue { var currentSchema: Schema[_] = schema var currentValue: Any = value var result: DynamicValue = null - val stack: mutable.Stack[DynamicValue => Unit] = mutable.Stack.empty + val stack: mutable.Stack[DynamicValue => Unit] = mutable.Stack.empty[DynamicValue => Unit] def finishWith(resultValue: DynamicValue): Unit = if (stack.nonEmpty) { @@ -146,7 +146,10 @@ object DynamicValue { finishWith( DynamicValue.Record( id, - ListMap.from(fs.map(_.name).zip(values.result())) + fs.map(_.name).zip(values.result()).foldLeft(ListMap.empty[String, DynamicValue]) { + case (lm, pair) => + lm.updated(pair._1, pair._2) + } ) ) } @@ -205,7 +208,10 @@ object DynamicValue { finishWith( DynamicValue.Record( id, - ListMap.from(structureChunk.map(_.name).zip(values.result())) + structureChunk.map(_.name).zip(values.result()).foldLeft(ListMap.empty[String, DynamicValue]) { + case (lm, pair) => + lm.updated(pair._1, pair._2) + } ) ) } From 5e8aff4b4e8c1087f7bd8165f3828d12967e4088 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Thu, 27 Oct 2022 12:24:06 -0400 Subject: [PATCH 04/18] Deprecation fix --- .../main/scala/zio/schema/DynamicValue.scala | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) 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 64c585acf..cb4ed4163 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala @@ -6,7 +6,6 @@ import java.time.format.DateTimeFormatter import java.util.UUID import scala.collection.immutable.ListMap -import scala.collection.mutable import zio.schema.meta.{ MetaSchema, Migration } import zio.{ Chunk, ChunkBuilder, Unsafe } @@ -121,14 +120,19 @@ object DynamicValue { //scalafmt: { maxColumn = 400 } def fromSchemaAndValue[A](schema: Schema[A], value: A): DynamicValue = { - var currentSchema: Schema[_] = schema - var currentValue: Any = value - var result: DynamicValue = null - val stack: mutable.Stack[DynamicValue => Unit] = mutable.Stack.empty[DynamicValue => Unit] + var currentSchema: Schema[_] = schema + var currentValue: Any = value + var result: DynamicValue = null + var stack: List[DynamicValue => Unit] = List.empty[DynamicValue => Unit] + + def push(f: DynamicValue => Unit): Unit = + stack = f :: stack def finishWith(resultValue: DynamicValue): Unit = if (stack.nonEmpty) { - stack.pop()(resultValue) + val head = stack.head + stack = stack.tail + head(resultValue) } else { result = resultValue } @@ -141,7 +145,7 @@ object DynamicValue { case next :: _ => currentSchema = next.schema currentValue = next.asInstanceOf[Schema.Field[Any, Any]].get(record) - stack.push(processField(remaining, _)) + push(processField(remaining, _)) case Nil => finishWith( DynamicValue.Record( @@ -172,7 +176,7 @@ object DynamicValue { case Some(v) => currentValue = v currentSchema = c.schema - stack.push(dv => finishWith(DynamicValue.Enumeration(id, c.id -> dv))) + push(dv => finishWith(DynamicValue.Enumeration(id, c.id -> dv))) found = true case None => } @@ -203,7 +207,7 @@ object DynamicValue { case next :: _ => currentSchema = next.schema currentValue = map(next.name) - stack.push(processField(remaining, _)) + push(processField(remaining, _)) case Nil => finishWith( DynamicValue.Record( @@ -307,7 +311,7 @@ object DynamicValue { else { currentSchema = schema currentValue = inputChunk(inputIdx) - stack.push { dv => + push { dv => resultChunk += dv processNext(inputIdx + 1) } @@ -326,7 +330,7 @@ object DynamicValue { else { currentSchema = kvSchema currentValue = inputChunk(inputIdx) - stack.push { + push { case DynamicValue.Tuple(a, b) => val pair = (a, b) resultChunk += pair @@ -348,7 +352,7 @@ object DynamicValue { else { currentSchema = as currentValue = inputChunk(inputIdx) - stack.push { dv => + push { dv => resultChunk += dv processNext(inputIdx + 1) } @@ -361,21 +365,21 @@ object DynamicValue { case Left(value: l) => currentValue = value currentSchema = schema.left - stack.push(dyn => finishWith(DynamicValue.LeftValue(dyn))) + push(dyn => finishWith(DynamicValue.LeftValue(dyn))) case Right(value: r) => currentValue = value currentSchema = schema.right - stack.push(dyn => finishWith(DynamicValue.RightValue(dyn))) + push(dyn => finishWith(DynamicValue.RightValue(dyn))) } case schema: Schema.Tuple2[a, b] => val (a: a, b: b) = currentValue.asInstanceOf[(a, b)] currentValue = a currentSchema = schema.left - stack.push { dynA => + push { dynA => currentValue = b currentSchema = schema.right - stack.push { dynB => + push { dynB => finishWith(DynamicValue.Tuple(dynA, dynB)) } } @@ -385,7 +389,7 @@ object DynamicValue { case Some(value: a) => currentValue = value currentSchema = schema.schema - stack.push { dyn => + push { dyn => finishWith(DynamicValue.SomeValue(dyn)) } case None => From cef1b058f4e00dd6823631f7c63b271b8f78c3cc Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Thu, 27 Oct 2022 18:04:42 -0400 Subject: [PATCH 05/18] Generic stack safe schema&value processing, wip --- .../scala/zio/schema/codec/ThriftCodec.scala | 1545 +++++++++++++---- .../zio/schema/codec/ThriftCodecSpec.scala | 17 +- .../main/scala/zio/schema/DynamicValue.scala | 739 +------- .../zio/schema/ProcessSchemaAndValue.scala | 1331 ++++++++++++++ 4 files changed, 2552 insertions(+), 1080 deletions(-) create mode 100644 zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala 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 5d735c517..4a6c1e9a5 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 @@ -139,32 +139,173 @@ object ThriftCodec extends Codec { ) } - class Encoder { - val write = new ChunkTransport.Write() - val p = new TBinaryProtocol(write) + // TODO: delete Encoder.Command and write directly in processXXX + class Encoder extends ProcessValueWithSchema[Encoder.Command, Encoder.State] { + import Encoder._ + + override protected def processPrimitive(state: State, value: Any, typ: StandardType[Any]): Encoder.Command = + Command.Sequence(Command.WriteFieldBegin(state.fieldNumber, getPrimitiveType(typ))) ++ + writePrimitiveType(typ, value) + + override protected def processRecord( + state: State, + schema: Schema.Record[_], + value: ListMap[String, Command] + ): Command = + Command.Sequence(Command.WriteFieldBegin(state.fieldNumber, TType.STRUCT)) ++ + Chunk.fromIterable(value.values) ++ + Command.WriteFieldEnd + + override protected def processEnum(state: State, schema: Schema.Enum[_], tuple: (String, Command)): Command = + Command.Sequence( + Command.WriteFieldBegin(state.fieldNumber, TType.STRUCT), + tuple._2, + Command.WriteFieldEnd + ) + + override protected def processSequence( + state: State, + schema: Schema.Sequence[_, _, _], + value: Chunk[Command] + ): Command = + Command.Sequence( + Command.WriteFieldBegin(state.fieldNumber, TType.LIST), + Command.WriteListBegin(getType(schema.elementSchema), value.size) + ) ++ value + + override protected def processDictionary( + state: State, + schema: Schema.Map[_, _], + value: Chunk[(Command, Command)] + ): Command = + value + .foldLeft( + Command.Sequence( + Command.WriteFieldBegin(state.fieldNumber, TType.MAP), + Command.WriteMapBegin(getType(schema.keySchema), getType(schema.valueSchema), value.size) + ) + ) { case (c, (c1, c2)) => c ++ c1 ++ c2 } + + override protected def processSet(state: State, schema: Schema.Set[_], value: Set[Command]): Command = + Command.Sequence( + Command.WriteFieldBegin(state.fieldNumber, TType.LIST), + Command.WriteSetBegin(getType(schema.elementSchema), value.size) + ) ++ Chunk.fromIterable(value) + + override protected def processEither( + state: State, + schema: Schema.Either[_, _], + value: Either[Command, Command] + ): Command = + Command.Sequence(Command.WriteFieldBegin(state.fieldNumber, TType.STRUCT)) ++ value.merge + + override protected def processOption(state: State, schema: Schema.Optional[_], value: Option[Command]): Command = + Command.Sequence(Command.WriteFieldBegin(state.fieldNumber, TType.STRUCT)) ++ + (value match { + case Some(value) => value + case None => + processPrimitive( + state.copy(fieldNumber = Some(1)), + (), + StandardType.UnitType.asInstanceOf[StandardType[Any]] + ) + }) ++ Command.WriteFieldEnd + + override protected def processTuple( + state: State, + schema: Schema.Tuple2[_, _], + left: Command, + right: Command + ): Command = + Command.Sequence( + Command.WriteFieldBegin(state.fieldNumber, TType.STRUCT), + left, + right, + Command.WriteFieldEnd + ) + + override protected def fail(state: State, message: String): Command = + Command.Fail(message) + + override protected def processDynamic(state: State, value: DynamicValue): Option[Command] = + None + + override protected val initialState: State = State(None) + + override protected def stateForRecordField(state: State, index: Int, field: Schema.Field[_, _]): State = + state.copy(fieldNumber = Some((index + 1).toShort)) + + override protected def stateForEnumConstructor(state: State, index: Int, c: Schema.Case[_, _]): State = + state.copy(fieldNumber = Some((index + 1).toShort)) + + override protected def stateForEither(state: State, e: Either[Unit, Unit]): State = + e match { + case Left(_) => state.copy(fieldNumber = Some(1)) + case Right(_) => state.copy(fieldNumber = Some(2)) + } + + override protected def stateForOption(state: State, o: Option[Unit]): State = + o match { + case None => state.copy(fieldNumber = Some(1)) + case Some(_) => state.copy(fieldNumber = Some(2)) + } + + override protected def stateForTuple(state: State, index: Int): State = + state.copy(fieldNumber = Some(index.toShort)) + + override protected def stateForSequence(state: State): State = + state.copy(fieldNumber = None) def encode[A](schema: Schema[A], value: A): Chunk[Byte] = { - encodeValue(None, schema, value) + val command = process(schema, value) + val write = new ChunkTransport.Write() + val p = new TBinaryProtocol(write) + + execute(p, command) + write.chunk } + } - @tailrec - final 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 + object Encoder { + final case class State(fieldNumber: Option[Short]) + + sealed trait Command + + object Command { + final case class Sequence(commands: Chunk[Command]) extends Command { self => + + def ++(other: Command): Sequence = + other match { + case Sequence(otherCommands) => Sequence(commands ++ otherCommands) + case _ => Sequence(commands :+ other) + } + + def ++(others: Chunk[Command]): Sequence = + others.foldLeft(self)(_ ++ _) + } + + object Sequence { + def apply(commands: Command*): Sequence = Sequence(Chunk.fromIterable(commands)) + } + final case class WriteFieldBegin(fieldNumber: Option[Short], ttype: Byte) extends Command + case object WriteFieldEnd extends Command + case object Noop extends Command + final case class WriteString(value: String) extends Command + final case class WriteBool(value: Boolean) extends Command + final case class WriteByte(value: Byte) extends Command + final case class WriteI16(value: Short) extends Command + final case class WriteI32(value: Int) extends Command + final case class WriteI64(value: Long) extends Command + final case class WriteDouble(value: Double) extends Command + final case class WriteBinary(value: Chunk[Byte]) extends Command + final case class WriteListBegin(ttype: Byte, count: Int) extends Command + final case class WriteSetBegin(ttype: Byte, count: Int) extends Command + final case class WriteMapBegin(keyType: Byte, valueType: Byte, count: Int) extends Command + final case class Fail(message: String) extends Command } - def getPrimitiveType[A](standardType: StandardType[A]): Byte = + private def getPrimitiveType[A](standardType: StandardType[A]): Byte = standardType match { case StandardType.UnitType => TType.VOID case StandardType.StringType => @@ -212,290 +353,160 @@ object ThriftCodec extends Codec { case _ => TType.VOID } - def writePrimitiveType[A](standardType: StandardType[A], value: A): Unit = + @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 + } + + private def writePrimitiveType[A](standardType: StandardType[A], value: A): Command = (standardType, value) match { - case (StandardType.UnitType, _) => () + case (StandardType.UnitType, _) => + Command.Noop case (StandardType.StringType, str: String) => - p.writeString(str) + Command.WriteString(str) case (StandardType.BoolType, b: Boolean) => - p.writeBool(b) + Command.WriteBool(b) case (StandardType.ByteType, v: Byte) => - p.writeByte(v) + Command.WriteByte(v) case (StandardType.ShortType, v: Short) => - p.writeI16(v) + Command.WriteI16(v) case (StandardType.IntType, v: Int) => - p.writeI32(v) + Command.WriteI32(v) case (StandardType.LongType, v: Long) => - p.writeI64(v) + Command.WriteI64(v) case (StandardType.FloatType, v: Float) => - p.writeDouble(v.toDouble) + Command.WriteDouble(v.toDouble) case (StandardType.DoubleType, v: Double) => - p.writeDouble(v.toDouble) + Command.WriteDouble(v.toDouble) case (StandardType.BigIntegerType, v: java.math.BigInteger) => - p.writeBinary(ByteBuffer.wrap(v.toByteArray)) + Command.WriteBinary(Chunk.fromArray(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) - ) + Command.Sequence(Command.WriteFieldBegin(Some(1), getPrimitiveType(StandardType.BigIntegerType))) ++ + writePrimitiveType(StandardType.BigIntegerType, unscaled) ++ + Command.WriteFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) ++ + writePrimitiveType(StandardType.IntType, precision) ++ + Command.WriteFieldBegin(Some(3), getPrimitiveType(StandardType.IntType)) ++ + writePrimitiveType(StandardType.IntType, scale) ++ + Command.WriteFieldEnd + case (StandardType.BinaryType, bytes: Chunk[Byte]) => - p.writeBinary(ByteBuffer.wrap(bytes.toArray)) + Command.WriteBinary(Chunk.fromArray(bytes.toArray)) case (StandardType.CharType, c: Char) => - p.writeString(c.toString) + Command.WriteString(c.toString) case (StandardType.UUIDType, u: UUID) => - p.writeString(u.toString) + Command.WriteString(u.toString) case (StandardType.DayOfWeekType, v: DayOfWeek) => - p.writeByte(v.getValue.toByte) + Command.WriteByte(v.getValue.toByte) case (StandardType.MonthType, v: Month) => - p.writeByte(v.getValue.toByte) + Command.WriteByte(v.getValue.toByte) case (StandardType.MonthDayType, v: MonthDay) => - encodeRecord(None, monthDayStructure, ListMap("month" -> v.getMonthValue, "day" -> v.getDayOfMonth)) + Command.Sequence(Command.WriteFieldBegin(Some(1), getPrimitiveType(StandardType.IntType))) ++ + writePrimitiveType(StandardType.IntType, v.getMonthValue) ++ + Command.WriteFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) ++ + writePrimitiveType(StandardType.IntType, v.getDayOfMonth) ++ + Command.WriteFieldEnd + case (StandardType.PeriodType, v: Period) => - encodeRecord( - None, - periodStructure, - ListMap("years" -> v.getYears, "months" -> v.getMonths, "days" -> v.getDays) - ) + Command.Sequence(Command.WriteFieldBegin(Some(1), getPrimitiveType(StandardType.IntType))) ++ + writePrimitiveType(StandardType.IntType, v.getYears) ++ + Command.WriteFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) ++ + writePrimitiveType(StandardType.IntType, v.getMonths) ++ + Command.WriteFieldBegin(Some(3), getPrimitiveType(StandardType.IntType)) ++ + writePrimitiveType(StandardType.IntType, v.getDays) ++ + Command.WriteFieldEnd + case (StandardType.YearType, v: Year) => - p.writeI32(v.getValue) + Command.WriteI32(v.getValue) case (StandardType.YearMonthType, v: YearMonth) => - encodeRecord(None, yearMonthStructure, ListMap("year" -> v.getYear, "month" -> v.getMonthValue)) + Command.Sequence(Command.WriteFieldBegin(Some(1), getPrimitiveType(StandardType.IntType))) ++ + writePrimitiveType(StandardType.IntType, v.getYear) ++ + Command.WriteFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) ++ + writePrimitiveType(StandardType.IntType, v.getMonthValue) ++ + Command.WriteFieldEnd case (StandardType.ZoneIdType, v: ZoneId) => - p.writeString(v.getId) + Command.WriteString(v.getId) case (StandardType.ZoneOffsetType, v: ZoneOffset) => - p.writeI32(v.getTotalSeconds) + Command.WriteI32(v.getTotalSeconds) case (StandardType.DurationType, v: Duration) => - encodeRecord(None, durationStructure, ListMap("seconds" -> v.getSeconds, "nanos" -> v.getNano)) + Command.Sequence(Command.WriteFieldBegin(Some(1), getPrimitiveType(StandardType.LongType))) ++ + writePrimitiveType(StandardType.LongType, v.getSeconds) ++ + Command.WriteFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) ++ + writePrimitiveType(StandardType.IntType, v.getNano) ++ + Command.WriteFieldEnd + case (StandardType.InstantType(formatter), v: Instant) => - p.writeString(formatter.format(v)) + Command.WriteString(formatter.format(v)) case (StandardType.LocalDateType(formatter), v: LocalDate) => - p.writeString(formatter.format(v)) + Command.WriteString(formatter.format(v)) case (StandardType.LocalTimeType(formatter), v: LocalTime) => - p.writeString(formatter.format(v)) + Command.WriteString(formatter.format(v)) case (StandardType.LocalDateTimeType(formatter), v: LocalDateTime) => - p.writeString(formatter.format(v)) + Command.WriteString(formatter.format(v)) case (StandardType.OffsetTimeType(formatter), v: OffsetTime) => - p.writeString(formatter.format(v)) + Command.WriteString(formatter.format(v)) case (StandardType.OffsetDateTimeType(formatter), v: OffsetDateTime) => - p.writeString(formatter.format(v)) + Command.WriteString(formatter.format(v)) case (StandardType.ZonedDateTimeType(formatter), v: ZonedDateTime) => - p.writeString(formatter.format(v)) + Command.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 (_, _) => () + Command.Fail(s"No encoder for $standardType") } - 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 + private def execute(p: TBinaryProtocol, command: Command): Unit = + command match { + case Command.Sequence(commands) => + for (command <- commands) + execute(p, command) + case Command.WriteFieldBegin(fieldNumber, ttype) => + fieldNumber match { + case Some(num) => + p.writeFieldBegin( + new TField("", ttype, num) + ) + case None => + } + case Command.WriteFieldEnd => + p.writeFieldStop() + case Command.Noop => + case Command.WriteString(value) => + p.writeString(value) + case Command.WriteBool(value) => + p.writeBool(value) + case Command.WriteByte(value) => + p.writeByte(value) + case Command.WriteI16(value) => + p.writeI16(value) + case Command.WriteI32(value) => + p.writeI32(value) + case Command.WriteI64(value) => + p.writeI64(value) + case Command.WriteDouble(value) => + p.writeDouble(value) + case Command.WriteBinary(value) => + p.writeBinary(ByteBuffer.wrap(value.toArray)) + case Command.WriteListBegin(ttype, count) => + p.writeListBegin(new TList(ttype, count)) + case Command.WriteSetBegin(ttype, count) => + p.writeSetBegin(new TSet(ttype, count)) + case Command.WriteMapBegin(keyType, valueType, count) => + p.writeMapBegin(new TMap(keyType, valueType, count)) + case Command.Fail(message) => + throw new RuntimeException(message) } - - } - - 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)))) - } } class Decoder(chunk: Chunk[Byte]) { @@ -564,32 +575,187 @@ object ThriftCodec extends Codec { 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}") + 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}") } private def dynamicDecoder(path: Path, schema: Schema[DynamicValue]): Result[DynamicValue] = @@ -612,7 +778,10 @@ object ThriftCodec extends Codec { 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}") + 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) @@ -621,7 +790,10 @@ object ThriftCodec extends Codec { } res.asInstanceOf[Result[Z]] } - }.fold(err => fail(path, s"Error decoding enum with cases ${cases.map(_.id).mkString(", ")}: ${err.getMessage}"), identity) + }.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() @@ -697,7 +869,14 @@ object ThriftCodec extends Codec { .map(data => MonthDay.of(data.getOrElse(1, 0).asInstanceOf[Int], data.getOrElse(2, 0).asInstanceOf[Int])) 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])) + .map( + data => + Period.of( + data.getOrElse(1, 0).asInstanceOf[Int], + data.getOrElse(2, 0).asInstanceOf[Int], + data.getOrElse(3, 0).asInstanceOf[Int] + ) + ) case StandardType.YearType => decodeInt(path).map(_.intValue).map(Year.of) case StandardType.YearMonthType => @@ -710,7 +889,11 @@ object ThriftCodec extends Codec { .map(ZoneOffset.ofTotalSeconds) case StandardType.DurationType => decodeRecord(path, durationStructure) - .map(data => Duration.ofSeconds(data.getOrElse(1, 0L).asInstanceOf[Long], data.getOrElse(2, 0L).asInstanceOf[Int].toLong)) + .map( + data => + Duration + .ofSeconds(data.getOrElse(1, 0L).asInstanceOf[Long], data.getOrElse(2, 0L).asInstanceOf[Int].toLong) + ) case StandardType.InstantType(formatter) => decodeString(path).map(v => Instant.from(formatter.parse(v))) case StandardType.LocalDateType(formatter) => @@ -777,7 +960,10 @@ object ThriftCodec extends Codec { } 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)) + Try { p.readListBegin() }.fold( + _ => fail(path, "Can not decode Sequence begin"), + begin => decodeElements(begin.size, ChunkBuilder.make[Elem]()).map(schema.fromChunk) + ) } def decodeMap[K, V](path: Path, schema: Schema.Map[K, V]): Result[scala.collection.immutable.Map[K, V]] = { @@ -795,7 +981,10 @@ object ThriftCodec extends Codec { Try { p.readMapBegin() - }.fold(_ => fail(path, "Can not decode Map begin"), begin => decodeElements(begin.size, scala.collection.mutable.Map.empty[K, V])) + }.fold( + _ => fail(path, "Can not decode Map begin"), + begin => decodeElements(begin.size, scala.collection.mutable.Map.empty[K, V]) + ) } def decodeSet[A](path: Path, schema: Schema.Set[A]): Result[scala.collection.immutable.Set[A]] = { @@ -808,31 +997,36 @@ object ThriftCodec extends Codec { } else succeed(cb.result()) - Try { p.readSetBegin() }.fold(_ => fail(path, "Can not decode Set begin"), begin => decodeElements(begin.size, ChunkBuilder.make[A]()).map(_.toSet)) + Try { p.readSetBegin() }.fold( + _ => fail(path, "Can not decode Set begin"), + begin => decodeElements(begin.size, ChunkBuilder.make[A]()).map(_.toSet) + ) } 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.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] => @@ -897,64 +1091,257 @@ object ThriftCodec extends Codec { _ <- 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] = + 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]) + } 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] = + 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]) + } 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] = + 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]) + 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] = + 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]) + 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] = + 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]) + 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] = + 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]) + 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] = + 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]) + 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] = + 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]) + 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] = + 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]) + 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] = + 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) + 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], @@ -971,10 +1358,28 @@ object ThriftCodec extends Codec { 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] = + 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) + 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], @@ -992,10 +1397,29 @@ object ThriftCodec extends Codec { 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] = + 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) + 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], @@ -1014,10 +1438,30 @@ object ThriftCodec extends Codec { 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] = + 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) + 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], @@ -1037,10 +1481,31 @@ object ThriftCodec extends Codec { 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] = + 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) + 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], @@ -1061,10 +1526,52 @@ object ThriftCodec extends Codec { 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] = + 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) + 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], @@ -1086,10 +1593,75 @@ object ThriftCodec extends Codec { 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] = + 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) + 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], @@ -1112,10 +1684,78 @@ object ThriftCodec extends Codec { 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] = + 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) + 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], @@ -1139,10 +1779,81 @@ object ThriftCodec extends Codec { 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] = + 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) + 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], @@ -1167,10 +1878,84 @@ object ThriftCodec extends Codec { 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] = + 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) + 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], 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 a9270c676..8a43dc071 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 @@ -146,9 +146,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)(fails(anything)) && assert(e2)(fails(anything)) } ), suite("Should successfully encode and decode")( @@ -509,9 +509,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 @@ -669,7 +672,7 @@ object ThriftCodecSpec extends ZIOSpecDefault { 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 cb4ed4163..d3adf2b97 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala @@ -1,15 +1,14 @@ package zio.schema +import zio.schema.meta.{ MetaSchema, Migration } +import zio.{ Chunk, Unsafe } + import java.math.{ BigDecimal, BigInteger } import java.time._ import java.time.format.DateTimeFormatter import java.util.UUID - import scala.collection.immutable.ListMap -import zio.schema.meta.{ MetaSchema, Migration } -import zio.{ Chunk, ChunkBuilder, Unsafe } - sealed trait DynamicValue { self => @@ -117,706 +116,61 @@ sealed trait DynamicValue { } object DynamicValue { + private object FromSchemaAndValue extends ProcessSchemaAndValueWithoutState[DynamicValue] { + override protected def processPrimitive(value: Any, typ: StandardType[Any]): DynamicValue = + DynamicValue.Primitive(value, typ) - //scalafmt: { maxColumn = 400 } - def fromSchemaAndValue[A](schema: Schema[A], value: A): DynamicValue = { - var currentSchema: Schema[_] = schema - var currentValue: Any = value - var result: DynamicValue = null - var stack: List[DynamicValue => Unit] = List.empty[DynamicValue => Unit] - - def push(f: DynamicValue => Unit): Unit = - stack = f :: stack - - def finishWith(resultValue: DynamicValue): Unit = - if (stack.nonEmpty) { - val head = stack.head - stack = stack.tail - head(resultValue) - } else { - result = resultValue - } + override protected def processRecord(schema: Schema.Record[_], value: ListMap[String, DynamicValue]): DynamicValue = + DynamicValue.Record(schema.id, value) - def fields(id: TypeId, record: Any, fs: Schema.Field[_, _]*): Unit = { - val values = ChunkBuilder.make[DynamicValue](fs.size) - - def processNext(remaining: List[Schema.Field[_, _]]): Unit = - remaining match { - case next :: _ => - currentSchema = next.schema - currentValue = next.asInstanceOf[Schema.Field[Any, Any]].get(record) - push(processField(remaining, _)) - case Nil => - finishWith( - DynamicValue.Record( - id, - fs.map(_.name).zip(values.result()).foldLeft(ListMap.empty[String, DynamicValue]) { - case (lm, pair) => - lm.updated(pair._1, pair._2) - } - ) - ) - } + override protected def processEnum(schema: Schema.Enum[_], tuple: (String, DynamicValue)): DynamicValue = + DynamicValue.Enumeration(schema.id, tuple) - def processField(currentStructure: List[Schema.Field[_, _]], fieldResult: DynamicValue): Unit = { - values += fieldResult - val remaining = currentStructure.tail - processNext(remaining) - } + override protected def processSequence(schema: Schema.Sequence[_, _, _], value: Chunk[DynamicValue]): DynamicValue = + DynamicValue.Sequence(value) - processNext(fs.toList) - } + override protected def processDictionary( + schema: Schema.Map[_, _], + value: Chunk[(DynamicValue, DynamicValue)] + ): DynamicValue = + DynamicValue.Dictionary(value) - def enumCases(id: TypeId, cs: Schema.Case[_, _]*): Unit = { - var found = false - val it = cs.iterator - 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 - push(dv => finishWith(DynamicValue.Enumeration(id, c.id -> dv))) - found = true - case None => - } - } + override protected def processSet(schema: Schema.Set[_], value: Set[DynamicValue]): DynamicValue = + DynamicValue.SetValue(value) - if (!found) { - //This should never happen unless someone manually builds an Enum and doesn't include all cases - finishWith(DynamicValue.NoneValue) + 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) } - } - - while (result eq null) { - currentSchema match { - - case l @ Schema.Lazy(_) => - currentSchema = l.schema - - case Schema.Primitive(p, _) => - finishWith(DynamicValue.Primitive(currentValue, p.asInstanceOf[StandardType[Any]])) - - case Schema.GenericRecord(id, structure, _) => - val map = currentValue.asInstanceOf[ListMap[String, _]] - val structureChunk = structure.toChunk - val values = ChunkBuilder.make[DynamicValue](structureChunk.size) - - def processNext(remaining: List[Schema.Field[ListMap[String, _], _]]): Unit = - remaining match { - case next :: _ => - currentSchema = next.schema - currentValue = map(next.name) - push(processField(remaining, _)) - case Nil => - finishWith( - DynamicValue.Record( - id, - structureChunk.map(_.name).zip(values.result()).foldLeft(ListMap.empty[String, DynamicValue]) { - case (lm, pair) => - lm.updated(pair._1, pair._2) - } - ) - ) - } - - def processField(currentStructure: List[Schema.Field[ListMap[String, _], _]], fieldResult: DynamicValue): Unit = { - values += fieldResult - val remaining = currentStructure.tail - processNext(remaining) - } - - processNext(structureChunk.toList) - - case Schema.Enum1(id, case1, _) => - enumCases(id, case1) - - case Schema.Enum2(id, case1, case2, _) => - enumCases(id, case1, case2) - - case Schema.Enum3(id, case1, case2, case3, _) => - enumCases(id, case1, case2, case3) - - case Schema.Enum4(id, case1, case2, case3, case4, _) => - enumCases(id, case1, case2, case3, case4) - case Schema.Enum5(id, case1, case2, case3, case4, case5, _) => - enumCases(id, case1, case2, case3, case4, case5) - - case Schema.Enum6(id, case1, case2, case3, case4, case5, case6, _) => - enumCases(id, case1, case2, case3, case4, case5, case6) - - case Schema.Enum7(id, case1, case2, case3, case4, case5, case6, case7, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7) - - case Schema.Enum8(id, case1, case2, case3, case4, case5, case6, case7, case8, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8) - - case Schema.Enum9(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9) - - case Schema.Enum10(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10) - - case Schema.Enum11(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11) - - case Schema.Enum12(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12) - - case Schema.Enum13(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13) - - case Schema.Enum14(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14) - - case Schema.Enum15(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15) - - case Schema.Enum16(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16) - - case Schema.Enum17(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17) - - case Schema.Enum18(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18) - - case Schema.Enum19(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19) - - case Schema.Enum20(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, case20, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, case20) - - 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, _) => - enumCases(id, case1, case2, case3, case4, case5, case6, case7, case8, case9, case10, case11, case12, case13, case14, case15, case16, case17, case18, case19, case20, case21) - - 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, _) => - enumCases(id, 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 Schema.EnumN(id, cases, _) => - enumCases(id, cases.toSeq: _*) - - case Schema.Fail(message, _) => - finishWith(DynamicValue.Error(message)) - - case Schema.Sequence(schema, _, toChunk, _, _) => - val inputChunk = toChunk.asInstanceOf[Any => Chunk[Any]](currentValue) - val resultChunk = ChunkBuilder.make[DynamicValue](inputChunk.size) - - def processNext(inputIdx: Int): Unit = - if (inputIdx == inputChunk.size) - finishWith(DynamicValue.Sequence(resultChunk.result())) - else { - currentSchema = schema - currentValue = inputChunk(inputIdx) - push { dv => - resultChunk += dv - processNext(inputIdx + 1) - } - } - - processNext(0) - - case Schema.Map(ks: Schema[k], vs: Schema[v], _) => - val inputChunk = Chunk.fromIterable(currentValue.asInstanceOf[Map[k, v]]) - val resultChunk = ChunkBuilder.make[(DynamicValue, DynamicValue)](inputChunk.size) - val kvSchema = Schema.tuple2(ks, vs) - - def processNext(inputIdx: Int): Unit = - if (inputIdx == inputChunk.size) - finishWith(DynamicValue.Dictionary(resultChunk.result())) - else { - currentSchema = kvSchema - currentValue = inputChunk(inputIdx) - push { - case DynamicValue.Tuple(a, b) => - val pair = (a, b) - resultChunk += pair - processNext(inputIdx + 1) - case _ => - finishWith(DynamicValue.Error("Unexpected dynamic value when converting a Map")) - } - } - - processNext(0) - - case Schema.Set(as: Schema[a], _) => - val inputChunk = Chunk.fromIterable(currentValue.asInstanceOf[Set[a]]) - val resultChunk = ChunkBuilder.make[DynamicValue](inputChunk.size) - - def processNext(inputIdx: Int): Unit = - if (inputIdx == inputChunk.size) - finishWith(DynamicValue.SetValue(resultChunk.result().toSet)) - else { - currentSchema = as - currentValue = inputChunk(inputIdx) - push { dv => - resultChunk += dv - processNext(inputIdx + 1) - } - } - - processNext(0) - - case schema: Schema.Either[l, r] => - currentValue.asInstanceOf[Either[l, r]] match { - case Left(value: l) => - currentValue = value - currentSchema = schema.left - push(dyn => finishWith(DynamicValue.LeftValue(dyn))) - case Right(value: r) => - currentValue = value - currentSchema = schema.right - push(dyn => finishWith(DynamicValue.RightValue(dyn))) - } + 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.Tuple2[a, b] => - val (a: a, b: b) = currentValue.asInstanceOf[(a, b)] - currentValue = a - currentSchema = schema.left - push { dynA => - currentValue = b - currentSchema = schema.right - push { dynB => - finishWith(DynamicValue.Tuple(dynA, dynB)) - } - } + override protected def processTuple( + schema: Schema.Tuple2[_, _], + left: DynamicValue, + right: DynamicValue + ): DynamicValue = + DynamicValue.Tuple(left, right) - case schema: Schema.Optional[a] => - currentValue.asInstanceOf[Option[a]] match { - case Some(value: a) => - currentValue = value - currentSchema = schema.schema - push { dyn => - finishWith(DynamicValue.SomeValue(dyn)) - } - case None => - finishWith(DynamicValue.NoneValue) - } - - case Schema.Transform(schema, _, g, _, _) => - g.asInstanceOf[Any => Either[String, Any]](currentValue) match { - case Left(message) => - finishWith(DynamicValue.Error(message)) - case Right(a) => - currentValue = a - currentSchema = schema - } + override protected def processDynamic(value: DynamicValue): Option[DynamicValue] = + Some(value) - case Schema.CaseClass0(id, _, _) => - finishWith(DynamicValue.Record(id, ListMap())) - - case Schema.CaseClass1(id, f, _, _) => - fields(id, currentValue, f) - - case Schema.CaseClass2(id, f1, f2, _, _) => - fields(id, currentValue, f1, f2) - case Schema.CaseClass3(id, f1, f2, f3, _, _) => - fields(id, currentValue, f1, f2, f3) - case Schema.CaseClass4(id, f1, f2, f3, f4, _, _) => - fields(id, currentValue, f1, f2, f3, f4) - case Schema.CaseClass5(id, f1, f2, f3, f4, f5, _, _) => - fields(id, currentValue, f1, f2, f3, f4, f5) - case Schema.CaseClass6(id, f1, f2, f3, f4, f5, f6, _, _) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6) - case Schema.CaseClass7(id, f1, f2, f3, f4, f5, f6, f7, _, _) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7) - case Schema.CaseClass8( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8) - case Schema.CaseClass9( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9) - case Schema.CaseClass10( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10) - case Schema.CaseClass11( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11) - case Schema.CaseClass12( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12) - case Schema.CaseClass13( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13) - case Schema.CaseClass14( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14) - case Schema.CaseClass15( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15) - case Schema.CaseClass16( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16) - case Schema.CaseClass17( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17) - case Schema.CaseClass18( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18) - case Schema.CaseClass19( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - _, - _ - ) => - fields(id, currentValue, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19) - case Schema.CaseClass20( - id, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - f20, - _, - _ - ) => - fields( - id, - currentValue, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - f20 - ) - 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, - _, - _ - ) => - fields( - id, - currentValue, - f1, - f2, - f3, - f4, - f5, - f6, - f7, - f8, - f9, - f10, - f11, - f12, - f13, - f14, - f15, - f16, - f17, - f18, - f19, - f20, - f21 - ) - 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, - _, - _ - ) => - fields( - id, - 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(_) => - finishWith(currentValue.asInstanceOf[DynamicValue]) - } - } - result + override protected def fail(message: String): DynamicValue = + DynamicValue.Error(message) } + //scalafmt: { maxColumn = 400 } + def fromSchemaAndValue[A](schema: Schema[A], value: A): DynamicValue = + FromSchemaAndValue.process(schema, value) + def decodeStructure( values: ListMap[String, DynamicValue], structure: Chunk[Schema.Field[_, _]] @@ -1134,8 +488,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/ProcessSchemaAndValue.scala b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala new file mode 100644 index 000000000..0c5c849c8 --- /dev/null +++ b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala @@ -0,0 +1,1331 @@ +package zio.schema + +import zio.{ Chunk, ChunkBuilder } + +import scala.collection.immutable.ListMap + +trait ProcessValueWithSchema[Target <: AnyRef, State] { + protected def processPrimitive(state: State, value: Any, typ: StandardType[Any]): Target + + protected def processRecord(state: State, schema: Schema.Record[_], value: ListMap[String, Target]): Target + + protected def processEnum(state: State, schema: Schema.Enum[_], tuple: (String, Target)): Target + + protected def processSequence(state: State, schema: Schema.Sequence[_, _, _], value: Chunk[Target]): Target + + protected def processDictionary(state: State, schema: Schema.Map[_, _], value: Chunk[(Target, Target)]): Target + + protected def processSet(state: State, schema: Schema.Set[_], value: Set[Target]): Target + + protected def processEither(state: State, schema: Schema.Either[_, _], value: Either[Target, Target]): Target + + protected def processOption(state: State, schema: Schema.Optional[_], value: Option[Target]): Target + + protected def processTuple(state: State, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target + + protected def processDynamic(state: State, value: DynamicValue): Option[Target] + + protected def fail(state: State, message: String): Target + + protected val initialState: State + + protected def stateForRecordField(state: State, index: Int, field: Schema.Field[_, _]): State + protected def stateForTuple(state: State, index: Int): State + protected def stateForEnumConstructor(state: State, index: Int, c: Schema.Case[_, _]): State + protected def stateForEither(state: State, e: Either[Unit, Unit]): State + protected def stateForOption(state: State, o: Option[Unit]): State + protected def stateForSequence(state: State): State + + def process[A](schema: Schema[A], value: A): Target = { + var currentSchema: Schema[_] = schema + var currentValue: Any = value + var result: Target = null.asInstanceOf[Target] + var stack: List[Target => Unit] = List.empty[Target => Unit] + var stateStack: List[State] = List(initialState) + + def push(f: Target => Unit): Unit = + stack = f :: stack + + def pushState(s: State): Unit = + stateStack = s :: stateStack + + def finishWith(resultValue: Target): Unit = + if (stack.nonEmpty) { + val head = stack.head + stack = stack.tail + head(resultValue) + } else { + result = 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) + pushState(stateForRecordField(stateStack.head, index, next)) + push(processField(index, remaining, _)) + case Nil => + finishWith( + processRecord( + stateStack.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 = { + stateStack = stateStack.tail + values += fieldResult + val remaining = currentStructure.tail + processNext(index + 1, remaining) + } + + processNext(0, fs.toList) + } + + def enumCases(s: Schema.Enum[_], cs: Schema.Case[_, _]*): Unit = { + 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 + pushState(stateForEnumConstructor(stateStack.head, index, c)) + push { dv => + stateStack = stateStack.tail + finishWith(processEnum(stateStack.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(stateStack.head, "Invalid enum constructor")) + } + } + + while (result eq null) { + val state = stateStack.head + + currentSchema match { + + case l @ Schema.Lazy(_) => + currentSchema = l.schema + + case Schema.Primitive(p, _) => + finishWith(processPrimitive(state, 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) + pushState(stateForRecordField(state, index, next)) + push(processField(index, remaining, _)) + case Nil => + finishWith( + processRecord( + state, + 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 = { + stateStack = stateStack.tail + values += fieldResult + val remaining = currentStructure.tail + processNext(index + 1, remaining) + } + + 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(state, 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 = + if (inputIdx == inputChunk.size) { + stateStack = stateStack.tail + finishWith(processSequence(state, s, resultChunk.result())) + } else { + currentSchema = schema + currentValue = inputChunk(inputIdx) + push { dv => + resultChunk += dv + processNext(inputIdx + 1) + } + } + + pushState(stateForSequence(state)) + 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(state, s, resultChunk.result())) + } else { + currentSchema = ks + val currentTuple = inputChunk(inputIdx) + currentValue = currentTuple._1 + + pushState(stateForSequence(state)) + push { (a: Target) => + stateStack = stateStack.tail + + currentSchema = vs + currentValue = currentTuple._2 + pushState(stateForSequence(state)) + push { (b: Target) => + stateStack = stateStack.tail + val pair = (a, b) + resultChunk += pair + processNext(inputIdx + 1) + } + } + } + + 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 = + if (inputIdx == inputChunk.size) { + stateStack = stateStack.tail + finishWith(processSet(state, s, resultChunk.result().toSet)) + } else { + currentSchema = as + currentValue = inputChunk(inputIdx) + push { dv => + resultChunk += dv + processNext(inputIdx + 1) + } + } + + pushState(stateForSequence(state)) + processNext(0) + + case s: Schema.Either[l, r] => + currentValue.asInstanceOf[Either[l, r]] match { + case Left(value: l) => + currentValue = value + currentSchema = s.left + pushState(stateForEither(state, Left(()))) + push { dyn => + stateStack = stateStack.tail + finishWith(processEither(state, s, Left(dyn))) + } + case Right(value: r) => + currentValue = value + currentSchema = s.right + pushState(stateForEither(state, Right(()))) + push { dyn => + stateStack = stateStack.tail + finishWith(processEither(state, s, Right(dyn))) + } + } + + case s: Schema.Tuple2[a, b] => + val (a: a, b: b) = currentValue.asInstanceOf[(a, b)] + currentValue = a + currentSchema = s.left + pushState(stateForTuple(state, 1)) + push { dynA => + currentValue = b + currentSchema = s.right + stateStack = stateStack.tail + pushState(stateForTuple(state, 2)) + push { dynB => + stateStack = stateStack.tail + finishWith(processTuple(state, s, dynA, dynB)) + } + } + + case s: Schema.Optional[a] => + currentValue.asInstanceOf[Option[a]] match { + case Some(value: a) => + currentValue = value + currentSchema = s.schema + pushState(stateForOption(state, Some(()))) + push { dyn => + stateStack = stateStack.tail + finishWith(processOption(state, s, Some(dyn))) + } + case None => + finishWith(processOption(state, s, None)) + } + + case Schema.Transform(schema, _, g, _, _) => + g.asInstanceOf[Any => Either[String, Any]](currentValue) match { + case Left(message) => + finishWith(fail(state, message)) + case Right(a) => + currentValue = a + currentSchema = schema + } + + case s @ Schema.CaseClass0(_, _, _) => + finishWith(processRecord(state, 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(state, currentValue.asInstanceOf[DynamicValue]) match { + case Some(target) => finishWith(target) + case None => + currentSchema = Schema.dynamicValue + } + } + } + result + } +} + +trait ProcessSchemaAndValueWithoutState[Target <: AnyRef] extends ProcessValueWithSchema[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(state: Unit, value: Any, typ: StandardType[Any]): Target = + processPrimitive(value, typ) + + override protected def processRecord(state: Unit, schema: Schema.Record[_], value: ListMap[String, Target]): Target = + processRecord(schema, value) + + override protected def processEnum(state: Unit, schema: Schema.Enum[_], tuple: (String, Target)): Target = + processEnum(schema, tuple) + + override protected def processSequence(state: Unit, schema: Schema.Sequence[_, _, _], value: Chunk[Target]): Target = + processSequence(schema, value) + + override protected def processDictionary( + state: Unit, + schema: Schema.Map[_, _], + value: Chunk[(Target, Target)] + ): Target = + processDictionary(schema, value) + + override protected def processSet(state: Unit, schema: Schema.Set[_], value: Set[Target]): Target = + processSet(schema, value) + + override protected def processEither( + state: Unit, + schema: Schema.Either[_, _], + value: Either[Target, Target] + ): Target = + processEither(schema, value) + + override protected def processOption(state: Unit, schema: Schema.Optional[_], value: Option[Target]): Target = + processOption(schema, value) + + override protected def processTuple(state: Unit, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target = + processTuple(schema, left, right) + + override protected def fail(state: Unit, message: String): Target = + fail(message) + + override protected def processDynamic(state: Unit, value: DynamicValue): Option[Target] = + processDynamic(value) + + override protected val initialState: Unit = () + + override protected def stateForRecordField(state: Unit, index: Int, field: Schema.Field[_, _]): Unit = + () + + override protected def stateForEnumConstructor(state: Unit, index: Int, c: Schema.Case[_, _]): Unit = + () + + override protected def stateForEither(state: Unit, e: Either[Unit, Unit]): Unit = + () + + override protected def stateForOption(state: Unit, o: Option[Unit]): Unit = + () + + override protected def stateForTuple(state: Unit, index: Int): Unit = + () + + override protected def stateForSequence(state: Unit): Unit = + () +} From 227ac684b33f567e76ded4edacf3b3f96df3d85c Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Fri, 28 Oct 2022 11:41:04 -0400 Subject: [PATCH 06/18] Stack safe protobuf encoder --- .../zio/schema/codec/JsonCodecSpec.scala | 2 +- .../zio/schema/codec/ProtobufCodec.scala | 422 +++++++++--------- .../zio/schema/codec/ProtobufCodecSpec.scala | 12 +- .../zio/schema/ProcessSchemaAndValue.scala | 32 +- 4 files changed, 235 insertions(+), 233 deletions(-) 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 cf4e5975a..b986b0425 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 @@ -560,7 +560,7 @@ object JsonCodecSpec extends ZIOSpecDefault { 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 3197c461e..bded91ad0 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 @@ -17,9 +17,9 @@ import zio.{ Chunk, ZIO } object ProtobufCodec extends Codec { override def encoder[A](schema: Schema[A]): ZPipeline[Any, Nothing, A, Byte] = - ZPipeline.mapChunks(values => values.flatMap(Encoder.encode(None, schema, _))) + ZPipeline.mapChunks(values => values.flatMap(Encoder.process(schema, _))) - override def encode[A](schema: Schema[A]): A => Chunk[Byte] = a => Encoder.encode(None, schema, a) + override def encode[A](schema: Schema[A]): A => Chunk[Byte] = a => Encoder.process(schema, a) override def decoder[A](schema: Schema[A]): ZPipeline[Any, String, Byte, A] = ZPipeline.mapChunksZIO(chunk => ZIO.fromEither(Decoder.decode(schema, chunk).map(Chunk(_)))) @@ -171,172 +171,196 @@ object ProtobufCodec extends Codec { } } - object Encoder { + final private[codec] case class EncoderState(fieldNumber: Option[Int]) - import ProductEncoder._ + object Encoder extends ProcessValueWithSchema[Chunk[Byte], EncoderState] { 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(state: EncoderState, value: Any, typ: StandardType[Any]): Chunk[Byte] = + encodePrimitive(state.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( + state: EncoderState, + schema: Schema.Record[_], + value: ListMap[String, Chunk[Byte]] + ): Chunk[Byte] = { + val encodedRecord = Chunk.fromIterable(value.values).flatten + encodeKey(WireType.LengthDelimited(encodedRecord.size), state.fieldNumber) ++ encodedRecord } - private def encodeRecord( - fieldNumber: Option[Int], - structure: Seq[Schema.Field[_, _]], - data: ListMap[String, _] + override protected def processEnum( + state: EncoderState, + 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), state.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( + state: EncoderState, + 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), state.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), state.fieldNumber) ++ data } - @scala.annotation.tailrec + override protected def processDictionary( + state: EncoderState, + 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), state.fieldNumber) ++ encodeKey( + WireType.LengthDelimited(0), + Some(1) + ) + } else { + val chunk = value.map { + case (left, right) => + (Decoder.keyDecoder.run(left), Decoder.keyDecoder.run(right)) match { + case ( + Right((leftRemainder, (leftWireType, seqIndex))), + Right((rightRemainder, (rightWireType, _))) + ) => + val data = + encodeKey(leftWireType, Some(1)) ++ + leftRemainder ++ + encodeKey(rightWireType, Some(2)) ++ + rightRemainder + 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), state.fieldNumber) ++ data + } + + override protected def processSet( + state: EncoderState, + 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), state.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), state.fieldNumber) ++ data + } + + override protected def processEither( + state: EncoderState, + schema: Schema.Either[_, _], + value: Either[Chunk[Byte], Chunk[Byte]] + ): Chunk[Byte] = { + val encodedEither = value.merge + encodeKey(WireType.LengthDelimited(encodedEither.size), state.fieldNumber) ++ encodedEither + } + + override protected def processOption( + state: EncoderState, + 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), state.fieldNumber) ++ data + } + + override protected def processTuple( + state: EncoderState, + schema: Schema.Tuple2[_, _], + left: Chunk[Byte], + right: Chunk[Byte] + ): Chunk[Byte] = { + val data = left ++ right + encodeKey(WireType.LengthDelimited(data.size), state.fieldNumber) ++ data + } + + override protected def processDynamic(state: EncoderState, value: DynamicValue): Option[Chunk[Byte]] = + None + + override protected def fail(state: EncoderState, message: String): Chunk[Byte] = + throw new RuntimeException(message) + + override protected val initialState: EncoderState = EncoderState(None) + + override protected def stateForRecordField( + state: EncoderState, + index: Int, + field: Schema.Field[_, _] + ): EncoderState = + state.copy(fieldNumber = Some(index + 1)) + + override protected def stateForTuple(state: EncoderState, index: Int): EncoderState = + state.copy(fieldNumber = Some(index)) + + override protected def stateForEnumConstructor( + state: EncoderState, + index: Int, + c: Schema.Case[_, _] + ): EncoderState = + state.copy(fieldNumber = Some(index + 1)) + + override protected def stateForEither(state: EncoderState, e: Either[Unit, Unit]): EncoderState = + e match { + case Left(_) => state.copy(fieldNumber = Some(1)) + case Right(_) => state.copy(fieldNumber = Some(2)) + } + + override protected def stateForOption(state: EncoderState, o: Option[Unit]): EncoderState = + o match { + case None => state.copy(fieldNumber = Some(1)) + case Some(_) => state.copy(fieldNumber = Some(2)) + } + + override protected def stateForSequence( + state: EncoderState, + s: Schema.Sequence[_, _, _], + index: Int + ): EncoderState = + if (canBePacked(s.elementSchema)) state.copy(fieldNumber = None) + else state.copy(fieldNumber = Some(index + 1)) + + override protected def stateForMap(state: EncoderState, s: Schema.Map[_, _], index: Int): EncoderState = + if (canBePacked(s.keySchema <*> s.valueSchema)) state.copy(fieldNumber = None) + else state.copy(fieldNumber = Some(index + 1)) + + override protected def stateForSet(state: EncoderState, s: Schema.Set[_], index: Int): EncoderState = + if (canBePacked(s.elementSchema)) state.copy(fieldNumber = None) + else state.copy(fieldNumber = Some(index + 1)) + private def encodePrimitive[A]( fieldNumber: Option[Int], standardType: StandardType[A], @@ -362,11 +386,14 @@ object ProtobufCodec extends Codec { 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 @@ -392,23 +419,36 @@ object ProtobufCodec extends Codec { 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) => @@ -427,40 +467,6 @@ object ProtobufCodec extends Codec { 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) @@ -929,22 +935,6 @@ object ProtobufCodec extends Codec { ) } - //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) => - Encoder.encode(Some(fieldNumber + 1), schema.asInstanceOf[Schema[Any]], get(value)) - }) - .flatten - Encoder.encodeKey(Protobuf.WireType.LengthDelimited(encoded.size), fieldNumber) ++ encoded - } - } - } - //scalafmt: { maxColumn = 400, optIn.configStyleArguments = false } private[codec] object ProductDecoder { import Decoder.{ fail, keyDecoder, succeed } 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 2baf57030..17693c28a 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") { @@ -591,7 +591,7 @@ object ProtobufCodecSpec extends ZIOSpecDefault { // ed2 <- encodeAndDecodeNS(schema, value) } yield assertTrue(ed == Right(Chunk(value))) //&& assert(ed2)(equalTo(value)) } - } + } @@ TestAspect.sized(200) ), suite("Should successfully decode")( test("empty input") { @@ -1011,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/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala index 0c5c849c8..1112ff405 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala @@ -34,7 +34,9 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { protected def stateForEnumConstructor(state: State, index: Int, c: Schema.Case[_, _]): State protected def stateForEither(state: State, e: Either[Unit, Unit]): State protected def stateForOption(state: State, o: Option[Unit]): State - protected def stateForSequence(state: State): State + protected def stateForSequence(state: State, schema: Schema.Sequence[_, _, _], index: Int): State + protected def stateForMap(state: State, schema: Schema.Map[_, _], index: Int): State + protected def stateForSet(state: State, schema: Schema.Set[_], index: Int): State def process[A](schema: Schema[A], value: A): Target = { var currentSchema: Schema[_] = schema @@ -659,20 +661,22 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { val inputChunk = toChunk.asInstanceOf[Any => Chunk[Any]](currentValue) val resultChunk = ChunkBuilder.make[Target](inputChunk.size) - def processNext(inputIdx: Int): Unit = + def processNext(inputIdx: Int): Unit = { + stateStack = stateStack.tail if (inputIdx == inputChunk.size) { - stateStack = stateStack.tail finishWith(processSequence(state, s, resultChunk.result())) } else { currentSchema = schema currentValue = inputChunk(inputIdx) + pushState(stateForSequence(state, s, inputIdx)) push { dv => resultChunk += dv processNext(inputIdx + 1) } } + } - pushState(stateForSequence(state)) + pushState(stateForSequence(state, s, 0)) processNext(0) case s @ Schema.Map(ks: Schema[k], vs: Schema[v], _) => @@ -687,13 +691,13 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { val currentTuple = inputChunk(inputIdx) currentValue = currentTuple._1 - pushState(stateForSequence(state)) + pushState(stateForMap(state, s, inputIdx)) push { (a: Target) => stateStack = stateStack.tail currentSchema = vs currentValue = currentTuple._2 - pushState(stateForSequence(state)) + pushState(stateForMap(state, s, inputIdx)) push { (b: Target) => stateStack = stateStack.tail val pair = (a, b) @@ -709,20 +713,22 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { val inputChunk = Chunk.fromIterable(currentValue.asInstanceOf[Set[a]]) val resultChunk = ChunkBuilder.make[Target](inputChunk.size) - def processNext(inputIdx: Int): Unit = + def processNext(inputIdx: Int): Unit = { + stateStack = stateStack.tail if (inputIdx == inputChunk.size) { - stateStack = stateStack.tail finishWith(processSet(state, s, resultChunk.result().toSet)) } else { currentSchema = as currentValue = inputChunk(inputIdx) + pushState(stateForSet(state, s, inputIdx)) push { dv => resultChunk += dv processNext(inputIdx + 1) } } + } - pushState(stateForSequence(state)) + pushState(stateForSet(state, s, 0)) processNext(0) case s: Schema.Either[l, r] => @@ -1326,6 +1332,12 @@ trait ProcessSchemaAndValueWithoutState[Target <: AnyRef] extends ProcessValueWi override protected def stateForTuple(state: Unit, index: Int): Unit = () - override protected def stateForSequence(state: Unit): Unit = + override protected def stateForSequence(state: Unit, schema: Schema.Sequence[_, _, _], index: Int): Unit = + () + + override protected def stateForMap(state: Unit, schema: Schema.Map[_, _], index: Int): Unit = + () + + override protected def stateForSet(state: Unit, schema: Schema.Set[_], index: Int): Unit = () } From bc700b675a374d76ca50530c7251bc808333d3cd Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Fri, 28 Oct 2022 11:46:47 -0400 Subject: [PATCH 07/18] Thrift fixes --- .../src/main/scala/zio/schema/codec/ThriftCodec.scala | 10 ++++++++-- .../scala-2/zio/schema/codec/ThriftCodecSpec.scala | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) 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 4a6c1e9a5..ba22a9da2 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 @@ -188,7 +188,7 @@ object ThriftCodec extends Codec { override protected def processSet(state: State, schema: Schema.Set[_], value: Set[Command]): Command = Command.Sequence( - Command.WriteFieldBegin(state.fieldNumber, TType.LIST), + Command.WriteFieldBegin(state.fieldNumber, TType.SET), Command.WriteSetBegin(getType(schema.elementSchema), value.size) ) ++ Chunk.fromIterable(value) @@ -253,7 +253,13 @@ object ThriftCodec extends Codec { override protected def stateForTuple(state: State, index: Int): State = state.copy(fieldNumber = Some(index.toShort)) - override protected def stateForSequence(state: State): State = + override protected def stateForSequence(state: State, schema: Schema.Sequence[_, _, _], index: Int): State = + state.copy(fieldNumber = None) + + override protected def stateForMap(state: State, schema: Schema.Map[_, _], index: Int): State = + state.copy(fieldNumber = None) + + override protected def stateForSet(state: State, schema: Schema.Set[_], index: Int): State = state.copy(fieldNumber = None) def encode[A](schema: Schema[A], value: A): Chunk[Byte] = { 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 8a43dc071..0f9b36d9f 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 @@ -148,7 +148,7 @@ object ThriftCodecSpec extends ZIOSpecDefault { for { e <- encode(schemaFail, StringValue("foo")).map(_.size).exit e2 <- encodeNS(schemaFail, StringValue("foo")).map(_.size).exit - } yield assert(e)(fails(anything)) && assert(e2)(fails(anything)) + } yield assert(e)(dies(anything)) && assert(e2)(dies(anything)) } ), suite("Should successfully encode and decode")( From 712d15a1b5b603c86e87627e504d337672887281 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Fri, 28 Oct 2022 11:48:40 -0400 Subject: [PATCH 08/18] Scalafix --- .../shared/src/main/scala/zio/schema/DynamicValue.scala | 7 ++++--- .../src/main/scala/zio/schema/ProcessSchemaAndValue.scala | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) 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 d3adf2b97..4ca58c5b3 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala @@ -1,14 +1,15 @@ package zio.schema -import zio.schema.meta.{ MetaSchema, Migration } -import zio.{ Chunk, Unsafe } - import java.math.{ BigDecimal, BigInteger } import java.time._ import java.time.format.DateTimeFormatter import java.util.UUID + import scala.collection.immutable.ListMap +import zio.schema.meta.{ MetaSchema, Migration } +import zio.{ Chunk, Unsafe } + sealed trait DynamicValue { self => diff --git a/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala index 1112ff405..fb1ed36ce 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala @@ -1,9 +1,9 @@ package zio.schema -import zio.{ Chunk, ChunkBuilder } - import scala.collection.immutable.ListMap +import zio.{ Chunk, ChunkBuilder } + trait ProcessValueWithSchema[Target <: AnyRef, State] { protected def processPrimitive(state: State, value: Any, typ: StandardType[Any]): Target From 7f52196dbd2b316a64ed70484a2d69d096d97db1 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Fri, 28 Oct 2022 12:40:42 -0400 Subject: [PATCH 09/18] Side-effecting thrift encoder --- .../scala/zio/schema/codec/ThriftCodec.scala | 456 ++++++++---------- .../zio/schema/ProcessSchemaAndValue.scala | 39 +- 2 files changed, 247 insertions(+), 248 deletions(-) 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 ba22a9da2..3e8339b30 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 @@ -139,95 +139,100 @@ object ThriftCodec extends Codec { ) } - // TODO: delete Encoder.Command and write directly in processXXX - class Encoder extends ProcessValueWithSchema[Encoder.Command, Encoder.State] { + class Encoder extends ProcessValueWithSchema[Unit, Encoder.State] { import Encoder._ - override protected def processPrimitive(state: State, value: Any, typ: StandardType[Any]): Encoder.Command = - Command.Sequence(Command.WriteFieldBegin(state.fieldNumber, getPrimitiveType(typ))) ++ - writePrimitiveType(typ, value) + override protected def processPrimitive(state: State, value: Any, typ: StandardType[Any]): Unit = { + writeFieldBegin(state.fieldNumber, getPrimitiveType(typ)) + writePrimitiveType(typ, value) + } + + override protected def startProcessingRecord(state: State, schema: Schema.Record[_]): Unit = + writeFieldBegin(state.fieldNumber, TType.STRUCT) override protected def processRecord( state: State, schema: Schema.Record[_], - value: ListMap[String, Command] - ): Command = - Command.Sequence(Command.WriteFieldBegin(state.fieldNumber, TType.STRUCT)) ++ - Chunk.fromIterable(value.values) ++ - Command.WriteFieldEnd - - override protected def processEnum(state: State, schema: Schema.Enum[_], tuple: (String, Command)): Command = - Command.Sequence( - Command.WriteFieldBegin(state.fieldNumber, TType.STRUCT), - tuple._2, - Command.WriteFieldEnd - ) + value: ListMap[String, Unit] + ): Unit = + writeFieldEnd() + + override protected def startProcessingEnum(state: State, schema: Schema.Enum[_]): Unit = + writeFieldBegin(state.fieldNumber, TType.STRUCT) + + override protected def processEnum(state: State, schema: Schema.Enum[_], tuple: (String, Unit)): Unit = + writeFieldEnd() + + override protected def startProcessingSequence(state: State, schema: Schema.Sequence[_, _, _], size: Int): Unit = { + writeFieldBegin(state.fieldNumber, TType.LIST) + writeListBegin(getType(schema.elementSchema), size) + } override protected def processSequence( state: State, schema: Schema.Sequence[_, _, _], - value: Chunk[Command] - ): Command = - Command.Sequence( - Command.WriteFieldBegin(state.fieldNumber, TType.LIST), - Command.WriteListBegin(getType(schema.elementSchema), value.size) - ) ++ value + value: Chunk[Unit] + ): Unit = {} + + override protected def startProcessingDictionary(state: State, schema: Schema.Map[_, _], size: Int): Unit = { + writeFieldBegin(state.fieldNumber, TType.MAP) + writeMapBegin(getType(schema.keySchema), getType(schema.valueSchema), size) + } override protected def processDictionary( state: State, schema: Schema.Map[_, _], - value: Chunk[(Command, Command)] - ): Command = - value - .foldLeft( - Command.Sequence( - Command.WriteFieldBegin(state.fieldNumber, TType.MAP), - Command.WriteMapBegin(getType(schema.keySchema), getType(schema.valueSchema), value.size) - ) - ) { case (c, (c1, c2)) => c ++ c1 ++ c2 } + value: Chunk[(Unit, Unit)] + ): Unit = {} + + override protected def startProcessingSet(state: State, schema: Schema.Set[_], size: Int): Unit = { + writeFieldBegin(state.fieldNumber, TType.SET) + writeSetBegin(getType(schema.elementSchema), size) + } - override protected def processSet(state: State, schema: Schema.Set[_], value: Set[Command]): Command = - Command.Sequence( - Command.WriteFieldBegin(state.fieldNumber, TType.SET), - Command.WriteSetBegin(getType(schema.elementSchema), value.size) - ) ++ Chunk.fromIterable(value) + override protected def processSet(state: State, schema: Schema.Set[_], value: Set[Unit]): Unit = {} + + override protected def startProcessingEither(state: State, schema: Schema.Either[_, _]): Unit = + writeFieldBegin(state.fieldNumber, TType.STRUCT) override protected def processEither( state: State, schema: Schema.Either[_, _], - value: Either[Command, Command] - ): Command = - Command.Sequence(Command.WriteFieldBegin(state.fieldNumber, TType.STRUCT)) ++ value.merge - - override protected def processOption(state: State, schema: Schema.Optional[_], value: Option[Command]): Command = - Command.Sequence(Command.WriteFieldBegin(state.fieldNumber, TType.STRUCT)) ++ - (value match { - case Some(value) => value - case None => - processPrimitive( - state.copy(fieldNumber = Some(1)), - (), - StandardType.UnitType.asInstanceOf[StandardType[Any]] - ) - }) ++ Command.WriteFieldEnd + value: Either[Unit, Unit] + ): Unit = + writeFieldEnd() + + override def startProcessingOption(state: State, schema: Schema.Optional[_]): Unit = + writeFieldBegin(state.fieldNumber, TType.STRUCT) + + override protected def processOption(state: State, schema: Schema.Optional[_], value: Option[Unit]): Unit = { + value match { + case None => + processPrimitive( + state.copy(fieldNumber = Some(1)), + (), + StandardType.UnitType.asInstanceOf[StandardType[Any]] + ) + case _ => + } + writeFieldEnd() + } + + override protected def startProcessingTuple(state: State, schema: Schema.Tuple2[_, _]): Unit = + writeFieldBegin(state.fieldNumber, TType.STRUCT) override protected def processTuple( state: State, schema: Schema.Tuple2[_, _], - left: Command, - right: Command - ): Command = - Command.Sequence( - Command.WriteFieldBegin(state.fieldNumber, TType.STRUCT), - left, - right, - Command.WriteFieldEnd - ) + left: Unit, + right: Unit + ): Unit = + writeFieldEnd() - override protected def fail(state: State, message: String): Command = - Command.Fail(message) + override protected def fail(state: State, message: String): Unit = + fail(message) - override protected def processDynamic(state: State, value: DynamicValue): Option[Command] = + override protected def processDynamic(state: State, value: DynamicValue): Option[Unit] = None override protected val initialState: State = State(None) @@ -262,54 +267,160 @@ object ThriftCodec extends Codec { override protected def stateForSet(state: State, schema: Schema.Set[_], index: Int): State = state.copy(fieldNumber = None) - def encode[A](schema: Schema[A], value: A): Chunk[Byte] = { - val command = process(schema, value) - val write = new ChunkTransport.Write() - val p = new TBinaryProtocol(write) - - execute(p, command) - + private[codec] def encode[A](schema: Schema[A], value: A): Chunk[Byte] = { + process(schema, value) write.chunk } - } - object Encoder { - final case class State(fieldNumber: Option[Short]) + private val write = new ChunkTransport.Write() + private val p = new TBinaryProtocol(write) - sealed trait Command + private def writeFieldBegin(fieldNumber: Option[Short], ttype: Byte): Unit = + fieldNumber match { + case Some(num) => + p.writeFieldBegin( + new TField("", ttype, num) + ) + case None => + } - object Command { - final case class Sequence(commands: Chunk[Command]) extends Command { self => + private def writeFieldEnd(): Unit = + p.writeFieldStop() - def ++(other: Command): Sequence = - other match { - case Sequence(otherCommands) => Sequence(commands ++ otherCommands) - case _ => Sequence(commands :+ other) - } + private def writeString(value: String): Unit = + p.writeString(value) - def ++(others: Chunk[Command]): Sequence = - others.foldLeft(self)(_ ++ _) - } + 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) + + 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() - object Sequence { - def apply(commands: Command*): Sequence = Sequence(Chunk.fromIterable(commands)) + 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") } - final case class WriteFieldBegin(fieldNumber: Option[Short], ttype: Byte) extends Command - case object WriteFieldEnd extends Command - case object Noop extends Command - final case class WriteString(value: String) extends Command - final case class WriteBool(value: Boolean) extends Command - final case class WriteByte(value: Byte) extends Command - final case class WriteI16(value: Short) extends Command - final case class WriteI32(value: Int) extends Command - final case class WriteI64(value: Long) extends Command - final case class WriteDouble(value: Double) extends Command - final case class WriteBinary(value: Chunk[Byte]) extends Command - final case class WriteListBegin(ttype: Byte, count: Int) extends Command - final case class WriteSetBegin(ttype: Byte, count: Int) extends Command - final case class WriteMapBegin(keyType: Byte, valueType: Byte, count: Int) extends Command - final case class Fail(message: String) extends Command - } + } + + object Encoder { + final case class State(fieldNumber: Option[Short]) private def getPrimitiveType[A](standardType: StandardType[A]): Byte = standardType match { @@ -374,145 +485,6 @@ object ThriftCodec extends Codec { case _: Schema.Enum[A] => TType.STRUCT case _ => TType.VOID } - - private def writePrimitiveType[A](standardType: StandardType[A], value: A): Command = - (standardType, value) match { - case (StandardType.UnitType, _) => - Command.Noop - case (StandardType.StringType, str: String) => - Command.WriteString(str) - case (StandardType.BoolType, b: Boolean) => - Command.WriteBool(b) - case (StandardType.ByteType, v: Byte) => - Command.WriteByte(v) - case (StandardType.ShortType, v: Short) => - Command.WriteI16(v) - case (StandardType.IntType, v: Int) => - Command.WriteI32(v) - case (StandardType.LongType, v: Long) => - Command.WriteI64(v) - case (StandardType.FloatType, v: Float) => - Command.WriteDouble(v.toDouble) - case (StandardType.DoubleType, v: Double) => - Command.WriteDouble(v.toDouble) - case (StandardType.BigIntegerType, v: java.math.BigInteger) => - Command.WriteBinary(Chunk.fromArray(v.toByteArray)) - case (StandardType.BigDecimalType, v: java.math.BigDecimal) => - val unscaled = v.unscaledValue() - val precision = v.precision() - val scale = v.scale() - Command.Sequence(Command.WriteFieldBegin(Some(1), getPrimitiveType(StandardType.BigIntegerType))) ++ - writePrimitiveType(StandardType.BigIntegerType, unscaled) ++ - Command.WriteFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) ++ - writePrimitiveType(StandardType.IntType, precision) ++ - Command.WriteFieldBegin(Some(3), getPrimitiveType(StandardType.IntType)) ++ - writePrimitiveType(StandardType.IntType, scale) ++ - Command.WriteFieldEnd - - case (StandardType.BinaryType, bytes: Chunk[Byte]) => - Command.WriteBinary(Chunk.fromArray(bytes.toArray)) - case (StandardType.CharType, c: Char) => - Command.WriteString(c.toString) - case (StandardType.UUIDType, u: UUID) => - Command.WriteString(u.toString) - case (StandardType.DayOfWeekType, v: DayOfWeek) => - Command.WriteByte(v.getValue.toByte) - case (StandardType.MonthType, v: Month) => - Command.WriteByte(v.getValue.toByte) - case (StandardType.MonthDayType, v: MonthDay) => - Command.Sequence(Command.WriteFieldBegin(Some(1), getPrimitiveType(StandardType.IntType))) ++ - writePrimitiveType(StandardType.IntType, v.getMonthValue) ++ - Command.WriteFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) ++ - writePrimitiveType(StandardType.IntType, v.getDayOfMonth) ++ - Command.WriteFieldEnd - - case (StandardType.PeriodType, v: Period) => - Command.Sequence(Command.WriteFieldBegin(Some(1), getPrimitiveType(StandardType.IntType))) ++ - writePrimitiveType(StandardType.IntType, v.getYears) ++ - Command.WriteFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) ++ - writePrimitiveType(StandardType.IntType, v.getMonths) ++ - Command.WriteFieldBegin(Some(3), getPrimitiveType(StandardType.IntType)) ++ - writePrimitiveType(StandardType.IntType, v.getDays) ++ - Command.WriteFieldEnd - - case (StandardType.YearType, v: Year) => - Command.WriteI32(v.getValue) - case (StandardType.YearMonthType, v: YearMonth) => - Command.Sequence(Command.WriteFieldBegin(Some(1), getPrimitiveType(StandardType.IntType))) ++ - writePrimitiveType(StandardType.IntType, v.getYear) ++ - Command.WriteFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) ++ - writePrimitiveType(StandardType.IntType, v.getMonthValue) ++ - Command.WriteFieldEnd - case (StandardType.ZoneIdType, v: ZoneId) => - Command.WriteString(v.getId) - case (StandardType.ZoneOffsetType, v: ZoneOffset) => - Command.WriteI32(v.getTotalSeconds) - case (StandardType.DurationType, v: Duration) => - Command.Sequence(Command.WriteFieldBegin(Some(1), getPrimitiveType(StandardType.LongType))) ++ - writePrimitiveType(StandardType.LongType, v.getSeconds) ++ - Command.WriteFieldBegin(Some(2), getPrimitiveType(StandardType.IntType)) ++ - writePrimitiveType(StandardType.IntType, v.getNano) ++ - Command.WriteFieldEnd - - case (StandardType.InstantType(formatter), v: Instant) => - Command.WriteString(formatter.format(v)) - case (StandardType.LocalDateType(formatter), v: LocalDate) => - Command.WriteString(formatter.format(v)) - case (StandardType.LocalTimeType(formatter), v: LocalTime) => - Command.WriteString(formatter.format(v)) - case (StandardType.LocalDateTimeType(formatter), v: LocalDateTime) => - Command.WriteString(formatter.format(v)) - case (StandardType.OffsetTimeType(formatter), v: OffsetTime) => - Command.WriteString(formatter.format(v)) - case (StandardType.OffsetDateTimeType(formatter), v: OffsetDateTime) => - Command.WriteString(formatter.format(v)) - case (StandardType.ZonedDateTimeType(formatter), v: ZonedDateTime) => - Command.WriteString(formatter.format(v)) - case (_, _) => - Command.Fail(s"No encoder for $standardType") - } - - private def execute(p: TBinaryProtocol, command: Command): Unit = - command match { - case Command.Sequence(commands) => - for (command <- commands) - execute(p, command) - case Command.WriteFieldBegin(fieldNumber, ttype) => - fieldNumber match { - case Some(num) => - p.writeFieldBegin( - new TField("", ttype, num) - ) - case None => - } - case Command.WriteFieldEnd => - p.writeFieldStop() - case Command.Noop => - case Command.WriteString(value) => - p.writeString(value) - case Command.WriteBool(value) => - p.writeBool(value) - case Command.WriteByte(value) => - p.writeByte(value) - case Command.WriteI16(value) => - p.writeI16(value) - case Command.WriteI32(value) => - p.writeI32(value) - case Command.WriteI64(value) => - p.writeI64(value) - case Command.WriteDouble(value) => - p.writeDouble(value) - case Command.WriteBinary(value) => - p.writeBinary(ByteBuffer.wrap(value.toArray)) - case Command.WriteListBegin(ttype, count) => - p.writeListBegin(new TList(ttype, count)) - case Command.WriteSetBegin(ttype, count) => - p.writeSetBegin(new TSet(ttype, count)) - case Command.WriteMapBegin(keyType, valueType, count) => - p.writeMapBegin(new TMap(keyType, valueType, count)) - case Command.Fail(message) => - throw new RuntimeException(message) - } } class Decoder(chunk: Chunk[Byte]) { diff --git a/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala index fb1ed36ce..9736aa1d5 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala @@ -1,26 +1,43 @@ package zio.schema import scala.collection.immutable.ListMap - import zio.{ Chunk, ChunkBuilder } -trait ProcessValueWithSchema[Target <: AnyRef, State] { +import scala.annotation.nowarn + +trait ProcessValueWithSchema[Target, State] { protected def processPrimitive(state: State, value: Any, typ: StandardType[Any]): Target + @nowarn protected def startProcessingRecord(state: State, schema: Schema.Record[_]): Unit = {} + protected def processRecord(state: State, schema: Schema.Record[_], value: ListMap[String, Target]): Target + @nowarn protected def startProcessingEnum(state: State, schema: Schema.Enum[_]): Unit = {} + protected def processEnum(state: State, schema: Schema.Enum[_], tuple: (String, Target)): Target + @nowarn protected def startProcessingSequence(state: State, schema: Schema.Sequence[_, _, _], size: Int): Unit = {} + protected def processSequence(state: State, schema: Schema.Sequence[_, _, _], value: Chunk[Target]): Target + @nowarn protected def startProcessingDictionary(state: State, schema: Schema.Map[_, _], size: Int): Unit = {} + protected def processDictionary(state: State, schema: Schema.Map[_, _], value: Chunk[(Target, Target)]): Target + @nowarn protected def startProcessingSet(state: State, schema: Schema.Set[_], size: Int): Unit = {} + protected def processSet(state: State, schema: Schema.Set[_], value: Set[Target]): Target + @nowarn protected def startProcessingEither(state: State, schema: Schema.Either[_, _]): Unit = {} + protected def processEither(state: State, schema: Schema.Either[_, _], value: Either[Target, Target]): Target + @nowarn protected def startProcessingOption(state: State, schema: Schema.Optional[_]): Unit = {} + protected def processOption(state: State, schema: Schema.Optional[_], value: Option[Target]): Target + @nowarn protected def startProcessingTuple(state: State, schema: Schema.Tuple2[_, _]): Unit = {} + protected def processTuple(state: State, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target protected def processDynamic(state: State, value: DynamicValue): Option[Target] @@ -41,7 +58,7 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { def process[A](schema: Schema[A], value: A): Target = { var currentSchema: Schema[_] = schema var currentValue: Any = value - var result: Target = null.asInstanceOf[Target] + var result: Option[Target] = None var stack: List[Target => Unit] = List.empty[Target => Unit] var stateStack: List[State] = List(initialState) @@ -57,7 +74,7 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { stack = stack.tail head(resultValue) } else { - result = resultValue + result = Some(resultValue) } def fields(s: Schema.Record[_], record: Any, fs: Schema.Field[_, _]*): Unit = { @@ -90,10 +107,13 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { processNext(index + 1, remaining) } + startProcessingRecord(stateStack.head, s) processNext(0, fs.toList) } def enumCases(s: Schema.Enum[_], cs: Schema.Case[_, _]*): Unit = { + startProcessingEnum(stateStack.head, s) + var found = false val it = cs.iterator var index = 0 @@ -120,7 +140,7 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { } } - while (result eq null) { + while (result.isEmpty) { val state = stateStack.head currentSchema match { @@ -167,6 +187,7 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { processNext(index + 1, remaining) } + startProcessingRecord(state, s) processNext(0, structureChunk.toList) case s @ Schema.Enum1(_, case1, _) => @@ -676,6 +697,7 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { } } + startProcessingSequence(state, s, inputChunk.size) pushState(stateForSequence(state, s, 0)) processNext(0) @@ -707,6 +729,7 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { } } + startProcessingDictionary(state, s, inputChunk.size) processNext(0) case s @ Schema.Set(as: Schema[a], _) => @@ -728,10 +751,12 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { } } + startProcessingSet(state, s, inputChunk.size) pushState(stateForSet(state, s, 0)) processNext(0) case s: Schema.Either[l, r] => + startProcessingEither(state, s) currentValue.asInstanceOf[Either[l, r]] match { case Left(value: l) => currentValue = value @@ -752,6 +777,7 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { } case s: Schema.Tuple2[a, b] => + startProcessingTuple(state, s) val (a: a, b: b) = currentValue.asInstanceOf[(a, b)] currentValue = a currentSchema = s.left @@ -768,6 +794,7 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { } case s: Schema.Optional[a] => + startProcessingOption(state, s) currentValue.asInstanceOf[Option[a]] match { case Some(value: a) => currentValue = value @@ -1246,7 +1273,7 @@ trait ProcessValueWithSchema[Target <: AnyRef, State] { } } } - result + result.get } } From b90e0e589e40aeaa034e085ffb572f99e8e51560 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Fri, 28 Oct 2022 16:46:38 -0400 Subject: [PATCH 10/18] Stack safe thrift decoder --- .../scala/zio/schema/codec/ThriftCodec.scala | 1687 +++-------------- .../zio/schema/CreateValueFromSchema.scala | 891 +++++++++ .../zio/schema/ProcessSchemaAndValue.scala | 2 +- 3 files changed, 1199 insertions(+), 1381 deletions(-) create mode 100644 zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala 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 3e8339b30..f301ccf26 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,27 +1,18 @@ package zio.schema.codec +import org.apache.thrift.protocol._ +import zio.schema._ +import zio.stream.ZPipeline +import zio.{ Chunk, Unsafe, ZIO } + import java.math.{ BigInteger, MathContext } import java.nio.ByteBuffer import java.time._ import java.util.UUID - import scala.annotation.tailrec import scala.collection.immutable.ListMap +import scala.util.Try import scala.util.control.NonFatal -import scala.util.{ Failure, Success, Try } - -import org.apache.thrift.protocol._ - -import zio.schema._ -import zio.schema.codec.ThriftCodec.Thrift.{ - bigDecimalStructure, - durationStructure, - monthDayStructure, - periodStructure, - yearMonthStructure -} -import zio.stream.ZPipeline -import zio.{ Chunk, ChunkBuilder, ZIO } object ThriftCodec extends Codec { override def encoder[A](schema: Schema[A]): ZPipeline[Any, Nothing, A, Byte] = { @@ -35,8 +26,9 @@ object ThriftCodec extends Codec { ZPipeline.mapChunksZIO { chunk => ZIO.fromEither( new Decoder(chunk) - .decode(Chunk.empty, schema) + .create(schema) .map(Chunk(_)) + .map(_.asInstanceOf[Chunk[A]]) .left .map(err => s"Error at path /${err.path.mkString(".")}: ${err.error}") ) @@ -50,7 +42,8 @@ object ThriftCodec extends Codec { Left("No bytes to decode") else new Decoder(ch) - .decode(Chunk.empty, schema) + .create(schema) + .map(_.asInstanceOf[A]) .left .map( err => s"Error at path /${err.path.mkString(".")}: ${err.error}" @@ -487,18 +480,27 @@ object ThriftCodec extends Codec { } } - class Decoder(chunk: Chunk[Byte]) { - type Path = Chunk[String] - case class Error(path: Path, error: String) - type Result[A] = scala.util.Either[Error, A] - type PrimitiveResult[A] = Path => Result[A] + case class Error(path: Path, error: String) + + type Path = Chunk[String] + type Result[A] = scala.util.Either[Error, A] + type PrimitiveResult[A] = Path => Result[A] + + final case class DecoderState(path: Path, remainingCount: Option[Int]) + + class Decoder(chunk: Chunk[Byte]) extends CreateValueFromSchema[Result[Any], DecoderState] { 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(Error(path, failure)) + def flip[A](as: Chunk[Result[A]]): Result[Chunk[A]] = + as.foldLeft[Result[Chunk[A]]](Right(Chunk.empty[A])) { + case (Right(as), Right(a)) => Right(as :+ a) + case (Left(failure), _) => Left(failure) + case (_, Left(failure)) => Left(failure) + } def decodePrimitive[A](f: TProtocol => A, name: String): PrimitiveResult[A] = path => @@ -536,1428 +538,353 @@ object ThriftCodec extends Codec { def decodeBinary: PrimitiveResult[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}") - } - - 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() - } - 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 => Error(path, msg))) - - private def primitiveDecoder[A](path: Path, standardType: StandardType[A]): Result[A] = - standardType match { + override protected def createPrimitive(state: DecoderState, typ: StandardType[_]): Result[Any] = + typ 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.StringType => decodeString(state.path) + case StandardType.BoolType => decodeBoolean(state.path) + case StandardType.ByteType => decodeByte(state.path) + case StandardType.ShortType => decodeShort(state.path) + case StandardType.IntType => decodeInt(state.path) + case StandardType.LongType => decodeLong(state.path) + case StandardType.FloatType => decodeFloat(state.path) + case StandardType.DoubleType => decodeDouble(state.path) + case StandardType.BigIntegerType => decodeBigInteger(state.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") + p.readFieldBegin() + decodeBigInteger(state.path).flatMap { unscaled => + p.readFieldBegin() + decodeInt(state.path).flatMap { precision => + p.readFieldBegin() + decodeInt(state.path).map { scale => + p.readFieldBegin() + new java.math.BigDecimal(unscaled, scale, new java.math.MathContext(precision)) + } } } - case StandardType.BinaryType => decodeBinary(path) + case StandardType.BinaryType => decodeBinary(state.path) case StandardType.CharType => - decodeString(path).flatMap( + decodeString(state.path).flatMap( decoded => - if (decoded.size == 1) + if (decoded.length == 1) succeed(decoded.charAt(0)) else { - fail(path, s"""Expected character, found string "$decoded"""") + fail(state, s"""Expected character, found string "$decoded"""") } ) case StandardType.UUIDType => - decodeString(path).flatMap { uuid => + decodeString(state.path).flatMap { uuid => try succeed(UUID.fromString(uuid)) catch { - case NonFatal(_) => fail(path, "Invalid UUID string") + case NonFatal(_) => fail(state, "Invalid UUID string") } } case StandardType.DayOfWeekType => - decodeByte(path).map(_.toInt).map(DayOfWeek.of) + decodeByte(state.path).map(_.toInt).map(DayOfWeek.of) case StandardType.MonthType => - decodeByte(path).map(_.toInt).map(Month.of) + decodeByte(state.path).map(_.toInt).map(Month.of) case StandardType.MonthDayType => - decodeRecord(path, monthDayStructure) - .map(data => MonthDay.of(data.getOrElse(1, 0).asInstanceOf[Int], data.getOrElse(2, 0).asInstanceOf[Int])) + p.readFieldBegin() + decodeInt(state.path).flatMap { month => + p.readFieldBegin() + decodeInt(state.path).map { day => + 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() + decodeInt(state.path).flatMap { year => + p.readFieldBegin() + decodeInt(state.path).flatMap { month => + p.readFieldBegin() + decodeInt(state.path).map { day => + p.readFieldBegin() + Period.of(year, month, day) + } + } + } case StandardType.YearType => - decodeInt(path).map(_.intValue).map(Year.of) + decodeInt(state.path).map(_.intValue).map(Year.of) 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() + decodeInt(state.path).flatMap { year => + p.readFieldBegin() + decodeInt(state.path).map { month => + p.readFieldBegin() + YearMonth.of(year, month) + } + } + case StandardType.ZoneIdType => decodeString(state.path).map(ZoneId.of) case StandardType.ZoneOffsetType => - decodeInt(path) + decodeInt(state.path) .map(_.intValue) .map(ZoneOffset.ofTotalSeconds) 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() + decodeLong(state.path).flatMap { seconds => + p.readFieldBegin() + decodeInt(state.path).map { nano => + p.readFieldBegin() + Duration.ofSeconds(seconds, nano) + } + } case StandardType.InstantType(formatter) => - decodeString(path).map(v => Instant.from(formatter.parse(v))) + decodeString(state.path).map(v => Instant.from(formatter.parse(v))) case StandardType.LocalDateType(formatter) => - decodeString(path).map(LocalDate.parse(_, formatter)) + decodeString(state.path).map(LocalDate.parse(_, formatter)) case StandardType.LocalTimeType(formatter) => - decodeString(path).map(LocalTime.parse(_, formatter)) + decodeString(state.path).map(LocalTime.parse(_, formatter)) case StandardType.LocalDateTimeType(formatter) => - decodeString(path).map(LocalDateTime.parse(_, formatter)) + decodeString(state.path).map(LocalDateTime.parse(_, formatter)) case StandardType.OffsetTimeType(formatter) => - decodeString(path).map(OffsetTime.parse(_, formatter)) + decodeString(state.path).map(OffsetTime.parse(_, formatter)) case StandardType.OffsetDateTimeType(formatter) => - decodeString(path).map(OffsetDateTime.parse(_, formatter)) + decodeString(state.path).map(OffsetDateTime.parse(_, formatter)) case StandardType.ZonedDateTimeType(formatter) => - decodeString(path).map(ZonedDateTime.parse(_, formatter)) - case _ => fail(path, s"Unsupported primitive type $standardType") + decodeString(state.path).map(ZonedDateTime.parse(_, formatter)) + case _ => fail(state, 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(state: DecoderState, record: Schema.Record[_]): DecoderState = + state + + override protected def startReadingField( + state: DecoderState, + record: Schema.Record[_], + index: Int + ): (DecoderState, Option[Int]) = { + val tfield = p.readFieldBegin() + (state, if (tfield.`type` == TType.STOP) None else Some(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( + state: DecoderState, + record: Schema.Record[_], + values: Chunk[(Int, Result[Any])] + ): Result[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) => succeed(value) + case None => fail(state, s"Missing value for field ${field.name}") + } } + } + Unsafe.unsafe { implicit u => + flip(allValues).flatMap { vs => + record.construct(vs) match { + case Left(message) => fail(state, message) + case Right(value) => succeed(value) } } + } + } - readFields(ListMap.empty) + override protected def startCreatingEnum( + state: DecoderState, + cases: Chunk[Schema.Case[_, _]] + ): (DecoderState, Int) = { + val readField = p.readFieldBegin() + val consIdx = readField.id - 1 + val subtypeCase = cases(consIdx) + (state.copy(path = state.path :+ s"[case:${subtypeCase.id}]"), consIdx) } - 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 createEnum( + state: DecoderState, + cases: Chunk[Schema.Case[_, _]], + index: Int, + value: Result[Any] + ): Result[Any] = { + value.foreach { _ => + p.readFieldBegin() + } + value } - 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(_.error, _.toString) - val value = r.fold(_.error, _.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 startCreatingSequence( + state: DecoderState, + schema: Schema.Sequence[_, _, _] + ): Option[DecoderState] = { + val begin = p.readListBegin() + if (begin.size == 0) None + else + Some(state.copy(remainingCount = 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 readOneSequenceElement( + state: DecoderState, + schema: Schema.Sequence[_, _, _] + ): (DecoderState, Boolean) = { + val updatedState = state.copy(remainingCount = state.remainingCount.map(_ - 1)) + val continue = updatedState.remainingCount.exists(_ > 0) + (updatedState, continue) } - 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 createSequence( + state: DecoderState, + schema: Schema.Sequence[_, _, _], + values: Chunk[Result[Any]] + ): Result[Any] = + flip(values).map(chunk => schema.fromChunk.asInstanceOf[Chunk[Any] => Any](chunk)) + + override protected def startCreatingDictionary( + state: DecoderState, + schema: Schema.Map[_, _] + ): Option[DecoderState] = { + val begin = p.readMapBegin() + if (begin.size == 0) None + else + Some(state.copy(remainingCount = Some(begin.size))) + } - private def unsafeDecodeFields[Z](path: Path, fields: Schema.Field[Z, _]*): Result[Array[Any]] = { - val buffer = Array.ofDim[Any](fields.size) - - @tailrec - def addFields(values: ListMap[Short, Any], index: Int): Result[Array[Any]] = - if (index >= fields.size) Right(buffer) - else { - val Schema.Field(label, schema, _, _, _, _) = 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 => fail(path :+ label, "Missing value") - } + override protected def readOneDictionaryElement( + state: DecoderState, + schema: Schema.Map[_, _] + ): (DecoderState, Boolean) = { + val updatedState = state.copy(remainingCount = state.remainingCount.map(_ - 1)) + val continue = updatedState.remainingCount.exists(_ > 0) + (updatedState, continue) + } + + override protected def createDictionary( + state: DecoderState, + schema: Schema.Map[_, _], + values: Chunk[(Result[Any], Result[Any])] + ): Result[Any] = + flip(values.map { + case (aa, bb) => + aa.flatMap { a => + bb.map { b => + (a, b) } } + }).map(_.toMap) - structDecoder(fields.map(_.schema), path).flatMap(addFields(_, 0)) - } - - @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])) - } - - 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] - ) + override protected def startCreatingSet(state: DecoderState, schema: Schema.Set[_]): Option[DecoderState] = { + val begin = p.readSetBegin() + if (begin.size == 0) None + else Some(state.copy(remainingCount = Some(begin.size))) + } - 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] - ) + override protected def readOneSetElement(state: DecoderState, schema: Schema.Set[_]): (DecoderState, Boolean) = { + val updatedState = state.copy(remainingCount = state.remainingCount.map(_ - 1)) + val continue = updatedState.remainingCount.exists(_ > 0) + (updatedState, continue) + } - 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] - ) + override protected def createSet( + state: DecoderState, + schema: Schema.Set[_], + values: Chunk[Result[Any]] + ): Result[Any] = + flip(values).map(_.toSet) + + override protected def startCreatingOptional( + state: DecoderState, + schema: Schema.Optional[_] + ): Option[DecoderState] = { + val field = p.readFieldBegin() + field.id match { + case 1 => None + case 2 => Some(state.copy(path = state.path :+ "Some")) + // TODO + } + } - 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] - ) + override protected def createOptional( + state: DecoderState, + schema: Schema.Optional[_], + value: Option[Result[Any]] + ): Result[Any] = { + p.readFieldBegin() + value match { + case Some(value) => value.map(Some(_)) + case None => succeed(None) + } + } - 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] - ) + override protected def startCreatingEither( + state: DecoderState, + schema: Schema.Either[_, _] + ): Either[DecoderState, DecoderState] = { + val readField = p.readFieldBegin() + readField.id match { + case 1 => Left(state.copy(path = state.path :+ "either:left")) + case 2 => Right(state.copy(path = state.path :+ "either:right")) + //case _ => fail(path, "Failed to decode either.") // TODO + } + } - 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] - ) + override protected def createEither( + state: DecoderState, + schema: Schema.Either[_, _], + value: Either[Result[Any], Result[Any]] + ): Result[Any] = + value match { + case Left(value) => value.map(Left(_)) + case Right(value) => value.map(Right(_)) + } - 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] - ) + override protected def startCreatingTuple(state: DecoderState, schema: Schema.Tuple2[_, _]): DecoderState = { + p.readFieldBegin() + state + } - 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] - ) + override protected def startReadingSecondTupleElement( + state: DecoderState, + schema: Schema.Tuple2[_, _] + ): DecoderState = { + p.readFieldBegin() + state + } - 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] - ) + override protected def createTuple( + state: DecoderState, + schema: Schema.Tuple2[_, _], + left: Result[Any], + right: Result[Any] + ): Result[Any] = { + p.readFieldBegin() + left.flatMap { l => + right.map { r => + (l, r) + } + } + } - 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] - ) + override protected def createDynamic(state: DecoderState): Option[Result[Any]] = + None - 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] - ) + override protected def transform( + state: DecoderState, + value: Result[Any], + f: Any => Either[String, Any] + ): Result[Any] = + value.flatMap { v => + f(v) match { + case Left(value) => fail(state, value) + case Right(value) => succeed(value) + } + } - 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] - ) + override protected def fail(state: DecoderState, message: String): Result[Any] = + Left(Error(state.path, message)) - 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] - ) + override protected val initialState: DecoderState = DecoderState(Chunk.empty, None) - 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/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala b/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala new file mode 100644 index 000000000..e5d7afa98 --- /dev/null +++ b/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala @@ -0,0 +1,891 @@ +package zio.schema + +import zio.{ Chunk, ChunkBuilder } + +trait CreateValueFromSchema[Target, State] { + + protected def createPrimitive(state: State, typ: StandardType[_]): Target + + protected def startCreatingRecord(state: State, record: Schema.Record[_]): State + protected def startReadingField(state: State, record: Schema.Record[_], index: Int): (State, Option[Int]) + protected def createRecord(state: State, record: Schema.Record[_], values: Chunk[(Int, Target)]): Target + + protected def startCreatingEnum(state: State, cases: Chunk[Schema.Case[_, _]]): (State, Int) + + protected def createEnum(state: State, cases: Chunk[Schema.Case[_, _]], index: Int, value: Target): Target + + protected def startCreatingSequence(state: State, schema: Schema.Sequence[_, _, _]): Option[State] + protected def readOneSequenceElement(state: State, schema: Schema.Sequence[_, _, _]): (State, Boolean) + protected def createSequence(state: State, schema: Schema.Sequence[_, _, _], values: Chunk[Target]): Target + + protected def startCreatingDictionary(state: State, schema: Schema.Map[_, _]): Option[State] + protected def readOneDictionaryElement(state: State, schema: Schema.Map[_, _]): (State, Boolean) + protected def createDictionary(state: State, schema: Schema.Map[_, _], values: Chunk[(Target, Target)]): Target + + protected def startCreatingSet(state: State, schema: Schema.Set[_]): Option[State] + protected def readOneSetElement(state: State, schema: Schema.Set[_]): (State, Boolean) + protected def createSet(state: State, schema: Schema.Set[_], values: Chunk[Target]): Target + + protected def startCreatingOptional(state: State, schema: Schema.Optional[_]): Option[State] + protected def createOptional(state: State, schema: Schema.Optional[_], value: Option[Target]): Target + + protected def startCreatingEither(state: State, schema: Schema.Either[_, _]): Either[State, State] + protected def createEither(state: State, schema: Schema.Either[_, _], value: Either[Target, Target]): Target + + protected def startCreatingTuple(state: State, schema: Schema.Tuple2[_, _]): State + protected def startReadingSecondTupleElement(state: State, schema: Schema.Tuple2[_, _]): State + protected def createTuple(state: State, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target + + protected def createDynamic(state: State): Option[Target] + + protected def transform(state: State, value: Target, f: Any => Either[String, Any]): Target + protected def fail(state: State, message: String): Target + + protected val initialState: State + + 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 stateStack: List[State] = List(initialState) + + 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 pushState(s: State): Unit = + stateStack = s :: stateStack + + def record(record: Schema.Record[_]): Unit = { + val values = ChunkBuilder.make[(Int, Target)](record.fields.size) + + def readField(index: Int): Unit = { + val (updatedState, fieldIndex) = startReadingField(stateStack.head, record, index) + stateStack = stateStack.tail + fieldIndex match { + case Some(idx) => + currentSchema = record.fields(idx).schema + pushState(updatedState) + push { field => + val elem = (idx, field) + values += elem + readField(index + 1) + } + case None => + finishWith(createRecord(stateStack.head, record, values.result())) + } + } + + pushState(startCreatingRecord(stateStack.head, record)) + readField(0) + } + + def enumCases(casesChunk: Chunk[Schema.Case[_, _]]): Unit = { + val (newState, index) = startCreatingEnum(stateStack.head, casesChunk) + currentSchema = casesChunk(index).schema + pushState(newState) + push { value => + stateStack = stateStack.tail + finishWith(createEnum(stateStack.head, casesChunk, index, value)) + } + } + + while (result.isEmpty) { + val state = stateStack.head + currentSchema match { + + case l @ Schema.Lazy(_) => + currentSchema = l.schema + + case Schema.Primitive(p, _) => + finishWith(createPrimitive(state, p.asInstanceOf[StandardType[Any]])) + + case s @ Schema.GenericRecord(_, structure, _) => + 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(state, message)) + + case s @ Schema.Sequence(elementSchema, _, _, _, _) => + val elems = ChunkBuilder.make[Target]() + + def readOne(): Unit = + push { elem => + elems += elem + + val (newState, continue) = readOneSequenceElement(stateStack.head, s) + stateStack = stateStack.tail + + if (continue) { + currentSchema = elementSchema + pushState(newState) + readOne() + } else { + finishWith(createSequence(stateStack.head, s, elems.result())) + } + } + + currentSchema = elementSchema + startCreatingSequence(state, s) match { + case Some(startingState) => + pushState(startingState) + readOne() + case None => + finishWith(createSequence(stateStack.head, s, Chunk.empty)) + } + + case s @ Schema.Map(ks: Schema[k], vs: Schema[v], _) => + val elems = ChunkBuilder.make[(Target, Target)]() + + def readOne(): Unit = + push { key => + currentSchema = vs + + push { value => + val elem = (key, value) + elems += elem + + val (newState, continue) = readOneDictionaryElement(stateStack.head, s) + stateStack = stateStack.tail + + if (continue) { + currentSchema = ks + pushState(newState) + readOne() + } else { + finishWith(createDictionary(stateStack.head, s, elems.result())) + } + } + } + + startCreatingDictionary(state, s) match { + case Some(startingState) => + currentSchema = ks + pushState(startingState) + readOne() + case None => + finishWith(createDictionary(stateStack.head, s, Chunk.empty)) + } + + case s @ Schema.Set(as: Schema[a], _) => + val elems = ChunkBuilder.make[Target]() + + def readOne(): Unit = + push { elem => + elems += elem + + val (newState, continue) = readOneSetElement(stateStack.head, s) + stateStack = stateStack.tail + + if (continue) { + currentSchema = as + pushState(newState) + readOne() + } else { + finishWith(createSet(stateStack.head, s, elems.result())) + } + } + + startCreatingSet(state, s) match { + case Some(startingState) => + currentSchema = as + pushState(startingState) + readOne() + case None => + finishWith(createSet(stateStack.head, s, Chunk.empty)) + } + + case s: Schema.Either[l, r] => + startCreatingEither(state, s) match { + case Left(newState) => + currentSchema = s.left + pushState(newState) + push { value => + stateStack = stateStack.tail + finishWith(createEither(stateStack.head, s, Left(value))) + } + case Right(newState) => + currentSchema = s.right + pushState(newState) + push { value => + stateStack = stateStack.tail + finishWith(createEither(stateStack.head, s, Right(value))) + } + } + + case s: Schema.Tuple2[a, b] => + currentSchema = s.left + pushState(startCreatingTuple(state, s)) + push { left => + val newState = startReadingSecondTupleElement(stateStack.head, s) + stateStack = stateStack.tail + currentSchema = s.right + pushState(newState) + push { right => + stateStack = stateStack.tail + finishWith(createTuple(stateStack.head, s, left, right)) + } + } + + case s: Schema.Optional[a] => + startCreatingOptional(state, s) match { + case Some(newState) => + currentSchema = s.schema + pushState(newState) + push { value => + stateStack = stateStack.tail + finishWith(createOptional(stateStack.head, s, Some(value))) + } + case None => + finishWith(createOptional(stateStack.head, s, None)) + } + + case Schema.Transform(schema, f, _, _, _) => + currentSchema = schema + push { result => + finishWith(transform(state, result, f.asInstanceOf[Any => Either[String, Any]])) + } + + case s @ Schema.CaseClass0(_, _, _) => + finishWith(createRecord(state, 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(state) match { + case Some(value) => + finishWith(value) + case None => + currentSchema = Schema.dynamicValue + } + } + } + result.get + } +} diff --git a/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala index 9736aa1d5..f47282414 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala @@ -1277,7 +1277,7 @@ trait ProcessValueWithSchema[Target, State] { } } -trait ProcessSchemaAndValueWithoutState[Target <: AnyRef] extends ProcessValueWithSchema[Target, Unit] { +trait ProcessSchemaAndValueWithoutState[Target] extends ProcessValueWithSchema[Target, Unit] { protected def processPrimitive(value: Any, typ: StandardType[Any]): Target From fc2a102591c547b4ee1526b4fbe41f07f7b8f583 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Sat, 29 Oct 2022 09:20:42 -0400 Subject: [PATCH 11/18] Cleanup --- .../scala/zio/schema/codec/ThriftCodec.scala | 97 ++----------------- .../zio/schema/CreateValueFromSchema.scala | 2 +- .../zio/schema/ProcessSchemaAndValue.scala | 4 +- 3 files changed, 11 insertions(+), 92 deletions(-) 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 f301ccf26..0f6daaaec 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,19 +1,21 @@ package zio.schema.codec -import org.apache.thrift.protocol._ -import zio.schema._ -import zio.stream.ZPipeline -import zio.{ Chunk, Unsafe, ZIO } - import java.math.{ BigInteger, MathContext } import java.nio.ByteBuffer import java.time._ import java.util.UUID + import scala.annotation.tailrec import scala.collection.immutable.ListMap import scala.util.Try import scala.util.control.NonFatal +import org.apache.thrift.protocol._ + +import zio.schema._ +import zio.stream.ZPipeline +import zio.{ Chunk, Unsafe, ZIO } + object ThriftCodec extends Codec { override def encoder[A](schema: Schema[A]): ZPipeline[Any, Nothing, A, Byte] = { val encoder = new Encoder() @@ -49,89 +51,6 @@ object ThriftCodec extends Codec { err => s"Error at path /${err.path.mkString(".")}: ${err.error}" ) - 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) - ) - ) - } - class Encoder extends ProcessValueWithSchema[Unit, Encoder.State] { import Encoder._ @@ -626,7 +545,7 @@ object ThriftCodec extends Codec { p.readFieldBegin() decodeInt(state.path).map { nano => p.readFieldBegin() - Duration.ofSeconds(seconds, nano) + Duration.ofSeconds(seconds, nano.toLong) } } case StandardType.InstantType(formatter) => diff --git a/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala b/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala index e5d7afa98..3e8b62336 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala @@ -108,7 +108,7 @@ trait CreateValueFromSchema[Target, State] { case Schema.Primitive(p, _) => finishWith(createPrimitive(state, p.asInstanceOf[StandardType[Any]])) - case s @ Schema.GenericRecord(_, structure, _) => + case s @ Schema.GenericRecord(_, _, _) => record(s) case s @ Schema.Enum1(_, _, _) => diff --git a/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala index f47282414..460da87bc 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala @@ -1,9 +1,9 @@ package zio.schema +import scala.annotation.nowarn import scala.collection.immutable.ListMap -import zio.{ Chunk, ChunkBuilder } -import scala.annotation.nowarn +import zio.{ Chunk, ChunkBuilder } trait ProcessValueWithSchema[Target, State] { protected def processPrimitive(state: State, value: Any, typ: StandardType[Any]): Target From 4157cbf44ada33bf33e2b699bb1f5d18fdc1aa6e Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Wed, 2 Nov 2022 19:53:01 -0400 Subject: [PATCH 12/18] Stack safe protobuf decoder, WIP --- .../zio/schema/codec/ProtobufCodec.scala | 1278 ++++++----------- .../scala/zio/schema/codec/ThriftCodec.scala | 35 +- .../zio/schema/CreateValueFromSchema.scala | 179 ++- 3 files changed, 603 insertions(+), 889 deletions(-) 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 bded91ad0..fec9949a3 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 @@ -5,15 +5,13 @@ 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 scala.util.control.{ NoStackTrace, NonFatal } import zio.schema._ import zio.schema.codec.ProtobufCodec.Protobuf.WireType.LengthDelimited import zio.stream.ZPipeline -import zio.{ Chunk, ZIO } +import zio.{ Chunk, Unsafe, ZIO } object ProtobufCodec extends Codec { override def encoder[A](schema: Schema[A]): ZPipeline[Any, Nothing, A, Byte] = @@ -22,10 +20,10 @@ object ProtobufCodec extends Codec { override def encode[A](schema: Schema[A]): A => Chunk[Byte] = a => Encoder.process(schema, a) override def decoder[A](schema: Schema[A]): ZPipeline[Any, String, Byte, A] = - ZPipeline.mapChunksZIO(chunk => ZIO.fromEither(Decoder.decode(schema, chunk).map(Chunk(_)))) + ZPipeline.mapChunksZIO(chunk => ZIO.fromEither(new Decoder(chunk).decode(schema).map(Chunk(_)))) override def decode[A](schema: Schema[A]): Chunk[Byte] => scala.util.Either[String, A] = - ch => Decoder.decode(schema, ch) + ch => new Decoder(ch).decode(schema) object Protobuf { @@ -40,87 +38,6 @@ object ProtobufCodec extends Codec { 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). */ @@ -232,20 +149,21 @@ object ProtobufCodec extends Codec { } else { val chunk = value.map { case (left, right) => - (Decoder.keyDecoder.run(left), Decoder.keyDecoder.run(right)) match { - case ( - Right((leftRemainder, (leftWireType, seqIndex))), - Right((rightRemainder, (rightWireType, _))) - ) => - val data = - encodeKey(leftWireType, Some(1)) ++ - leftRemainder ++ - encodeKey(rightWireType, Some(2)) ++ - rightRemainder - encodeKey(WireType.LengthDelimited(data.size), Some(seqIndex)) ++ data - case other => - throw new IllegalStateException(s"Invalid state in processDictionary: $other") - } + ??? // TODO +// (Decoder.keyDecoder.run(left), Decoder.keyDecoder.run(right)) match { +// case ( +// Right((leftRemainder, (leftWireType, seqIndex))), +// Right((rightRemainder, (rightWireType, _))) +// ) => +// val data = +// encodeKey(leftWireType, Some(1)) ++ +// leftRemainder ++ +// encodeKey(rightWireType, Some(2)) ++ +// rightRemainder +// 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), @@ -502,412 +420,451 @@ object ProtobufCodec extends Codec { }.getOrElse(Chunk.empty) } - final case class Decoder[+A](run: Chunk[Byte] => scala.util.Either[String, (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): Decoder[B] = - Decoder { 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 => Decoder[B]): Decoder[B] = - Decoder { bytes => - self.run(bytes).flatMap { - case (remainder, a) => - f(a).run(remainder) - } - } + def all(context: DecoderContext): Chunk[Byte] = read(length(context)) - def loop: Decoder[Chunk[A]] = - self.flatMap( - a0 => - Decoder(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): Decoder[A] = - Decoder(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 } - object Decoder { + final case class DecoderContext(limit: Option[Int], packed: Boolean) { + + def limitedTo(state: DecoderState, w: Int): DecoderContext = + copy(limit = Some(state.currentPosition + w)) + } + + final case class DecoderException(message: String) extends RuntimeException(message) with NoStackTrace + + class Decoder(chunk: Chunk[Byte]) extends CreateValueFromSchema[Any, DecoderContext] { - import ProductDecoder._ import Protobuf._ - def fail(failure: String): Decoder[Nothing] = Decoder(_ => Left(failure)) - - def succeedNow[A](a: A): Decoder[A] = Decoder(bytes => Right((bytes, a))) - - def succeed[A](a: => A): Decoder[A] = Decoder(bytes => Right((bytes, a))) - - def binaryDecoder: Decoder[Chunk[Byte]] = Decoder(bytes => Right((Chunk.empty, bytes))) - - def collectAll[A](chunk: Chunk[Decoder[A]]): Decoder[Chunk[A]] = ??? - - def failWhen(cond: Boolean, message: String): Decoder[Unit] = - if (cond) Decoder.fail(message) else Decoder.succeed(()) - - private[codec] val stringDecoder: Decoder[String] = - Decoder(bytes => Right((Chunk.empty, new String(bytes.toArray, StandardCharsets.UTF_8)))) - - def decode[A](schema: Schema[A], chunk: Chunk[Byte]): scala.util.Either[String, A] = - decoder(schema) - .run(chunk) - .map(_._2) - - private[codec] def decoder[A](schema: Schema[A]): Decoder[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(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[String, A] = + try { + Right(create(schema).asInstanceOf[A]) + } catch { + case DecoderException(message) => + Left(message) + } + + 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 scale = createTypedPrimitive(rawFieldDecoder(context, 2), StandardType.IntType) + val precision = 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 DecoderException(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 DecoderException(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 + ): (DecoderContext, Option[Int]) = + if (index == record.fields.size) { + (context, None) + } else { + keyDecoder(context) match { + case (wt, fieldNumber) => + if (record.fields.isDefinedAt(fieldNumber - 1)) { + wt match { + case LengthDelimited(width) => + (context.limitedTo(state, width), Some(fieldNumber - 1)) + case _ => + (context, Some(fieldNumber - 1)) + } + } else { + throw DecoderException(s"Failed to decode record. Schema does not contain field number $fieldNumber.") + } + } } - //scalafmt: { maxColumn = 120, optIn.configStyleArguments = true } - private val dynamicDecoder: Decoder[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 DecoderException(message) + } + } - private def enumDecoder[Z](cases: Schema.Case[Z, _]*): Decoder[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[Decoder[Z]] + (context.limitedTo(state, width), fieldNumber - 1) case _ => - decoder(subtypeCase.schema) - .asInstanceOf[Decoder[Z]] + (context, fieldNumber - 1) } case (_, fieldNumber) => - fail(s"Failed to decode enumeration. Schema does not contain field number $fieldNumber.") + throw DecoderException(s"Failed to decode enumeration. Schema does not contain field number $fieldNumber.") } - private def recordDecoder[Z](fields: Seq[Schema.Field[Z, _]], decoded: Int = 0): Decoder[ListMap[String, _]] = - if (fields.isEmpty || (fields.size == decoded)) - Decoder.succeed(ListMap.empty) - else - keyDecoder.flatMap { - case (wt, fieldNumber) => - if (fields.isDefinedAt(fieldNumber - 1)) { - val Schema.Field(fieldName, schema, _, _, _, _) = fields(fieldNumber - 1) + 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 DecoderException(s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") + } + override protected def readOneSequenceElement( + context: DecoderContext, + schema: Schema.Sequence[_, _, _], + index: Int + ): (DecoderContext, Boolean) = + if (state.length(context) > 0) { + if (context.packed) { + (context, true) + } else { + keyDecoder(context) match { + case (wt, _) => wt match { - case LengthDelimited(width) => - for { - fieldValue <- decoder(schema).take(width) - remainder <- recordDecoder(fields, decoded + 1) - } yield (remainder.updated(fieldName, fieldValue)) - + case LengthDelimited(elemWidth) => + (context.limitedTo(state, elemWidth), true) case _ => - for { - fieldValue <- decoder(schema) - remainder <- recordDecoder(fields, decoded + 1) - } yield (remainder.updated(fieldName, fieldValue)) + throw DecoderException(s"Unexpected wire type $wt for non-packed sequence") + } - } else { - fail(s"Failed to decode record. Schema does not contain field number $fieldNumber.") - } + } } + } else { + (context, false) + } - private def packedSequenceDecoder[A](schema: Schema[A]): Decoder[Chunk[A]] = - keyDecoder.flatMap { - case (LengthDelimited(0), 1) => succeed(Chunk.empty) + 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) - } - case (wt, fieldNumber) => fail(s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") + Some(context.limitedTo(state, width).copy(packed = canBePacked(schema.keySchema.zip(schema.valueSchema)))) + case (wt, fieldNumber) => + throw DecoderException(s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") } - private def nonPackedSequenceDecoder[A](schema: Schema[A]): Decoder[Chunk[A]] = - keyDecoder.flatMap { - case (LengthDelimited(0), 1) => succeed(Chunk.empty) - case (LengthDelimited(width), 2) => - keyDecoder.flatMap { + override protected def readOneDictionaryElement( + context: DecoderContext, + schema: Schema.Map[_, _], + index: Int + ): (DecoderContext, Boolean) = + if (state.length(context) > 0) { + if (context.packed) { + (context, true) + } else { + keyDecoder(context) match { case (wt, _) => wt match { - case LengthDelimited(width) => decoder(schema).take(width) - case _ => fail(s"Unexpected wire type $wt for non-packed sequence") + case LengthDelimited(elemWidth) => + (context.limitedTo(state, elemWidth), true) + case _ => + throw DecoderException(s"Unexpected wire type $wt for non-packed sequence") + } - }.loop.take(width) - case (wt, fieldNumber) => - fail(s"Invalid wire type ($wt) or field number ($fieldNumber) for non-packed sequence") + } + } + } else { + (context, false) } - private def tupleDecoder[A, B](left: Schema[A], right: Schema[B]): Decoder[(A, B)] = { - def elementDecoder[A](schema: Schema[A], wt: WireType): Decoder[A] = wt match { - case LengthDelimited(width) => decoder(schema).take(width) - case _ => decoder(schema) + 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) => + throw DecoderException(s"Invalid wire type ($wt) or field number ($fieldNumber) for 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(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(s"Invalid field number ($fieldNumber) for tuple") - } + override protected def readOneSetElement( + context: DecoderContext, + schema: Schema.Set[_], + index: Int + ): (DecoderContext, Boolean) = + if (state.length(context) > 0) { + if (context.packed) { + (context, true) + } else { + keyDecoder(context) match { + case (wt, _) => + wt match { + case LengthDelimited(elemWidth) => + (context.limitedTo(state, elemWidth), true) + case _ => + throw DecoderException(s"Unexpected wire type $wt for non-packed sequence") + } } - case (_, fieldNumber) => fail(s"Invalid field number ($fieldNumber) for tuple") + } + } else { + (context, false) } - } - private def eitherDecoder[A, B](left: Schema[A], right: Schema[B]): Decoder[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(_)) - case (_, fieldNumber) => fail(s"Invalid field number ($fieldNumber) for either") + 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) => throw DecoderException(s"Invalid field number $fieldNumber for option") } - private def optionalDecoder[A](schema: Schema[A]): Decoder[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(s"Invalid field number $fieldNumber for option") + 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) => throw DecoderException(s"Invalid field number ($fieldNumber) for either") } - private def floatDecoder: Decoder[Float] = - Decoder(bytes => { - if (bytes.size < 4) { - Left(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) + override protected def createEither( + context: DecoderContext, + schema: Schema.Either[_, _], + value: Either[Any, Any] + ): Any = + value - private def doubleDecoder: Decoder[Double] = - Decoder(bytes => { - if (bytes.size < 8) { - Left(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]): Decoder[A] = - schema match { - case Schema.Primitive(typ, _) if typ == StandardType.UnitType => - Decoder { (chunk: Chunk[Byte]) => - f(().asInstanceOf[B]) match { - case Left(err) => Left(err) - case Right(b) => Right(chunk -> b) - } + override protected def startCreatingTuple(context: DecoderContext, schema: Schema.Tuple2[_, _]): DecoderContext = + keyDecoder(context) match { + case (wt, 1) => + wt match { + case LengthDelimited(width) => context.limitedTo(state, width) + case _ => context } - case _ => decoder(schema).flatMap(a => Decoder(chunk => f(a).map(b => (chunk, b)))) + case (_, fieldNumber) => + throw DecoderException(s"Invalid field number ($fieldNumber) for tuple's first field") } - private def primitiveDecoder[A](standardType: StandardType[A]): Decoder[A] = - standardType match { - case StandardType.UnitType => Decoder((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(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(s"Invalid UUID string $uuid") - } + override protected def startReadingSecondTupleElement( + context: DecoderContext, + schema: Schema.Tuple2[_, _] + ): 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(s"Unsupported primitive type $st") + case (_, fieldNumber) => + throw DecoderException(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]): Any = + f(value) match { + case Left(value) => throw DecoderException(value) + case Right(value) => value } + override protected def fail(context: DecoderContext, message: String): Any = + throw DecoderException(message) + + override protected val initialState: DecoderContext = DecoderContext(limit = None, packed = false) + /** * 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: Decoder[(WireType, Int)] = - varIntDecoder.flatMap { key => - val fieldNumber = (key >>> 3).toInt - if (fieldNumber < 1) { - fail(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(s"Failed decoding key. Unknown wire type $n") - } + private def keyDecoder(context: DecoderContext): (WireType, Int) = { + val key = varIntDecoder(context) + val fieldNumber = (key >>> 3).toInt + if (fieldNumber < 1) { + throw DecoderException(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 DecoderException(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 DecoderException(s"Failed to decode record. Schema does not contain field number $expectedFieldNumber.") + } + + private def floatDecoder(context: DecoderContext): Float = + if (state.length(context) < 4) + throw DecoderException(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 DecoderException(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. @@ -918,415 +875,22 @@ object ProtobufCodec extends Codec { * (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: Decoder[Long] = - Decoder( - (chunk) => - if (chunk.isEmpty) { - Left("Failed to decode VarInt. Unexpected end of chunk") - } else { - val length = chunk.indexWhere(octet => (octet.longValue() & 0x80) != 0x80) + 1 - if (length <= 0) { - Left("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 ProductDecoder { - import Decoder.{ fail, keyDecoder, succeed } - import Protobuf.WireType._ - - private def unsafeDecodeFields[Z](buffer: Array[Any], fields: Schema.Field[Z, _]*): Decoder[Array[Any]] = - keyDecoder.flatMap { - case (wt, fieldNumber) if fieldNumber == fields.length => - wt match { - case LengthDelimited(width) => - Decoder - .decoder(fields(fieldNumber - 1).schema) - .take(width) - .map(fieldValue => buffer.updated(fieldNumber - 1, fieldValue)) - case _ => - Decoder - .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 <- Decoder.decoder(fields(fieldNumber - 1).schema).take(width) - remainder <- unsafeDecodeFields(buffer, fields: _*) - } yield remainder.updated(fieldNumber - 1, fieldValue) - case _ => - for { - fieldValue <- Decoder.decoder(fields(fieldNumber - 1).schema) - remainder <- unsafeDecodeFields(buffer, fields: _*) - } yield remainder.updated(fieldNumber - 1, fieldValue) - } - } else { - fail(s"Failed to decode record. Schema does not contain field number $fieldNumber.") - } - } - - @tailrec - private def validateBuffer(index: Int, buffer: Array[Any]): Decoder[Array[Any]] = - if (index == buffer.length - 1 && buffer(index) != null) - succeed(buffer) - else if (buffer(index) == null) - fail(s"Failed to decode record. Missing field number $index.") - else - validateBuffer(index + 1, buffer) - - private[codec] def caseClass0Decoder[Z](schema: Schema.CaseClass0[Z]): Decoder[Z] = - succeed(schema.defaultConstruct()) - - private[codec] def caseClass1Decoder[A, Z](schema: Schema.CaseClass1[A, Z]): Decoder[Z] = - unsafeDecodeFields(Array.ofDim[Any](1), schema.field).flatMap { buffer => - if (buffer(0) == null) - fail("Failed to decode record. Missing field 1.") - else - succeed(schema.defaultConstruct(buffer(0).asInstanceOf[A])) + private def varIntDecoder(context: DecoderContext): Long = + if (state.length(context) == 0) { + throw DecoderException("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 DecoderException("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)) + } } - private[codec] def caseClass2Decoder[A1, A2, Z](schema: Schema.CaseClass2[A1, A2, Z]): Decoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](2), schema.field1, schema.field2) - _ <- validateBuffer(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]): Decoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](3), schema.field1, schema.field2, schema.field3) - _ <- validateBuffer(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]): Decoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](4), schema.field1, schema.field2, schema.field3, schema.field4) - _ <- validateBuffer(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]): Decoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](5), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5) - _ <- validateBuffer(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]): Decoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](6), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6) - _ <- validateBuffer(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]): Decoder[Z] = - for { - buffer <- unsafeDecodeFields(Array.ofDim[Any](7), schema.field1, schema.field2, schema.field3, schema.field4, schema.field5, schema.field6, schema.field7) - _ <- validateBuffer(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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]): Decoder[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(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 binaryDecoder(context: DecoderContext): Chunk[Byte] = + state.all(context) } } 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 0f6daaaec..f52d39fb2 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 @@ -405,7 +405,7 @@ object ThriftCodec extends Codec { type Result[A] = scala.util.Either[Error, A] type PrimitiveResult[A] = Path => Result[A] - final case class DecoderState(path: Path, remainingCount: Option[Int]) + final case class DecoderState(path: Path, expectedCount: Option[Int]) class Decoder(chunk: Chunk[Byte]) extends CreateValueFromSchema[Result[Any], DecoderState] { @@ -634,16 +634,16 @@ object ThriftCodec extends Codec { val begin = p.readListBegin() if (begin.size == 0) None else - Some(state.copy(remainingCount = Some(begin.size))) + Some(state.copy(expectedCount = Some(begin.size))) } override protected def readOneSequenceElement( state: DecoderState, - schema: Schema.Sequence[_, _, _] + schema: Schema.Sequence[_, _, _], + index: Int ): (DecoderState, Boolean) = { - val updatedState = state.copy(remainingCount = state.remainingCount.map(_ - 1)) - val continue = updatedState.remainingCount.exists(_ > 0) - (updatedState, continue) + val continue = state.expectedCount.map(_ - index).exists(_ > 0) + (state, continue) } override protected def createSequence( @@ -660,16 +660,16 @@ object ThriftCodec extends Codec { val begin = p.readMapBegin() if (begin.size == 0) None else - Some(state.copy(remainingCount = Some(begin.size))) + Some(state.copy(expectedCount = Some(begin.size))) } override protected def readOneDictionaryElement( state: DecoderState, - schema: Schema.Map[_, _] + schema: Schema.Map[_, _], + index: Int ): (DecoderState, Boolean) = { - val updatedState = state.copy(remainingCount = state.remainingCount.map(_ - 1)) - val continue = updatedState.remainingCount.exists(_ > 0) - (updatedState, continue) + val continue = state.expectedCount.map(_ - index).exists(_ > 0) + (state, continue) } override protected def createDictionary( @@ -689,13 +689,16 @@ object ThriftCodec extends Codec { override protected def startCreatingSet(state: DecoderState, schema: Schema.Set[_]): Option[DecoderState] = { val begin = p.readSetBegin() if (begin.size == 0) None - else Some(state.copy(remainingCount = Some(begin.size))) + else Some(state.copy(expectedCount = Some(begin.size))) } - override protected def readOneSetElement(state: DecoderState, schema: Schema.Set[_]): (DecoderState, Boolean) = { - val updatedState = state.copy(remainingCount = state.remainingCount.map(_ - 1)) - val continue = updatedState.remainingCount.exists(_ > 0) - (updatedState, continue) + override protected def readOneSetElement( + state: DecoderState, + schema: Schema.Set[_], + index: Int + ): (DecoderState, Boolean) = { + val continue = state.expectedCount.map(_ - index).exists(_ > 0) + (state, continue) } override protected def createSet( diff --git a/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala b/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala index 3e8b62336..4a4620a03 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala @@ -15,15 +15,15 @@ trait CreateValueFromSchema[Target, State] { protected def createEnum(state: State, cases: Chunk[Schema.Case[_, _]], index: Int, value: Target): Target protected def startCreatingSequence(state: State, schema: Schema.Sequence[_, _, _]): Option[State] - protected def readOneSequenceElement(state: State, schema: Schema.Sequence[_, _, _]): (State, Boolean) + protected def readOneSequenceElement(state: State, schema: Schema.Sequence[_, _, _], index: Int): (State, Boolean) protected def createSequence(state: State, schema: Schema.Sequence[_, _, _], values: Chunk[Target]): Target protected def startCreatingDictionary(state: State, schema: Schema.Map[_, _]): Option[State] - protected def readOneDictionaryElement(state: State, schema: Schema.Map[_, _]): (State, Boolean) + protected def readOneDictionaryElement(state: State, schema: Schema.Map[_, _], index: Int): (State, Boolean) protected def createDictionary(state: State, schema: Schema.Map[_, _], values: Chunk[(Target, Target)]): Target protected def startCreatingSet(state: State, schema: Schema.Set[_]): Option[State] - protected def readOneSetElement(state: State, schema: Schema.Set[_]): (State, Boolean) + protected def readOneSetElement(state: State, schema: Schema.Set[_], index: Int): (State, Boolean) protected def createSet(state: State, schema: Schema.Set[_], values: Chunk[Target]): Target protected def startCreatingOptional(state: State, schema: Schema.Optional[_]): Option[State] @@ -68,8 +68,8 @@ trait CreateValueFromSchema[Target, State] { val values = ChunkBuilder.make[(Int, Target)](record.fields.size) def readField(index: Int): Unit = { - val (updatedState, fieldIndex) = startReadingField(stateStack.head, record, index) stateStack = stateStack.tail + val (updatedState, fieldIndex) = startReadingField(stateStack.head, record, index) fieldIndex match { case Some(idx) => currentSchema = record.fields(idx).schema @@ -407,17 +407,17 @@ trait CreateValueFromSchema[Target, State] { case s @ Schema.Sequence(elementSchema, _, _, _, _) => val elems = ChunkBuilder.make[Target]() - def readOne(): Unit = + def readOne(index: Int): Unit = push { elem => elems += elem - val (newState, continue) = readOneSequenceElement(stateStack.head, s) stateStack = stateStack.tail + val (newState, continue) = readOneSequenceElement(stateStack.head, s, index) if (continue) { currentSchema = elementSchema pushState(newState) - readOne() + readOne(index + 1) } else { finishWith(createSequence(stateStack.head, s, elems.result())) } @@ -427,7 +427,8 @@ trait CreateValueFromSchema[Target, State] { startCreatingSequence(state, s) match { case Some(startingState) => pushState(startingState) - readOne() + pushState(startingState) + readOne(0) case None => finishWith(createSequence(stateStack.head, s, Chunk.empty)) } @@ -435,7 +436,7 @@ trait CreateValueFromSchema[Target, State] { case s @ Schema.Map(ks: Schema[k], vs: Schema[v], _) => val elems = ChunkBuilder.make[(Target, Target)]() - def readOne(): Unit = + def readOne(index: Int): Unit = push { key => currentSchema = vs @@ -443,13 +444,13 @@ trait CreateValueFromSchema[Target, State] { val elem = (key, value) elems += elem - val (newState, continue) = readOneDictionaryElement(stateStack.head, s) stateStack = stateStack.tail + val (newState, continue) = readOneDictionaryElement(stateStack.head, s, index) if (continue) { currentSchema = ks pushState(newState) - readOne() + readOne(index + 1) } else { finishWith(createDictionary(stateStack.head, s, elems.result())) } @@ -460,7 +461,8 @@ trait CreateValueFromSchema[Target, State] { case Some(startingState) => currentSchema = ks pushState(startingState) - readOne() + pushState(startingState) + readOne(0) case None => finishWith(createDictionary(stateStack.head, s, Chunk.empty)) } @@ -468,17 +470,17 @@ trait CreateValueFromSchema[Target, State] { case s @ Schema.Set(as: Schema[a], _) => val elems = ChunkBuilder.make[Target]() - def readOne(): Unit = + def readOne(index: Int): Unit = push { elem => elems += elem - val (newState, continue) = readOneSetElement(stateStack.head, s) stateStack = stateStack.tail + val (newState, continue) = readOneSetElement(stateStack.head, s, index) if (continue) { currentSchema = as pushState(newState) - readOne() + readOne(index + 1) } else { finishWith(createSet(stateStack.head, s, elems.result())) } @@ -488,7 +490,8 @@ trait CreateValueFromSchema[Target, State] { case Some(startingState) => currentSchema = as pushState(startingState) - readOne() + pushState(startingState) + readOne(0) case None => finishWith(createSet(stateStack.head, s, Chunk.empty)) } @@ -889,3 +892,147 @@ trait CreateValueFromSchema[Target, State] { result.get } } + +trait CreateValueFromSchemaWithoutState[Target] extends CreateValueFromSchema[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): (Unit, Option[Int]) = + ((), startReadingField(record, index)) + 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 readOneSequenceElement( + context: Unit, + schema: Schema.Sequence[_, _, _], + index: Int + ): (Unit, Boolean) = + ((), readOneSequenceElement(schema, index)) + + protected def readOneSequenceElement(schema: Schema.Sequence[_, _, _], index: Int): Boolean + + 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 readOneDictionaryElement( + context: Unit, + schema: Schema.Map[_, _], + index: Int + ): (Unit, Boolean) = + ((), readOneDictionaryElement(schema, index)) + + protected def readOneDictionaryElement(schema: Schema.Map[_, _], index: Int): Boolean + + 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 readOneSetElement(context: Unit, schema: Schema.Set[_], index: Int): (Unit, Boolean) = + ((), readOneSetElement(schema, index)) + + protected def readOneSetElement(schema: Schema.Set[_], index: Int): Boolean + + 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]): Target = + transform(value, f) + + protected def transform(value: Target, f: Any => Either[String, Any]): Target + + override protected def fail(context: Unit, message: String): Target = + fail(message) + + protected def fail(message: String): Target + + override protected val initialState: Unit = () +} From 7fb702d44b106133c61ee3f03834e125892fd6d3 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Thu, 3 Nov 2022 19:10:53 -0400 Subject: [PATCH 13/18] Work on stack-safe protobuf codec --- .../zio/schema/codec/ProtobufCodec.scala | 144 +++++++++--------- .../scala/zio/schema/codec/ThriftCodec.scala | 15 ++ .../zio/schema/CreateValueFromSchema.scala | 50 ++++-- 3 files changed, 127 insertions(+), 82 deletions(-) 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 fec9949a3..d3f667e74 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 @@ -149,21 +149,23 @@ object ProtobufCodec extends Codec { } else { val chunk = value.map { case (left, right) => - ??? // TODO -// (Decoder.keyDecoder.run(left), Decoder.keyDecoder.run(right)) match { -// case ( -// Right((leftRemainder, (leftWireType, seqIndex))), -// Right((rightRemainder, (rightWireType, _))) -// ) => -// val data = -// encodeKey(leftWireType, Some(1)) ++ -// leftRemainder ++ -// encodeKey(rightWireType, Some(2)) ++ -// rightRemainder -// encodeKey(WireType.LengthDelimited(data.size), Some(seqIndex)) ++ data -// case other => -// throw new IllegalStateException(s"Invalid state in processDictionary: $other") -// } + val leftDecoder = new Decoder(left) + val rightDecoder = new Decoder(right) + + ( + leftDecoder.keyDecoder(DecoderContext(None, packed = false)), + rightDecoder.keyDecoder(DecoderContext(None, packed = false)) + ) 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), @@ -613,29 +615,30 @@ object ProtobufCodec extends Codec { throw DecoderException(s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") } + override protected def startReadingOneSequenceElement( + 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 DecoderException(s"Unexpected wire type $wt for non-packed sequence") + } + } + } + override protected def readOneSequenceElement( context: DecoderContext, schema: Schema.Sequence[_, _, _], index: Int ): (DecoderContext, Boolean) = - if (state.length(context) > 0) { - if (context.packed) { - (context, true) - } else { - keyDecoder(context) match { - case (wt, _) => - wt match { - case LengthDelimited(elemWidth) => - (context.limitedTo(state, elemWidth), true) - case _ => - throw DecoderException(s"Unexpected wire type $wt for non-packed sequence") - - } - } - } - } else { - (context, false) - } + (context, state.length(context) > 0) override protected def createSequence( context: DecoderContext, @@ -657,29 +660,30 @@ object ProtobufCodec extends Codec { throw DecoderException(s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") } + override protected def startReadingOneDictionaryElement( + context: DecoderContext, + schema: Schema.Map[_, _] + ): DecoderContext = + if (context.packed) { + context + } else { + keyDecoder(context) match { + case (wt, _) => + wt match { + case LengthDelimited(elemWidth) => + context.limitedTo(state, elemWidth) + case _ => + throw DecoderException(s"Unexpected wire type $wt for non-packed sequence") + } + } + } + override protected def readOneDictionaryElement( context: DecoderContext, schema: Schema.Map[_, _], index: Int ): (DecoderContext, Boolean) = - if (state.length(context) > 0) { - if (context.packed) { - (context, true) - } else { - keyDecoder(context) match { - case (wt, _) => - wt match { - case LengthDelimited(elemWidth) => - (context.limitedTo(state, elemWidth), true) - case _ => - throw DecoderException(s"Unexpected wire type $wt for non-packed sequence") - - } - } - } - } else { - (context, false) - } + (context, state.length(context) > 0) override protected def createDictionary( context: DecoderContext, @@ -698,28 +702,27 @@ object ProtobufCodec extends Codec { throw DecoderException(s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") } + override protected def startReadingOneSetElement(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 DecoderException(s"Unexpected wire type $wt for non-packed sequence") + } + } + } + override protected def readOneSetElement( context: DecoderContext, schema: Schema.Set[_], index: Int ): (DecoderContext, Boolean) = - if (state.length(context) > 0) { - if (context.packed) { - (context, true) - } else { - keyDecoder(context) match { - case (wt, _) => - wt match { - case LengthDelimited(elemWidth) => - (context.limitedTo(state, elemWidth), true) - case _ => - throw DecoderException(s"Unexpected wire type $wt for non-packed sequence") - } - } - } - } else { - (context, false) - } + (context, state.length(context) > 0) override protected def createSet(context: DecoderContext, schema: Schema.Set[_], values: Chunk[Any]): Any = values.toSet @@ -812,7 +815,7 @@ object ProtobufCodec extends Codec { * 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 def keyDecoder(context: DecoderContext): (WireType, Int) = { + private[codec] def keyDecoder(context: DecoderContext): (WireType, Int) = { val key = varIntDecoder(context) val fieldNumber = (key >>> 3).toInt if (fieldNumber < 1) { @@ -891,6 +894,9 @@ object ProtobufCodec extends Codec { private def binaryDecoder(context: DecoderContext): Chunk[Byte] = state.all(context) + + private[codec] def remainder: Chunk[Byte] = + state.peek(DecoderContext(None, packed = false)) } } 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 f52d39fb2..9cb23165a 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 @@ -637,6 +637,12 @@ object ThriftCodec extends Codec { Some(state.copy(expectedCount = Some(begin.size))) } + override protected def startReadingOneSequenceElement( + state: DecoderState, + schema: Schema.Sequence[_, _, _] + ): DecoderState = + state + override protected def readOneSequenceElement( state: DecoderState, schema: Schema.Sequence[_, _, _], @@ -663,6 +669,12 @@ object ThriftCodec extends Codec { Some(state.copy(expectedCount = Some(begin.size))) } + override protected def startReadingOneDictionaryElement( + state: DecoderState, + schema: Schema.Map[_, _] + ): DecoderState = + state + override protected def readOneDictionaryElement( state: DecoderState, schema: Schema.Map[_, _], @@ -692,6 +704,9 @@ object ThriftCodec extends Codec { else Some(state.copy(expectedCount = Some(begin.size))) } + override protected def startReadingOneSetElement(state: DecoderState, schema: Schema.Set[_]): DecoderState = + state + override protected def readOneSetElement( state: DecoderState, schema: Schema.Set[_], diff --git a/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala b/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala index 4a4620a03..b9077a1c7 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala @@ -15,14 +15,17 @@ trait CreateValueFromSchema[Target, State] { protected def createEnum(state: State, cases: Chunk[Schema.Case[_, _]], index: Int, value: Target): Target protected def startCreatingSequence(state: State, schema: Schema.Sequence[_, _, _]): Option[State] + protected def startReadingOneSequenceElement(state: State, schema: Schema.Sequence[_, _, _]): State protected def readOneSequenceElement(state: State, schema: Schema.Sequence[_, _, _], index: Int): (State, Boolean) protected def createSequence(state: State, schema: Schema.Sequence[_, _, _], values: Chunk[Target]): Target protected def startCreatingDictionary(state: State, schema: Schema.Map[_, _]): Option[State] + protected def startReadingOneDictionaryElement(state: State, schema: Schema.Map[_, _]): State protected def readOneDictionaryElement(state: State, schema: Schema.Map[_, _], index: Int): (State, Boolean) protected def createDictionary(state: State, schema: Schema.Map[_, _], values: Chunk[(Target, Target)]): Target protected def startCreatingSet(state: State, schema: Schema.Set[_]): Option[State] + protected def startReadingOneSetElement(state: State, schema: Schema.Set[_]): State protected def readOneSetElement(state: State, schema: Schema.Set[_], index: Int): (State, Boolean) protected def createSet(state: State, schema: Schema.Set[_], values: Chunk[Target]): Target @@ -412,14 +415,16 @@ trait CreateValueFromSchema[Target, State] { elems += elem stateStack = stateStack.tail - val (newState, continue) = readOneSequenceElement(stateStack.head, s, index) + val (newState0, continue) = readOneSequenceElement(stateStack.head, s, index) if (continue) { currentSchema = elementSchema - pushState(newState) + pushState(startReadingOneSequenceElement(newState0, s)) readOne(index + 1) } else { - finishWith(createSequence(stateStack.head, s, elems.result())) + val state = stateStack.head + stateStack = stateStack.tail + finishWith(createSequence(state, s, elems.result())) } } @@ -427,7 +432,7 @@ trait CreateValueFromSchema[Target, State] { startCreatingSequence(state, s) match { case Some(startingState) => pushState(startingState) - pushState(startingState) + pushState(startReadingOneSequenceElement(startingState, s)) readOne(0) case None => finishWith(createSequence(stateStack.head, s, Chunk.empty)) @@ -445,14 +450,16 @@ trait CreateValueFromSchema[Target, State] { elems += elem stateStack = stateStack.tail - val (newState, continue) = readOneDictionaryElement(stateStack.head, s, index) + val (newState0, continue) = readOneDictionaryElement(stateStack.head, s, index) if (continue) { currentSchema = ks - pushState(newState) + pushState(startReadingOneDictionaryElement(newState0, s)) readOne(index + 1) } else { - finishWith(createDictionary(stateStack.head, s, elems.result())) + val state = stateStack.head + stateStack = stateStack.tail + finishWith(createDictionary(state, s, elems.result())) } } } @@ -461,7 +468,7 @@ trait CreateValueFromSchema[Target, State] { case Some(startingState) => currentSchema = ks pushState(startingState) - pushState(startingState) + pushState(startReadingOneDictionaryElement(startingState, s)) readOne(0) case None => finishWith(createDictionary(stateStack.head, s, Chunk.empty)) @@ -475,14 +482,16 @@ trait CreateValueFromSchema[Target, State] { elems += elem stateStack = stateStack.tail - val (newState, continue) = readOneSetElement(stateStack.head, s, index) + val (newState0, continue) = readOneSetElement(stateStack.head, s, index) if (continue) { currentSchema = as - pushState(newState) + pushState(startReadingOneSetElement(newState0, s)) readOne(index + 1) } else { - finishWith(createSet(stateStack.head, s, elems.result())) + val state = stateStack.head + stateStack = stateStack.tail + finishWith(createSet(state, s, elems.result())) } } @@ -490,7 +499,7 @@ trait CreateValueFromSchema[Target, State] { case Some(startingState) => currentSchema = as pushState(startingState) - pushState(startingState) + pushState(startReadingOneSetElement(startingState, s)) readOne(0) case None => finishWith(createSet(stateStack.head, s, Chunk.empty)) @@ -518,8 +527,8 @@ trait CreateValueFromSchema[Target, State] { currentSchema = s.left pushState(startCreatingTuple(state, s)) push { left => - val newState = startReadingSecondTupleElement(stateStack.head, s) stateStack = stateStack.tail + val newState = startReadingSecondTupleElement(stateStack.head, s) currentSchema = s.right pushState(newState) push { right => @@ -925,6 +934,11 @@ trait CreateValueFromSchemaWithoutState[Target] extends CreateValueFromSchema[Ta protected def startCreatingSequence(schema: Schema.Sequence[_, _, _]): Option[Unit] + override protected def startReadingOneSequenceElement(state: Unit, schema: Schema.Sequence[_, _, _]): Unit = + startReadingOneSequenceElement(schema) + + protected def startReadingOneSequenceElement(schema: Schema.Sequence[_, _, _]): Unit + override protected def readOneSequenceElement( context: Unit, schema: Schema.Sequence[_, _, _], @@ -955,6 +969,11 @@ trait CreateValueFromSchemaWithoutState[Target] extends CreateValueFromSchema[Ta ): (Unit, Boolean) = ((), readOneDictionaryElement(schema, index)) + override protected def startReadingOneDictionaryElement(state: Unit, schema: Schema.Map[_, _]): Unit = + startReadingOneDictionaryElement(schema) + + protected def startReadingOneDictionaryElement(schema: Schema.Map[_, _]): Unit + protected def readOneDictionaryElement(schema: Schema.Map[_, _], index: Int): Boolean override protected def createDictionary( @@ -971,6 +990,11 @@ trait CreateValueFromSchemaWithoutState[Target] extends CreateValueFromSchema[Ta protected def startCreatingSet(schema: Schema.Set[_]): Option[Unit] + override protected def startReadingOneSetElement(state: Unit, schema: Schema.Set[_]): Unit = + startReadingOneSetElement(schema) + + protected def startReadingOneSetElement(schema: Schema.Set[_]): Unit + override protected def readOneSetElement(context: Unit, schema: Schema.Set[_], index: Int): (Unit, Boolean) = ((), readOneSetElement(schema, index)) From 12993b07b86bff1ad46719e6eebe1b6892a244ed Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Sun, 6 Nov 2022 15:53:06 +0100 Subject: [PATCH 14/18] Map fix --- .../zio/schema/codec/ProtobufCodec.scala | 71 ++++++++++++------- .../scala/zio/schema/codec/ThriftCodec.scala | 6 ++ .../zio/schema/CreateValueFromSchema.scala | 13 +++- 3 files changed, 61 insertions(+), 29 deletions(-) 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 d3f667e74..33e459f45 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,17 +1,16 @@ package zio.schema.codec -import java.math.{ BigInteger, MathContext } +import zio.schema._ +import zio.schema.codec.ProtobufCodec.Protobuf.WireType.LengthDelimited +import zio.stream.ZPipeline +import zio.{Chunk, Unsafe, ZIO} + import java.nio.charset.StandardCharsets -import java.nio.{ ByteBuffer, ByteOrder } +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.{ NoStackTrace, NonFatal } -import zio.schema._ -import zio.schema.codec.ProtobufCodec.Protobuf.WireType.LengthDelimited -import zio.stream.ZPipeline -import zio.{ Chunk, Unsafe, ZIO } +import scala.util.control.{NoStackTrace, NonFatal} object ProtobufCodec extends Codec { override def encoder[A](schema: Schema[A]): ZPipeline[Any, Nothing, A, Byte] = @@ -153,8 +152,8 @@ object ProtobufCodec extends Codec { val rightDecoder = new Decoder(right) ( - leftDecoder.keyDecoder(DecoderContext(None, packed = false)), - rightDecoder.keyDecoder(DecoderContext(None, packed = false)) + leftDecoder.keyDecoder(DecoderContext(None, packed = false, dictionaryElementContext = None)), + rightDecoder.keyDecoder(DecoderContext(None, packed = false, dictionaryElementContext = None)) ) match { case ((leftWireType, seqIndex), (rightWireType, _)) => val data = @@ -442,7 +441,11 @@ object ProtobufCodec extends Codec { def currentPosition: Int = position } - final case class DecoderContext(limit: Option[Int], packed: Boolean) { + 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)) @@ -663,20 +666,29 @@ object ProtobufCodec extends Codec { override protected def startReadingOneDictionaryElement( context: DecoderContext, schema: Schema.Map[_, _] - ): DecoderContext = - if (context.packed) { - context - } else { - keyDecoder(context) match { - case (wt, _) => - wt match { - case LengthDelimited(elemWidth) => - context.limitedTo(state, elemWidth) - case _ => - throw DecoderException(s"Unexpected wire type $wt for non-packed sequence") - } + ): DecoderContext = { + val elemContext = + if (context.packed) { + context + } else { + keyDecoder(context) match { + case (wt, _) => + wt match { + case LengthDelimited(elemWidth) => + context.limitedTo(state, elemWidth) + case _ => + throw DecoderException(s"Unexpected wire type $wt for non-packed sequence") + } + } } - } + enterFirstTupleElement(elemContext).copy(dictionaryElementContext = Some(elemContext)) + } + + override protected def startReadingOneDictionaryValue( + context: DecoderContext, + schema: Schema.Map[_, _] + ): DecoderContext = + enterSecondTupleElement(context.dictionaryElementContext.getOrElse(context)) override protected def readOneDictionaryElement( context: DecoderContext, @@ -763,6 +775,9 @@ object ProtobufCodec extends Codec { value override protected def startCreatingTuple(context: DecoderContext, schema: Schema.Tuple2[_, _]): DecoderContext = + enterFirstTupleElement(context) + + private def enterFirstTupleElement(context: DecoderContext): DecoderContext = keyDecoder(context) match { case (wt, 1) => wt match { @@ -777,6 +792,9 @@ object ProtobufCodec extends Codec { context: DecoderContext, schema: Schema.Tuple2[_, _] ): DecoderContext = + enterSecondTupleElement(context) + + private def enterSecondTupleElement(context: DecoderContext): DecoderContext = keyDecoder(context) match { case (wt, 2) => wt match { @@ -807,7 +825,8 @@ object ProtobufCodec extends Codec { override protected def fail(context: DecoderContext, message: String): Any = throw DecoderException(message) - override protected val initialState: DecoderContext = DecoderContext(limit = None, packed = false) + override protected val initialState: DecoderContext = + DecoderContext(limit = None, packed = false, dictionaryElementContext = None) /** * Decodes key which consist out of field type (wire type) and a field number. @@ -896,7 +915,7 @@ object ProtobufCodec extends Codec { state.all(context) private[codec] def remainder: Chunk[Byte] = - state.peek(DecoderContext(None, packed = false)) + state.peek(DecoderContext(None, packed = false, dictionaryElementContext = None)) } } 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 9cb23165a..96aa68d33 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 @@ -675,6 +675,12 @@ object ThriftCodec extends Codec { ): DecoderState = state + override protected def startReadingOneDictionaryValue( + state: DecoderState, + schema: Schema.Map[_, _] + ): DecoderState = + state + override protected def readOneDictionaryElement( state: DecoderState, schema: Schema.Map[_, _], diff --git a/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala b/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala index b9077a1c7..c57d0c6e7 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala @@ -21,6 +21,7 @@ trait CreateValueFromSchema[Target, State] { protected def startCreatingDictionary(state: State, schema: Schema.Map[_, _]): Option[State] protected def startReadingOneDictionaryElement(state: State, schema: Schema.Map[_, _]): State + protected def startReadingOneDictionaryValue(state: State, schema: Schema.Map[_, _]): State protected def readOneDictionaryElement(state: State, schema: Schema.Map[_, _], index: Int): (State, Boolean) protected def createDictionary(state: State, schema: Schema.Map[_, _], values: Chunk[(Target, Target)]): Target @@ -444,12 +445,13 @@ trait CreateValueFromSchema[Target, State] { def readOne(index: Int): Unit = push { key => currentSchema = vs + pushState(startReadingOneDictionaryValue(state, s)) push { value => val elem = (key, value) elems += elem - stateStack = stateStack.tail + stateStack = stateStack.tail.tail val (newState0, continue) = readOneDictionaryElement(stateStack.head, s, index) if (continue) { @@ -970,9 +972,14 @@ trait CreateValueFromSchemaWithoutState[Target] extends CreateValueFromSchema[Ta ((), readOneDictionaryElement(schema, index)) override protected def startReadingOneDictionaryElement(state: Unit, schema: Schema.Map[_, _]): Unit = - startReadingOneDictionaryElement(schema) + startReadingOneDictionaryKey(schema) + + protected def startReadingOneDictionaryKey(schema: Schema.Map[_, _]): Unit + + override protected def startReadingOneDictionaryValue(state: Unit, schema: Schema.Map[_, _]): Unit = + startReadingOneDictionaryValue(schema) - protected def startReadingOneDictionaryElement(schema: Schema.Map[_, _]): Unit + protected def startReadingOneDictionaryValue(schema: Schema.Map[_, _]): Unit protected def readOneDictionaryElement(schema: Schema.Map[_, _], index: Int): Boolean From 1d76bb82c3e3735cbe26e6ea0b53369362aff913 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Sun, 6 Nov 2022 16:46:47 +0100 Subject: [PATCH 15/18] Protobuf and thrift fixes --- .../zio/schema/codec/ProtobufCodec.scala | 10 +++++----- .../scala/zio/schema/codec/ThriftCodec.scala | 19 ++++++++----------- 2 files changed, 13 insertions(+), 16 deletions(-) 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 33e459f45..6e76fdb35 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 @@ -3,14 +3,14 @@ package zio.schema.codec import zio.schema._ import zio.schema.codec.ProtobufCodec.Protobuf.WireType.LengthDelimited import zio.stream.ZPipeline -import zio.{Chunk, Unsafe, ZIO} +import zio.{ Chunk, Unsafe, ZIO } import java.nio.charset.StandardCharsets -import java.nio.{ByteBuffer, ByteOrder} +import java.nio.{ ByteBuffer, ByteOrder } import java.time._ import java.util.UUID import scala.collection.immutable.ListMap -import scala.util.control.{NoStackTrace, NonFatal} +import scala.util.control.{ NoStackTrace, NonFatal } object ProtobufCodec extends Codec { override def encoder[A](schema: Schema[A]): ZPipeline[Any, Nothing, A, Byte] = @@ -486,8 +486,8 @@ object ProtobufCodec extends Codec { new java.math.BigInteger(bytes.toArray) case StandardType.BigDecimalType => val unscaled = createTypedPrimitive(rawFieldDecoder(context, 1), StandardType.BigIntegerType) - val scale = createTypedPrimitive(rawFieldDecoder(context, 2), StandardType.IntType) - val precision = createTypedPrimitive(rawFieldDecoder(context, 3), StandardType.IntType) + 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) 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 96aa68d33..150961c56 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,21 +1,18 @@ package zio.schema.codec -import java.math.{ BigInteger, MathContext } +import org.apache.thrift.protocol._ +import zio.schema._ +import zio.stream.ZPipeline +import zio.{ Chunk, Unsafe, ZIO } + import java.nio.ByteBuffer import java.time._ import java.util.UUID - import scala.annotation.tailrec import scala.collection.immutable.ListMap import scala.util.Try import scala.util.control.NonFatal -import org.apache.thrift.protocol._ - -import zio.schema._ -import zio.stream.ZPipeline -import zio.{ Chunk, Unsafe, ZIO } - object ThriftCodec extends Codec { override def encoder[A](schema: Schema[A]): ZPipeline[Any, Nothing, A, Byte] = { val encoder = new Encoder() @@ -648,7 +645,7 @@ object ThriftCodec extends Codec { schema: Schema.Sequence[_, _, _], index: Int ): (DecoderState, Boolean) = { - val continue = state.expectedCount.map(_ - index).exists(_ > 0) + val continue = state.expectedCount.map(_ - (index + 1)).exists(_ > 0) (state, continue) } @@ -686,7 +683,7 @@ object ThriftCodec extends Codec { schema: Schema.Map[_, _], index: Int ): (DecoderState, Boolean) = { - val continue = state.expectedCount.map(_ - index).exists(_ > 0) + val continue = state.expectedCount.map(_ - (index + 1)).exists(_ > 0) (state, continue) } @@ -718,7 +715,7 @@ object ThriftCodec extends Codec { schema: Schema.Set[_], index: Int ): (DecoderState, Boolean) = { - val continue = state.expectedCount.map(_ - index).exists(_ > 0) + val continue = state.expectedCount.map(_ - (index + 1)).exists(_ > 0) (state, continue) } From 661d0c41cc45122dbc9445871562906b63022a94 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Sun, 6 Nov 2022 18:55:22 +0100 Subject: [PATCH 16/18] Cleanup --- .../zio/schema/codec/ProtobufCodec.scala | 162 +-- .../scala/zio/schema/codec/ThriftCodec.scala | 600 ++++----- .../zio/schema/codec/ThriftCodecSpec.scala | 4 +- .../zio/schema/CreateValueFromSchema.scala | 1069 --------------- .../main/scala/zio/schema/DynamicValue.scala | 2 +- .../MutableSchemaBasedValueBuilder.scala | 1163 +++++++++++++++++ ...=> MutableSchemaBasedValueProcessor.scala} | 273 ++-- 7 files changed, 1702 insertions(+), 1571 deletions(-) delete mode 100644 zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala create mode 100644 zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueBuilder.scala rename zio-schema/shared/src/main/scala/zio/schema/{ProcessSchemaAndValue.scala => MutableSchemaBasedValueProcessor.scala} (72%) 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 6e76fdb35..310547237 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,17 +1,19 @@ package zio.schema.codec -import zio.schema._ -import zio.schema.codec.ProtobufCodec.Protobuf.WireType.LengthDelimited -import zio.stream.ZPipeline -import zio.{ Chunk, Unsafe, ZIO } - import java.nio.charset.StandardCharsets import java.nio.{ ByteBuffer, ByteOrder } import java.time._ import java.util.UUID + import scala.collection.immutable.ListMap import scala.util.control.{ NoStackTrace, NonFatal } +import zio.schema.MutableSchemaBasedValueBuilder.CreateValueFromSchemaError +import zio.schema._ +import zio.schema.codec.ProtobufCodec.Protobuf.WireType.LengthDelimited +import zio.stream.ZPipeline +import zio.{ Chunk, Unsafe, ZIO } + object ProtobufCodec extends Codec { override def encoder[A](schema: Schema[A]): ZPipeline[Any, Nothing, A, Byte] = ZPipeline.mapChunks(values => values.flatMap(Encoder.process(schema, _))) @@ -87,40 +89,40 @@ object ProtobufCodec extends Codec { } } - final private[codec] case class EncoderState(fieldNumber: Option[Int]) + final private[codec] case class EncoderContext(fieldNumber: Option[Int]) - object Encoder extends ProcessValueWithSchema[Chunk[Byte], EncoderState] { + object Encoder extends MutableSchemaBasedValueProcessor[Chunk[Byte], EncoderContext] { import Protobuf._ - override protected def processPrimitive(state: EncoderState, value: Any, typ: StandardType[Any]): Chunk[Byte] = - encodePrimitive(state.fieldNumber, typ, value) + override protected def processPrimitive(context: EncoderContext, value: Any, typ: StandardType[Any]): Chunk[Byte] = + encodePrimitive(context.fieldNumber, typ, value) override protected def processRecord( - state: EncoderState, + context: EncoderContext, schema: Schema.Record[_], value: ListMap[String, Chunk[Byte]] ): Chunk[Byte] = { val encodedRecord = Chunk.fromIterable(value.values).flatten - encodeKey(WireType.LengthDelimited(encodedRecord.size), state.fieldNumber) ++ encodedRecord + encodeKey(WireType.LengthDelimited(encodedRecord.size), context.fieldNumber) ++ encodedRecord } override protected def processEnum( - state: EncoderState, + context: EncoderContext, schema: Schema.Enum[_], tuple: (String, Chunk[Byte]) ): Chunk[Byte] = { val encoded = tuple._2 - encodeKey(WireType.LengthDelimited(encoded.size), state.fieldNumber) ++ encoded + encodeKey(WireType.LengthDelimited(encoded.size), context.fieldNumber) ++ encoded } override protected def processSequence( - state: EncoderState, + context: EncoderContext, schema: Schema.Sequence[_, _, _], value: Chunk[Chunk[Byte]] ): Chunk[Byte] = if (value.isEmpty) { val data = encodeKey(WireType.LengthDelimited(0), Some(1)) - encodeKey(WireType.LengthDelimited(data.size), state.fieldNumber) ++ encodeKey( + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ encodeKey( WireType.LengthDelimited(0), Some(1) ) @@ -131,17 +133,17 @@ object ProtobufCodec extends Codec { Some(2) ) ++ chunk - encodeKey(WireType.LengthDelimited(data.size), state.fieldNumber) ++ data + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ data } override protected def processDictionary( - state: EncoderState, + 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), state.fieldNumber) ++ encodeKey( + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ encodeKey( WireType.LengthDelimited(0), Some(1) ) @@ -171,17 +173,17 @@ object ProtobufCodec extends Codec { Some(2) ) ++ chunk - encodeKey(WireType.LengthDelimited(data.size), state.fieldNumber) ++ data + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ data } override protected def processSet( - state: EncoderState, + 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), state.fieldNumber) ++ encodeKey( + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ encodeKey( WireType.LengthDelimited(0), Some(1) ) @@ -192,20 +194,20 @@ object ProtobufCodec extends Codec { Some(2) ) ++ chunk - encodeKey(WireType.LengthDelimited(data.size), state.fieldNumber) ++ data + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ data } override protected def processEither( - state: EncoderState, + context: EncoderContext, schema: Schema.Either[_, _], value: Either[Chunk[Byte], Chunk[Byte]] ): Chunk[Byte] = { val encodedEither = value.merge - encodeKey(WireType.LengthDelimited(encodedEither.size), state.fieldNumber) ++ encodedEither + encodeKey(WireType.LengthDelimited(encodedEither.size), context.fieldNumber) ++ encodedEither } override protected def processOption( - state: EncoderState, + context: EncoderContext, schema: Schema.Optional[_], value: Option[Chunk[Byte]] ): Chunk[Byte] = { @@ -214,71 +216,71 @@ object ProtobufCodec extends Codec { case None => encodeKey(WireType.LengthDelimited(0), Some(1)) } - encodeKey(WireType.LengthDelimited(data.size), state.fieldNumber) ++ data + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ data } override protected def processTuple( - state: EncoderState, + context: EncoderContext, schema: Schema.Tuple2[_, _], left: Chunk[Byte], right: Chunk[Byte] ): Chunk[Byte] = { val data = left ++ right - encodeKey(WireType.LengthDelimited(data.size), state.fieldNumber) ++ data + encodeKey(WireType.LengthDelimited(data.size), context.fieldNumber) ++ data } - override protected def processDynamic(state: EncoderState, value: DynamicValue): Option[Chunk[Byte]] = + override protected def processDynamic(context: EncoderContext, value: DynamicValue): Option[Chunk[Byte]] = None - override protected def fail(state: EncoderState, message: String): Chunk[Byte] = + override protected def fail(context: EncoderContext, message: String): Chunk[Byte] = throw new RuntimeException(message) - override protected val initialState: EncoderState = EncoderState(None) + override protected val initialContext: EncoderContext = EncoderContext(None) - override protected def stateForRecordField( - state: EncoderState, + override protected def contextForRecordField( + context: EncoderContext, index: Int, field: Schema.Field[_, _] - ): EncoderState = - state.copy(fieldNumber = Some(index + 1)) + ): EncoderContext = + context.copy(fieldNumber = Some(index + 1)) - override protected def stateForTuple(state: EncoderState, index: Int): EncoderState = - state.copy(fieldNumber = Some(index)) + override protected def contextForTuple(context: EncoderContext, index: Int): EncoderContext = + context.copy(fieldNumber = Some(index)) - override protected def stateForEnumConstructor( - state: EncoderState, + override protected def contextForEnumConstructor( + context: EncoderContext, index: Int, c: Schema.Case[_, _] - ): EncoderState = - state.copy(fieldNumber = Some(index + 1)) + ): EncoderContext = + context.copy(fieldNumber = Some(index + 1)) - override protected def stateForEither(state: EncoderState, e: Either[Unit, Unit]): EncoderState = + override protected def contextForEither(context: EncoderContext, e: Either[Unit, Unit]): EncoderContext = e match { - case Left(_) => state.copy(fieldNumber = Some(1)) - case Right(_) => state.copy(fieldNumber = Some(2)) + case Left(_) => context.copy(fieldNumber = Some(1)) + case Right(_) => context.copy(fieldNumber = Some(2)) } - override protected def stateForOption(state: EncoderState, o: Option[Unit]): EncoderState = + override protected def contextForOption(context: EncoderContext, o: Option[Unit]): EncoderContext = o match { - case None => state.copy(fieldNumber = Some(1)) - case Some(_) => state.copy(fieldNumber = Some(2)) + case None => context.copy(fieldNumber = Some(1)) + case Some(_) => context.copy(fieldNumber = Some(2)) } - override protected def stateForSequence( - state: EncoderState, + override protected def contextForSequence( + context: EncoderContext, s: Schema.Sequence[_, _, _], index: Int - ): EncoderState = - if (canBePacked(s.elementSchema)) state.copy(fieldNumber = None) - else state.copy(fieldNumber = Some(index + 1)) + ): EncoderContext = + if (canBePacked(s.elementSchema)) context.copy(fieldNumber = None) + else context.copy(fieldNumber = Some(index + 1)) - override protected def stateForMap(state: EncoderState, s: Schema.Map[_, _], index: Int): EncoderState = - if (canBePacked(s.keySchema <*> s.valueSchema)) state.copy(fieldNumber = None) - else state.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 stateForSet(state: EncoderState, s: Schema.Set[_], index: Int): EncoderState = - if (canBePacked(s.elementSchema)) state.copy(fieldNumber = None) - else state.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], @@ -453,7 +455,7 @@ object ProtobufCodec extends Codec { final case class DecoderException(message: String) extends RuntimeException(message) with NoStackTrace - class Decoder(chunk: Chunk[Byte]) extends CreateValueFromSchema[Any, DecoderContext] { + class Decoder(chunk: Chunk[Byte]) extends MutableSchemaBasedValueBuilder[Any, DecoderContext] { import Protobuf._ @@ -463,7 +465,7 @@ object ProtobufCodec extends Codec { try { Right(create(schema).asInstanceOf[A]) } catch { - case DecoderException(message) => + case CreateValueFromSchemaError(_, DecoderException(message)) => Left(message) } @@ -550,19 +552,19 @@ object ProtobufCodec extends Codec { context: DecoderContext, record: Schema.Record[_], index: Int - ): (DecoderContext, Option[Int]) = + ): Option[(DecoderContext, Int)] = if (index == record.fields.size) { - (context, None) + None } else { keyDecoder(context) match { case (wt, fieldNumber) => if (record.fields.isDefinedAt(fieldNumber - 1)) { - wt match { + Some(wt match { case LengthDelimited(width) => - (context.limitedTo(state, width), Some(fieldNumber - 1)) + (context.limitedTo(state, width), fieldNumber - 1) case _ => - (context, Some(fieldNumber - 1)) - } + (context, fieldNumber - 1) + }) } else { throw DecoderException(s"Failed to decode record. Schema does not contain field number $fieldNumber.") } @@ -618,7 +620,7 @@ object ProtobufCodec extends Codec { throw DecoderException(s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") } - override protected def startReadingOneSequenceElement( + override protected def startCreatingOneSequenceElement( context: DecoderContext, schema: Schema.Sequence[_, _, _] ): DecoderContext = @@ -636,12 +638,12 @@ object ProtobufCodec extends Codec { } } - override protected def readOneSequenceElement( + override protected def finishedCreatingOneSequenceElement( context: DecoderContext, schema: Schema.Sequence[_, _, _], index: Int - ): (DecoderContext, Boolean) = - (context, state.length(context) > 0) + ): Boolean = + state.length(context) > 0 override protected def createSequence( context: DecoderContext, @@ -663,7 +665,7 @@ object ProtobufCodec extends Codec { throw DecoderException(s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") } - override protected def startReadingOneDictionaryElement( + override protected def startCreatingOneDictionaryElement( context: DecoderContext, schema: Schema.Map[_, _] ): DecoderContext = { @@ -684,18 +686,18 @@ object ProtobufCodec extends Codec { enterFirstTupleElement(elemContext).copy(dictionaryElementContext = Some(elemContext)) } - override protected def startReadingOneDictionaryValue( + override protected def startCreatingOneDictionaryValue( context: DecoderContext, schema: Schema.Map[_, _] ): DecoderContext = enterSecondTupleElement(context.dictionaryElementContext.getOrElse(context)) - override protected def readOneDictionaryElement( + override protected def finishedCreatingOneDictionaryElement( context: DecoderContext, schema: Schema.Map[_, _], index: Int - ): (DecoderContext, Boolean) = - (context, state.length(context) > 0) + ): Boolean = + state.length(context) > 0 override protected def createDictionary( context: DecoderContext, @@ -714,7 +716,7 @@ object ProtobufCodec extends Codec { throw DecoderException(s"Invalid wire type ($wt) or field number ($fieldNumber) for packed sequence") } - override protected def startReadingOneSetElement(context: DecoderContext, schema: Schema.Set[_]): DecoderContext = + override protected def startCreatingOneSetElement(context: DecoderContext, schema: Schema.Set[_]): DecoderContext = if (context.packed) { context } else { @@ -729,12 +731,12 @@ object ProtobufCodec extends Codec { } } - override protected def readOneSetElement( + override protected def finishedCreatingOneSetElement( context: DecoderContext, schema: Schema.Set[_], index: Int - ): (DecoderContext, Boolean) = - (context, state.length(context) > 0) + ): Boolean = + state.length(context) > 0 override protected def createSet(context: DecoderContext, schema: Schema.Set[_], values: Chunk[Any]): Any = values.toSet @@ -825,7 +827,7 @@ object ProtobufCodec extends Codec { override protected def fail(context: DecoderContext, message: String): Any = throw DecoderException(message) - override protected val initialState: DecoderContext = + override protected val initialContext: DecoderContext = DecoderContext(limit = None, packed = false, dictionaryElementContext = None) /** 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 150961c56..5da24c9b1 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,18 +1,20 @@ package zio.schema.codec -import org.apache.thrift.protocol._ -import zio.schema._ -import zio.stream.ZPipeline -import zio.{ Chunk, Unsafe, ZIO } - 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.Try import scala.util.control.NonFatal +import org.apache.thrift.protocol._ + +import zio.schema.MutableSchemaBasedValueBuilder.CreateValueFromSchemaError +import zio.schema._ +import zio.stream.ZPipeline +import zio.{ Chunk, Unsafe, ZIO } + object ThriftCodec extends Codec { override def encoder[A](schema: Schema[A]): ZPipeline[Any, Nothing, A, Byte] = { val encoder = new Encoder() @@ -21,17 +23,14 @@ object ThriftCodec extends Codec { } } - override def decoder[A](schema: Schema[A]): ZPipeline[Any, String, Byte, A] = + override def decoder[A](schema: Schema[A]): ZPipeline[Any, String, Byte, A] = { + val d: Chunk[Byte] => Either[String, A] = decode(schema) ZPipeline.mapChunksZIO { chunk => ZIO.fromEither( - new Decoder(chunk) - .create(schema) - .map(Chunk(_)) - .map(_.asInstanceOf[Chunk[A]]) - .left - .map(err => s"Error at path /${err.path.mkString(".")}: ${err.error}") + d(chunk).map(Chunk.single) ) } + } override def encode[A](schema: Schema[A]): A => Chunk[Byte] = a => new Encoder().encode(schema, a) @@ -39,86 +38,100 @@ object ThriftCodec extends Codec { ch => if (ch.isEmpty) Left("No bytes to decode") - else - new Decoder(ch) - .create(schema) - .map(_.asInstanceOf[A]) - .left - .map( - err => s"Error at path /${err.path.mkString(".")}: ${err.error}" + else { + try { + Right( + new Decoder(ch) + .create(schema) + .asInstanceOf[A] ) + } catch { + case error: CreateValueFromSchemaError[DecoderContext] => + error.cause match { + case error: Error => Left(error.getMessage) + case _ => + Left(Error(error.context.path, error.cause.getMessage, error).getMessage) + } + case NonFatal(err) => + Left(err.getMessage) + } + }: @nowarn - class Encoder extends ProcessValueWithSchema[Unit, Encoder.State] { + class Encoder extends MutableSchemaBasedValueProcessor[Unit, Encoder.Context] { import Encoder._ - override protected def processPrimitive(state: State, value: Any, typ: StandardType[Any]): Unit = { - writeFieldBegin(state.fieldNumber, getPrimitiveType(typ)) + override protected def processPrimitive(context: Context, value: Any, typ: StandardType[Any]): Unit = { + writeFieldBegin(context.fieldNumber, getPrimitiveType(typ)) writePrimitiveType(typ, value) } - override protected def startProcessingRecord(state: State, schema: Schema.Record[_]): Unit = - writeFieldBegin(state.fieldNumber, TType.STRUCT) + override protected def startProcessingRecord(context: Context, schema: Schema.Record[_]): Unit = + writeFieldBegin(context.fieldNumber, TType.STRUCT) override protected def processRecord( - state: State, + context: Context, schema: Schema.Record[_], value: ListMap[String, Unit] ): Unit = writeFieldEnd() - override protected def startProcessingEnum(state: State, schema: Schema.Enum[_]): Unit = - writeFieldBegin(state.fieldNumber, TType.STRUCT) + override protected def startProcessingEnum(context: Context, schema: Schema.Enum[_]): Unit = + writeFieldBegin(context.fieldNumber, TType.STRUCT) - override protected def processEnum(state: State, schema: Schema.Enum[_], tuple: (String, Unit)): Unit = + override protected def processEnum(context: Context, schema: Schema.Enum[_], tuple: (String, Unit)): Unit = writeFieldEnd() - override protected def startProcessingSequence(state: State, schema: Schema.Sequence[_, _, _], size: Int): Unit = { - writeFieldBegin(state.fieldNumber, TType.LIST) + 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( - state: State, + context: Context, schema: Schema.Sequence[_, _, _], value: Chunk[Unit] ): Unit = {} - override protected def startProcessingDictionary(state: State, schema: Schema.Map[_, _], size: Int): Unit = { - writeFieldBegin(state.fieldNumber, TType.MAP) + 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( - state: State, + context: Context, schema: Schema.Map[_, _], value: Chunk[(Unit, Unit)] ): Unit = {} - override protected def startProcessingSet(state: State, schema: Schema.Set[_], size: Int): Unit = { - writeFieldBegin(state.fieldNumber, TType.SET) + 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(state: State, schema: Schema.Set[_], value: Set[Unit]): Unit = {} + override protected def processSet(context: Context, schema: Schema.Set[_], value: Set[Unit]): Unit = {} - override protected def startProcessingEither(state: State, schema: Schema.Either[_, _]): Unit = - writeFieldBegin(state.fieldNumber, TType.STRUCT) + override protected def startProcessingEither(context: Context, schema: Schema.Either[_, _]): Unit = + writeFieldBegin(context.fieldNumber, TType.STRUCT) override protected def processEither( - state: State, + context: Context, schema: Schema.Either[_, _], value: Either[Unit, Unit] ): Unit = writeFieldEnd() - override def startProcessingOption(state: State, schema: Schema.Optional[_]): Unit = - writeFieldBegin(state.fieldNumber, TType.STRUCT) + override def startProcessingOption(context: Context, schema: Schema.Optional[_]): Unit = + writeFieldBegin(context.fieldNumber, TType.STRUCT) - override protected def processOption(state: State, schema: Schema.Optional[_], value: Option[Unit]): Unit = { + override protected def processOption(context: Context, schema: Schema.Optional[_], value: Option[Unit]): Unit = { value match { case None => processPrimitive( - state.copy(fieldNumber = Some(1)), + context.copy(fieldNumber = Some(1)), (), StandardType.UnitType.asInstanceOf[StandardType[Any]] ) @@ -127,54 +140,54 @@ object ThriftCodec extends Codec { writeFieldEnd() } - override protected def startProcessingTuple(state: State, schema: Schema.Tuple2[_, _]): Unit = - writeFieldBegin(state.fieldNumber, TType.STRUCT) + override protected def startProcessingTuple(context: Context, schema: Schema.Tuple2[_, _]): Unit = + writeFieldBegin(context.fieldNumber, TType.STRUCT) override protected def processTuple( - state: State, + context: Context, schema: Schema.Tuple2[_, _], left: Unit, right: Unit ): Unit = writeFieldEnd() - override protected def fail(state: State, message: String): Unit = + override protected def fail(context: Context, message: String): Unit = fail(message) - override protected def processDynamic(state: State, value: DynamicValue): Option[Unit] = + override protected def processDynamic(context: Context, value: DynamicValue): Option[Unit] = None - override protected val initialState: State = State(None) + override protected val initialContext: Context = Context(None) - override protected def stateForRecordField(state: State, index: Int, field: Schema.Field[_, _]): State = - state.copy(fieldNumber = Some((index + 1).toShort)) + override protected def contextForRecordField(context: Context, index: Int, field: Schema.Field[_, _]): Context = + context.copy(fieldNumber = Some((index + 1).toShort)) - override protected def stateForEnumConstructor(state: State, index: Int, c: Schema.Case[_, _]): State = - state.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 stateForEither(state: State, e: Either[Unit, Unit]): State = + override protected def contextForEither(context: Context, e: Either[Unit, Unit]): Context = e match { - case Left(_) => state.copy(fieldNumber = Some(1)) - case Right(_) => state.copy(fieldNumber = Some(2)) + case Left(_) => context.copy(fieldNumber = Some(1)) + case Right(_) => context.copy(fieldNumber = Some(2)) } - override protected def stateForOption(state: State, o: Option[Unit]): State = + override protected def contextForOption(context: Context, o: Option[Unit]): Context = o match { - case None => state.copy(fieldNumber = Some(1)) - case Some(_) => state.copy(fieldNumber = Some(2)) + case None => context.copy(fieldNumber = Some(1)) + case Some(_) => context.copy(fieldNumber = Some(2)) } - override protected def stateForTuple(state: State, index: Int): State = - state.copy(fieldNumber = Some(index.toShort)) + override protected def contextForTuple(context: Context, index: Int): Context = + context.copy(fieldNumber = Some(index.toShort)) - override protected def stateForSequence(state: State, schema: Schema.Sequence[_, _, _], index: Int): State = - state.copy(fieldNumber = None) + override protected def contextForSequence(context: Context, schema: Schema.Sequence[_, _, _], index: Int): Context = + context.copy(fieldNumber = None) - override protected def stateForMap(state: State, schema: Schema.Map[_, _], index: Int): State = - state.copy(fieldNumber = None) + override protected def contextForMap(context: Context, schema: Schema.Map[_, _], index: Int): Context = + context.copy(fieldNumber = None) - override protected def stateForSet(state: State, schema: Schema.Set[_], index: Int): State = - state.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) @@ -329,7 +342,7 @@ object ThriftCodec extends Codec { } object Encoder { - final case class State(fieldNumber: Option[Short]) + final case class Context(fieldNumber: Option[Short]) private def getPrimitiveType[A](standardType: StandardType[A]): Byte = standardType match { @@ -396,189 +409,178 @@ object ThriftCodec extends Codec { } } - case class Error(path: Path, error: String) + type Path = Chunk[String] + type PrimitiveDecoder[A] = Path => A - type Path = Chunk[String] - type Result[A] = scala.util.Either[Error, A] - type PrimitiveResult[A] = Path => Result[A] + case class Error(path: Path, error: String, cause: Throwable) + extends RuntimeException(s"Error at path /${path.mkString(".")}: $error", cause) + + object Error { + def apply(path: Path, error: String): Error = Error(path, error, null) + } - final case class DecoderState(path: Path, expectedCount: Option[Int]) + final case class DecoderContext(path: Path, expectedCount: Option[Int]) - class Decoder(chunk: Chunk[Byte]) extends CreateValueFromSchema[Result[Any], DecoderState] { + 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 flip[A](as: Chunk[Result[A]]): Result[Chunk[A]] = - as.foldLeft[Result[Chunk[A]]](Right(Chunk.empty[A])) { - case (Right(as), Right(a)) => Right(as :+ a) - case (Left(failure), _) => Left(failure) - case (_, Left(failure)) => Left(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(_ => Error(path, s"Unable to decode $name")) + } catch { + case NonFatal(reason) => throw Error(path, s"Unable to decode $name", reason) + } - 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") - override protected def createPrimitive(state: DecoderState, typ: StandardType[_]): Result[Any] = + override protected def createPrimitive(context: DecoderContext, typ: StandardType[_]): Any = typ match { - case StandardType.UnitType => Right(()) - case StandardType.StringType => decodeString(state.path) - case StandardType.BoolType => decodeBoolean(state.path) - case StandardType.ByteType => decodeByte(state.path) - case StandardType.ShortType => decodeShort(state.path) - case StandardType.IntType => decodeInt(state.path) - case StandardType.LongType => decodeLong(state.path) - case StandardType.FloatType => decodeFloat(state.path) - case StandardType.DoubleType => decodeDouble(state.path) - case StandardType.BigIntegerType => decodeBigInteger(state.path) + 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() - decodeBigInteger(state.path).flatMap { unscaled => - p.readFieldBegin() - decodeInt(state.path).flatMap { precision => - p.readFieldBegin() - decodeInt(state.path).map { scale => - p.readFieldBegin() - new java.math.BigDecimal(unscaled, scale, new java.math.MathContext(precision)) - } - } - } - case StandardType.BinaryType => decodeBinary(state.path) + 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 => - decodeString(state.path).flatMap( - decoded => - if (decoded.length == 1) - succeed(decoded.charAt(0)) - else { - fail(state, s"""Expected character, found string "$decoded"""") - } - ) - case StandardType.UUIDType => - decodeString(state.path).flatMap { uuid => - try succeed(UUID.fromString(uuid)) - catch { - case NonFatal(_) => fail(state, "Invalid UUID string") - } + val decoded = decodeString(context.path) + + if (decoded.length == 1) + decoded.charAt(0) + else { + fail(context, s"""Expected character, found string "$decoded"""") } + + case StandardType.UUIDType => + decodeUUID(context.path) case StandardType.DayOfWeekType => - decodeByte(state.path).map(_.toInt).map(DayOfWeek.of) + DayOfWeek.of(decodeByte(context.path).toInt) case StandardType.MonthType => - decodeByte(state.path).map(_.toInt).map(Month.of) + Month.of(decodeByte(context.path).toInt) case StandardType.MonthDayType => p.readFieldBegin() - decodeInt(state.path).flatMap { month => - p.readFieldBegin() - decodeInt(state.path).map { day => - p.readFieldBegin() - MonthDay.of(month, day) - } - } + val month = decodeInt(context.path) + p.readFieldBegin() + val day = decodeInt(context.path) + p.readFieldBegin() + MonthDay.of(month, day) + case StandardType.PeriodType => p.readFieldBegin() - decodeInt(state.path).flatMap { year => - p.readFieldBegin() - decodeInt(state.path).flatMap { month => - p.readFieldBegin() - decodeInt(state.path).map { day => - p.readFieldBegin() - Period.of(year, month, day) - } - } - } + 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(state.path).map(_.intValue).map(Year.of) + Year.of(decodeInt(context.path).intValue) case StandardType.YearMonthType => p.readFieldBegin() - decodeInt(state.path).flatMap { year => - p.readFieldBegin() - decodeInt(state.path).map { month => - p.readFieldBegin() - YearMonth.of(year, month) - } - } - case StandardType.ZoneIdType => decodeString(state.path).map(ZoneId.of) + 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(state.path) - .map(_.intValue) - .map(ZoneOffset.ofTotalSeconds) + ZoneOffset.ofTotalSeconds(decodeInt(context.path).intValue) case StandardType.DurationType => p.readFieldBegin() - decodeLong(state.path).flatMap { seconds => - p.readFieldBegin() - decodeInt(state.path).map { nano => - p.readFieldBegin() - Duration.ofSeconds(seconds, nano.toLong) - } - } + val seconds = decodeLong(context.path) + p.readFieldBegin() + val nano = decodeInt(context.path) + p.readFieldBegin() + Duration.ofSeconds(seconds, nano.toLong) + case StandardType.InstantType(formatter) => - decodeString(state.path).map(v => Instant.from(formatter.parse(v))) + Instant.from(formatter.parse(decodeString(context.path))) case StandardType.LocalDateType(formatter) => - decodeString(state.path).map(LocalDate.parse(_, formatter)) + LocalDate.parse(decodeString(context.path), formatter) case StandardType.LocalTimeType(formatter) => - decodeString(state.path).map(LocalTime.parse(_, formatter)) + LocalTime.parse(decodeString(context.path), formatter) case StandardType.LocalDateTimeType(formatter) => - decodeString(state.path).map(LocalDateTime.parse(_, formatter)) + LocalDateTime.parse(decodeString(context.path), formatter) case StandardType.OffsetTimeType(formatter) => - decodeString(state.path).map(OffsetTime.parse(_, formatter)) + OffsetTime.parse(decodeString(context.path), formatter) case StandardType.OffsetDateTimeType(formatter) => - decodeString(state.path).map(OffsetDateTime.parse(_, formatter)) + OffsetDateTime.parse(decodeString(context.path), formatter) case StandardType.ZonedDateTimeType(formatter) => - decodeString(state.path).map(ZonedDateTime.parse(_, formatter)) - case _ => fail(state, s"Unsupported primitive type $typ") + ZonedDateTime.parse(decodeString(context.path), formatter) + case _ => fail(context, s"Unsupported primitive type $typ") } - override protected def startCreatingRecord(state: DecoderState, record: Schema.Record[_]): DecoderState = - state + override protected def startCreatingRecord(context: DecoderContext, record: Schema.Record[_]): DecoderContext = + context override protected def startReadingField( - state: DecoderState, + context: DecoderContext, record: Schema.Record[_], index: Int - ): (DecoderState, Option[Int]) = { + ): Option[(DecoderContext, Int)] = { val tfield = p.readFieldBegin() - (state, if (tfield.`type` == TType.STOP) None else Some(tfield.id - 1)) + if (tfield.`type` == TType.STOP) None + else Some((context.copy(path = context.path :+ s"fieldId:${tfield.id}"), tfield.id - 1)) } override protected def createRecord( - state: DecoderState, + context: DecoderContext, record: Schema.Record[_], - values: Chunk[(Int, Result[Any])] - ): Result[Any] = { + values: Chunk[(Int, Any)] + ): Any = { val valuesMap = values.toMap val allValues = record.fields.zipWithIndex.map { @@ -587,237 +589,209 @@ object ThriftCodec extends Codec { case Some(value) => value case None => emptyValue(field.schema) match { - case Some(value) => succeed(value) - case None => fail(state, s"Missing value for field ${field.name}") + case Some(value) => value + case None => fail(context.copy(path = context.path :+ field.name), s"Missing value") } } } Unsafe.unsafe { implicit u => - flip(allValues).flatMap { vs => - record.construct(vs) match { - case Left(message) => fail(state, message) - case Right(value) => succeed(value) - } + record.construct(allValues) match { + case Left(message) => fail(context, message) + case Right(value) => value } } } override protected def startCreatingEnum( - state: DecoderState, + context: DecoderContext, cases: Chunk[Schema.Case[_, _]] - ): (DecoderState, Int) = { + ): (DecoderContext, Int) = { val readField = p.readFieldBegin() val consIdx = readField.id - 1 val subtypeCase = cases(consIdx) - (state.copy(path = state.path :+ s"[case:${subtypeCase.id}]"), consIdx) + (context.copy(path = context.path :+ s"[case:${subtypeCase.id}]"), consIdx) } override protected def createEnum( - state: DecoderState, + context: DecoderContext, cases: Chunk[Schema.Case[_, _]], index: Int, - value: Result[Any] - ): Result[Any] = { - value.foreach { _ => - p.readFieldBegin() - } + value: Any + ): Any = { + p.readFieldBegin() value } override protected def startCreatingSequence( - state: DecoderState, + context: DecoderContext, schema: Schema.Sequence[_, _, _] - ): Option[DecoderState] = { + ): Option[DecoderContext] = { val begin = p.readListBegin() if (begin.size == 0) None else - Some(state.copy(expectedCount = Some(begin.size))) + Some(context.copy(expectedCount = Some(begin.size))) } - override protected def startReadingOneSequenceElement( - state: DecoderState, + override protected def startCreatingOneSequenceElement( + context: DecoderContext, schema: Schema.Sequence[_, _, _] - ): DecoderState = - state + ): DecoderContext = + context - override protected def readOneSequenceElement( - state: DecoderState, + override protected def finishedCreatingOneSequenceElement( + context: DecoderContext, schema: Schema.Sequence[_, _, _], index: Int - ): (DecoderState, Boolean) = { - val continue = state.expectedCount.map(_ - (index + 1)).exists(_ > 0) - (state, continue) - } + ): Boolean = + context.expectedCount.map(_ - (index + 1)).exists(_ > 0) override protected def createSequence( - state: DecoderState, + context: DecoderContext, schema: Schema.Sequence[_, _, _], - values: Chunk[Result[Any]] - ): Result[Any] = - flip(values).map(chunk => schema.fromChunk.asInstanceOf[Chunk[Any] => Any](chunk)) + values: Chunk[Any] + ): Any = + schema.fromChunk.asInstanceOf[Chunk[Any] => Any](values) override protected def startCreatingDictionary( - state: DecoderState, + context: DecoderContext, schema: Schema.Map[_, _] - ): Option[DecoderState] = { + ): Option[DecoderContext] = { val begin = p.readMapBegin() if (begin.size == 0) None else - Some(state.copy(expectedCount = Some(begin.size))) + Some(context.copy(expectedCount = Some(begin.size))) } - override protected def startReadingOneDictionaryElement( - state: DecoderState, + override protected def startCreatingOneDictionaryElement( + context: DecoderContext, schema: Schema.Map[_, _] - ): DecoderState = - state + ): DecoderContext = + context - override protected def startReadingOneDictionaryValue( - state: DecoderState, + override protected def startCreatingOneDictionaryValue( + context: DecoderContext, schema: Schema.Map[_, _] - ): DecoderState = - state + ): DecoderContext = + context - override protected def readOneDictionaryElement( - state: DecoderState, + override protected def finishedCreatingOneDictionaryElement( + context: DecoderContext, schema: Schema.Map[_, _], index: Int - ): (DecoderState, Boolean) = { - val continue = state.expectedCount.map(_ - (index + 1)).exists(_ > 0) - (state, continue) - } + ): Boolean = + context.expectedCount.map(_ - (index + 1)).exists(_ > 0) override protected def createDictionary( - state: DecoderState, + context: DecoderContext, schema: Schema.Map[_, _], - values: Chunk[(Result[Any], Result[Any])] - ): Result[Any] = - flip(values.map { - case (aa, bb) => - aa.flatMap { a => - bb.map { b => - (a, b) - } - } - }).map(_.toMap) + values: Chunk[(Any, Any)] + ): Any = + values.toMap - override protected def startCreatingSet(state: DecoderState, schema: Schema.Set[_]): Option[DecoderState] = { + override protected def startCreatingSet(context: DecoderContext, schema: Schema.Set[_]): Option[DecoderContext] = { val begin = p.readSetBegin() if (begin.size == 0) None - else Some(state.copy(expectedCount = Some(begin.size))) + else Some(context.copy(expectedCount = Some(begin.size))) } - override protected def startReadingOneSetElement(state: DecoderState, schema: Schema.Set[_]): DecoderState = - state + override protected def startCreatingOneSetElement(context: DecoderContext, schema: Schema.Set[_]): DecoderContext = + context - override protected def readOneSetElement( - state: DecoderState, + override protected def finishedCreatingOneSetElement( + context: DecoderContext, schema: Schema.Set[_], index: Int - ): (DecoderState, Boolean) = { - val continue = state.expectedCount.map(_ - (index + 1)).exists(_ > 0) - (state, continue) - } + ): Boolean = + context.expectedCount.map(_ - (index + 1)).exists(_ > 0) override protected def createSet( - state: DecoderState, + context: DecoderContext, schema: Schema.Set[_], - values: Chunk[Result[Any]] - ): Result[Any] = - flip(values).map(_.toSet) + values: Chunk[Any] + ): Any = + values.toSet override protected def startCreatingOptional( - state: DecoderState, + context: DecoderContext, schema: Schema.Optional[_] - ): Option[DecoderState] = { + ): Option[DecoderContext] = { val field = p.readFieldBegin() field.id match { case 1 => None - case 2 => Some(state.copy(path = state.path :+ "Some")) - // TODO + case 2 => Some(context.copy(path = context.path :+ "Some")) + case id => + fail(context, s"Error decoding optional, wrong field id $id").asInstanceOf[Option[DecoderContext]] } } override protected def createOptional( - state: DecoderState, + context: DecoderContext, schema: Schema.Optional[_], - value: Option[Result[Any]] - ): Result[Any] = { + value: Option[Any] + ): Any = { p.readFieldBegin() - value match { - case Some(value) => value.map(Some(_)) - case None => succeed(None) - } + value } override protected def startCreatingEither( - state: DecoderState, + context: DecoderContext, schema: Schema.Either[_, _] - ): Either[DecoderState, DecoderState] = { + ): Either[DecoderContext, DecoderContext] = { val readField = p.readFieldBegin() readField.id match { - case 1 => Left(state.copy(path = state.path :+ "either:left")) - case 2 => Right(state.copy(path = state.path :+ "either:right")) - //case _ => fail(path, "Failed to decode either.") // TODO + 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( - state: DecoderState, + context: DecoderContext, schema: Schema.Either[_, _], - value: Either[Result[Any], Result[Any]] - ): Result[Any] = - value match { - case Left(value) => value.map(Left(_)) - case Right(value) => value.map(Right(_)) - } + value: Either[Any, Any] + ): Any = + value - override protected def startCreatingTuple(state: DecoderState, schema: Schema.Tuple2[_, _]): DecoderState = { + override protected def startCreatingTuple(context: DecoderContext, schema: Schema.Tuple2[_, _]): DecoderContext = { p.readFieldBegin() - state + context } override protected def startReadingSecondTupleElement( - state: DecoderState, + context: DecoderContext, schema: Schema.Tuple2[_, _] - ): DecoderState = { + ): DecoderContext = { p.readFieldBegin() - state + context } override protected def createTuple( - state: DecoderState, + context: DecoderContext, schema: Schema.Tuple2[_, _], - left: Result[Any], - right: Result[Any] - ): Result[Any] = { + left: Any, + right: Any + ): Any = { p.readFieldBegin() - left.flatMap { l => - right.map { r => - (l, r) - } - } + (left, right) } - override protected def createDynamic(state: DecoderState): Option[Result[Any]] = + override protected def createDynamic(context: DecoderContext): Option[Any] = None override protected def transform( - state: DecoderState, - value: Result[Any], + context: DecoderContext, + value: Any, f: Any => Either[String, Any] - ): Result[Any] = - value.flatMap { v => - f(v) match { - case Left(value) => fail(state, value) - case Right(value) => succeed(value) - } + ): Any = + f(value) match { + case Left(value) => fail(context, value) + case Right(value) => value } - override protected def fail(state: DecoderState, message: String): Result[Any] = - Left(Error(state.path, message)) + override protected def fail(context: DecoderContext, message: String): Any = + throw Error(context.path, message) - override protected val initialState: DecoderState = DecoderState(Chunk.empty, None) + override protected val initialContext: DecoderContext = DecoderContext(Chunk.empty, None) private def emptyValue[A](schema: Schema[A]): Option[A] = schema match { case Schema.Lazy(s) => emptyValue(s()) 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 0f9b36d9f..a3df9a3e4 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 @@ -790,8 +790,8 @@ object ThriftCodecSpec extends ZIOSpecDefault { for { d <- decode(Record.schemaRecord, "0F").exit d2 <- decodeNS(Record.schemaRecord, "0F").exit - } yield assert(d)(fails(equalTo("Error at path /: Error reading field begin: MaxMessageSize reached"))) && - assert(d2)(fails(equalTo("Error at path /: Error reading field begin: MaxMessageSize reached"))) + } yield assert(d)(fails(equalTo("Error at path /: MaxMessageSize reached"))) && + assert(d2)(fails(equalTo("Error at path /: MaxMessageSize reached"))) }, test("missing value") { for { diff --git a/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala b/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala deleted file mode 100644 index c57d0c6e7..000000000 --- a/zio-schema/shared/src/main/scala/zio/schema/CreateValueFromSchema.scala +++ /dev/null @@ -1,1069 +0,0 @@ -package zio.schema - -import zio.{ Chunk, ChunkBuilder } - -trait CreateValueFromSchema[Target, State] { - - protected def createPrimitive(state: State, typ: StandardType[_]): Target - - protected def startCreatingRecord(state: State, record: Schema.Record[_]): State - protected def startReadingField(state: State, record: Schema.Record[_], index: Int): (State, Option[Int]) - protected def createRecord(state: State, record: Schema.Record[_], values: Chunk[(Int, Target)]): Target - - protected def startCreatingEnum(state: State, cases: Chunk[Schema.Case[_, _]]): (State, Int) - - protected def createEnum(state: State, cases: Chunk[Schema.Case[_, _]], index: Int, value: Target): Target - - protected def startCreatingSequence(state: State, schema: Schema.Sequence[_, _, _]): Option[State] - protected def startReadingOneSequenceElement(state: State, schema: Schema.Sequence[_, _, _]): State - protected def readOneSequenceElement(state: State, schema: Schema.Sequence[_, _, _], index: Int): (State, Boolean) - protected def createSequence(state: State, schema: Schema.Sequence[_, _, _], values: Chunk[Target]): Target - - protected def startCreatingDictionary(state: State, schema: Schema.Map[_, _]): Option[State] - protected def startReadingOneDictionaryElement(state: State, schema: Schema.Map[_, _]): State - protected def startReadingOneDictionaryValue(state: State, schema: Schema.Map[_, _]): State - protected def readOneDictionaryElement(state: State, schema: Schema.Map[_, _], index: Int): (State, Boolean) - protected def createDictionary(state: State, schema: Schema.Map[_, _], values: Chunk[(Target, Target)]): Target - - protected def startCreatingSet(state: State, schema: Schema.Set[_]): Option[State] - protected def startReadingOneSetElement(state: State, schema: Schema.Set[_]): State - protected def readOneSetElement(state: State, schema: Schema.Set[_], index: Int): (State, Boolean) - protected def createSet(state: State, schema: Schema.Set[_], values: Chunk[Target]): Target - - protected def startCreatingOptional(state: State, schema: Schema.Optional[_]): Option[State] - protected def createOptional(state: State, schema: Schema.Optional[_], value: Option[Target]): Target - - protected def startCreatingEither(state: State, schema: Schema.Either[_, _]): Either[State, State] - protected def createEither(state: State, schema: Schema.Either[_, _], value: Either[Target, Target]): Target - - protected def startCreatingTuple(state: State, schema: Schema.Tuple2[_, _]): State - protected def startReadingSecondTupleElement(state: State, schema: Schema.Tuple2[_, _]): State - protected def createTuple(state: State, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target - - protected def createDynamic(state: State): Option[Target] - - protected def transform(state: State, value: Target, f: Any => Either[String, Any]): Target - protected def fail(state: State, message: String): Target - - protected val initialState: State - - 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 stateStack: List[State] = List(initialState) - - 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 pushState(s: State): Unit = - stateStack = s :: stateStack - - def record(record: Schema.Record[_]): Unit = { - val values = ChunkBuilder.make[(Int, Target)](record.fields.size) - - def readField(index: Int): Unit = { - stateStack = stateStack.tail - val (updatedState, fieldIndex) = startReadingField(stateStack.head, record, index) - fieldIndex match { - case Some(idx) => - currentSchema = record.fields(idx).schema - pushState(updatedState) - push { field => - val elem = (idx, field) - values += elem - readField(index + 1) - } - case None => - finishWith(createRecord(stateStack.head, record, values.result())) - } - } - - pushState(startCreatingRecord(stateStack.head, record)) - readField(0) - } - - def enumCases(casesChunk: Chunk[Schema.Case[_, _]]): Unit = { - val (newState, index) = startCreatingEnum(stateStack.head, casesChunk) - currentSchema = casesChunk(index).schema - pushState(newState) - push { value => - stateStack = stateStack.tail - finishWith(createEnum(stateStack.head, casesChunk, index, value)) - } - } - - while (result.isEmpty) { - val state = stateStack.head - currentSchema match { - - case l @ Schema.Lazy(_) => - currentSchema = l.schema - - case Schema.Primitive(p, _) => - finishWith(createPrimitive(state, 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(state, message)) - - case s @ Schema.Sequence(elementSchema, _, _, _, _) => - val elems = ChunkBuilder.make[Target]() - - def readOne(index: Int): Unit = - push { elem => - elems += elem - - stateStack = stateStack.tail - val (newState0, continue) = readOneSequenceElement(stateStack.head, s, index) - - if (continue) { - currentSchema = elementSchema - pushState(startReadingOneSequenceElement(newState0, s)) - readOne(index + 1) - } else { - val state = stateStack.head - stateStack = stateStack.tail - finishWith(createSequence(state, s, elems.result())) - } - } - - currentSchema = elementSchema - startCreatingSequence(state, s) match { - case Some(startingState) => - pushState(startingState) - pushState(startReadingOneSequenceElement(startingState, s)) - readOne(0) - case None => - finishWith(createSequence(stateStack.head, 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 - pushState(startReadingOneDictionaryValue(state, s)) - - push { value => - val elem = (key, value) - elems += elem - - stateStack = stateStack.tail.tail - val (newState0, continue) = readOneDictionaryElement(stateStack.head, s, index) - - if (continue) { - currentSchema = ks - pushState(startReadingOneDictionaryElement(newState0, s)) - readOne(index + 1) - } else { - val state = stateStack.head - stateStack = stateStack.tail - finishWith(createDictionary(state, s, elems.result())) - } - } - } - - startCreatingDictionary(state, s) match { - case Some(startingState) => - currentSchema = ks - pushState(startingState) - pushState(startReadingOneDictionaryElement(startingState, s)) - readOne(0) - case None => - finishWith(createDictionary(stateStack.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 - - stateStack = stateStack.tail - val (newState0, continue) = readOneSetElement(stateStack.head, s, index) - - if (continue) { - currentSchema = as - pushState(startReadingOneSetElement(newState0, s)) - readOne(index + 1) - } else { - val state = stateStack.head - stateStack = stateStack.tail - finishWith(createSet(state, s, elems.result())) - } - } - - startCreatingSet(state, s) match { - case Some(startingState) => - currentSchema = as - pushState(startingState) - pushState(startReadingOneSetElement(startingState, s)) - readOne(0) - case None => - finishWith(createSet(stateStack.head, s, Chunk.empty)) - } - - case s: Schema.Either[l, r] => - startCreatingEither(state, s) match { - case Left(newState) => - currentSchema = s.left - pushState(newState) - push { value => - stateStack = stateStack.tail - finishWith(createEither(stateStack.head, s, Left(value))) - } - case Right(newState) => - currentSchema = s.right - pushState(newState) - push { value => - stateStack = stateStack.tail - finishWith(createEither(stateStack.head, s, Right(value))) - } - } - - case s: Schema.Tuple2[a, b] => - currentSchema = s.left - pushState(startCreatingTuple(state, s)) - push { left => - stateStack = stateStack.tail - val newState = startReadingSecondTupleElement(stateStack.head, s) - currentSchema = s.right - pushState(newState) - push { right => - stateStack = stateStack.tail - finishWith(createTuple(stateStack.head, s, left, right)) - } - } - - case s: Schema.Optional[a] => - startCreatingOptional(state, s) match { - case Some(newState) => - currentSchema = s.schema - pushState(newState) - push { value => - stateStack = stateStack.tail - finishWith(createOptional(stateStack.head, s, Some(value))) - } - case None => - finishWith(createOptional(stateStack.head, s, None)) - } - - case Schema.Transform(schema, f, _, _, _) => - currentSchema = schema - push { result => - finishWith(transform(state, result, f.asInstanceOf[Any => Either[String, Any]])) - } - - case s @ Schema.CaseClass0(_, _, _) => - finishWith(createRecord(state, 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(state) match { - case Some(value) => - finishWith(value) - case None => - currentSchema = Schema.dynamicValue - } - } - } - result.get - } -} - -trait CreateValueFromSchemaWithoutState[Target] extends CreateValueFromSchema[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): (Unit, Option[Int]) = - ((), startReadingField(record, index)) - 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 startReadingOneSequenceElement(state: Unit, schema: Schema.Sequence[_, _, _]): Unit = - startReadingOneSequenceElement(schema) - - protected def startReadingOneSequenceElement(schema: Schema.Sequence[_, _, _]): Unit - - override protected def readOneSequenceElement( - context: Unit, - schema: Schema.Sequence[_, _, _], - index: Int - ): (Unit, Boolean) = - ((), readOneSequenceElement(schema, index)) - - protected def readOneSequenceElement(schema: Schema.Sequence[_, _, _], index: Int): Boolean - - 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 readOneDictionaryElement( - context: Unit, - schema: Schema.Map[_, _], - index: Int - ): (Unit, Boolean) = - ((), readOneDictionaryElement(schema, index)) - - override protected def startReadingOneDictionaryElement(state: Unit, schema: Schema.Map[_, _]): Unit = - startReadingOneDictionaryKey(schema) - - protected def startReadingOneDictionaryKey(schema: Schema.Map[_, _]): Unit - - override protected def startReadingOneDictionaryValue(state: Unit, schema: Schema.Map[_, _]): Unit = - startReadingOneDictionaryValue(schema) - - protected def startReadingOneDictionaryValue(schema: Schema.Map[_, _]): Unit - - protected def readOneDictionaryElement(schema: Schema.Map[_, _], index: Int): Boolean - - 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 startReadingOneSetElement(state: Unit, schema: Schema.Set[_]): Unit = - startReadingOneSetElement(schema) - - protected def startReadingOneSetElement(schema: Schema.Set[_]): Unit - - override protected def readOneSetElement(context: Unit, schema: Schema.Set[_], index: Int): (Unit, Boolean) = - ((), readOneSetElement(schema, index)) - - protected def readOneSetElement(schema: Schema.Set[_], index: Int): Boolean - - 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]): Target = - transform(value, f) - - protected def transform(value: Target, f: Any => Either[String, Any]): Target - - override protected def fail(context: Unit, message: String): Target = - fail(message) - - protected def fail(message: String): Target - - override protected val initialState: Unit = () -} 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 4ca58c5b3..1d5b862b6 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/DynamicValue.scala @@ -117,7 +117,7 @@ sealed trait DynamicValue { } object DynamicValue { - private object FromSchemaAndValue extends ProcessSchemaAndValueWithoutState[DynamicValue] { + private object FromSchemaAndValue extends SimpleMutableSchemaBasedValueProcessor[DynamicValue] { override protected def processPrimitive(value: Any, typ: StandardType[Any]): DynamicValue = DynamicValue.Primitive(value, typ) 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..014fe8c0c --- /dev/null +++ b/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueBuilder.scala @@ -0,0 +1,1163 @@ +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]): 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 Schema.Transform(schema, f, _, _, _) => + currentSchema = schema + push { result => + finishWith(transform(currentContext, result, f.asInstanceOf[Any => Either[String, Any]])) + } + + 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]): Target = + transform(value, f) + + protected def transform(value: Target, f: Any => Either[String, Any]): 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/ProcessSchemaAndValue.scala b/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueProcessor.scala similarity index 72% rename from zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala rename to zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueProcessor.scala index 460da87bc..e2e3d78f6 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/ProcessSchemaAndValue.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueProcessor.scala @@ -5,68 +5,120 @@ import scala.collection.immutable.ListMap import zio.{ Chunk, ChunkBuilder } -trait ProcessValueWithSchema[Target, State] { - protected def processPrimitive(state: State, value: Any, typ: StandardType[Any]): Target +/** 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] { - @nowarn protected def startProcessingRecord(state: State, schema: Schema.Record[_]): Unit = {} + /** Process a primitive value */ + protected def processPrimitive(context: Context, value: Any, typ: StandardType[Any]): Target - protected def processRecord(state: State, schema: Schema.Record[_], value: ListMap[String, Target]): Target + /** Called before processing a record (before calling processXYZ for the record's fields) */ + @nowarn protected def startProcessingRecord(context: Context, schema: Schema.Record[_]): Unit = {} - @nowarn protected def startProcessingEnum(state: State, schema: Schema.Enum[_]): 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 - protected def processEnum(state: State, schema: Schema.Enum[_], tuple: (String, Target)): Target + /** Called before processing an enum */ + @nowarn protected def startProcessingEnum(context: Context, schema: Schema.Enum[_]): Unit = {} - @nowarn protected def startProcessingSequence(state: State, schema: Schema.Sequence[_, _, _], size: Int): 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 - protected def processSequence(state: State, schema: Schema.Sequence[_, _, _], value: Chunk[Target]): Target + /** Called before processing a sequence */ + @nowarn protected def startProcessingSequence(context: Context, schema: Schema.Sequence[_, _, _], size: Int): Unit = {} - @nowarn protected def startProcessingDictionary(state: State, schema: Schema.Map[_, _], size: Int): Unit = {} + /** Process a sequence using its already processed elements */ + protected def processSequence(context: Context, schema: Schema.Sequence[_, _, _], value: Chunk[Target]): Target - protected def processDictionary(state: State, schema: Schema.Map[_, _], value: Chunk[(Target, Target)]): Target + /** Called before processing a dictionary */ + @nowarn protected def startProcessingDictionary(context: Context, schema: Schema.Map[_, _], size: Int): Unit = {} - @nowarn protected def startProcessingSet(state: State, schema: Schema.Set[_], 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 - protected def processSet(state: State, schema: Schema.Set[_], value: Set[Target]): Target + /** Called before processing a set */ + @nowarn protected def startProcessingSet(context: Context, schema: Schema.Set[_], size: Int): Unit = {} - @nowarn protected def startProcessingEither(state: State, schema: Schema.Either[_, _]): Unit = {} + /** Process a set using its already processed elements */ + protected def processSet(context: Context, schema: Schema.Set[_], value: Set[Target]): Target - protected def processEither(state: State, schema: Schema.Either[_, _], value: Either[Target, Target]): Target + /** Called before processing and either value */ + @nowarn protected def startProcessingEither(context: Context, schema: Schema.Either[_, _]): Unit = {} - @nowarn protected def startProcessingOption(state: State, schema: Schema.Optional[_]): 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 - protected def processOption(state: State, schema: Schema.Optional[_], value: Option[Target]): Target + /** Called before processing an option value */ + @nowarn protected def startProcessingOption(context: Context, schema: Schema.Optional[_]): Unit = {} - @nowarn protected def startProcessingTuple(state: State, schema: Schema.Tuple2[_, _]): 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 - protected def processTuple(state: State, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target + /** Called before processing a pair of values */ + @nowarn protected def startProcessingTuple(context: Context, schema: Schema.Tuple2[_, _]): Unit = {} - protected def processDynamic(state: State, value: DynamicValue): Option[Target] + /** Process a tuple using its already processed left and right values */ + protected def processTuple(context: Context, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target - protected def fail(state: State, message: String): 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] - protected val initialState: State + /** Fails the processing */ + protected def fail(context: Context, message: String): Target - protected def stateForRecordField(state: State, index: Int, field: Schema.Field[_, _]): State - protected def stateForTuple(state: State, index: Int): State - protected def stateForEnumConstructor(state: State, index: Int, c: Schema.Case[_, _]): State - protected def stateForEither(state: State, e: Either[Unit, Unit]): State - protected def stateForOption(state: State, o: Option[Unit]): State - protected def stateForSequence(state: State, schema: Schema.Sequence[_, _, _], index: Int): State - protected def stateForMap(state: State, schema: Schema.Map[_, _], index: Int): State - protected def stateForSet(state: State, schema: Schema.Set[_], index: Int): State + /** 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 stateStack: List[State] = List(initialState) + var contextStack: List[Context] = List(initialContext) def push(f: Target => Unit): Unit = stack = f :: stack - def pushState(s: State): Unit = - stateStack = s :: stateStack + def pushContext(s: Context): Unit = + contextStack = s :: contextStack def finishWith(resultValue: Target): Unit = if (stack.nonEmpty) { @@ -85,12 +137,12 @@ trait ProcessValueWithSchema[Target, State] { case next :: _ => currentSchema = next.schema currentValue = next.asInstanceOf[Schema.Field[Any, Any]].get(record) - pushState(stateForRecordField(stateStack.head, index, next)) + pushContext(contextForRecordField(contextStack.head, index, next)) push(processField(index, remaining, _)) case Nil => finishWith( processRecord( - stateStack.head, + contextStack.head, s, fs.map(_.name).zip(values.result()).foldLeft(ListMap.empty[String, Target]) { case (lm, pair) => @@ -101,18 +153,18 @@ trait ProcessValueWithSchema[Target, State] { } def processField(index: Int, currentStructure: List[Schema.Field[_, _]], fieldResult: Target): Unit = { - stateStack = stateStack.tail + contextStack = contextStack.tail values += fieldResult val remaining = currentStructure.tail processNext(index + 1, remaining) } - startProcessingRecord(stateStack.head, s) + startProcessingRecord(contextStack.head, s) processNext(0, fs.toList) } def enumCases(s: Schema.Enum[_], cs: Schema.Case[_, _]*): Unit = { - startProcessingEnum(stateStack.head, s) + startProcessingEnum(contextStack.head, s) var found = false val it = cs.iterator @@ -123,10 +175,10 @@ trait ProcessValueWithSchema[Target, State] { case Some(v) => currentValue = v currentSchema = c.schema - pushState(stateForEnumConstructor(stateStack.head, index, c)) + pushContext(contextForEnumConstructor(contextStack.head, index, c)) push { dv => - stateStack = stateStack.tail - finishWith(processEnum(stateStack.head, s, c.id -> dv)) + contextStack = contextStack.tail + finishWith(processEnum(contextStack.head, s, c.id -> dv)) } found = true case None => @@ -136,12 +188,12 @@ trait ProcessValueWithSchema[Target, State] { if (!found) { //This should never happen unless someone manually builds an Enum and doesn't include all cases - finishWith(fail(stateStack.head, "Invalid enum constructor")) + finishWith(fail(contextStack.head, "Invalid enum constructor")) } } while (result.isEmpty) { - val state = stateStack.head + val currentContext = contextStack.head currentSchema match { @@ -149,7 +201,7 @@ trait ProcessValueWithSchema[Target, State] { currentSchema = l.schema case Schema.Primitive(p, _) => - finishWith(processPrimitive(state, currentValue, p.asInstanceOf[StandardType[Any]])) + finishWith(processPrimitive(currentContext, currentValue, p.asInstanceOf[StandardType[Any]])) case s @ Schema.GenericRecord(_, structure, _) => val map = currentValue.asInstanceOf[ListMap[String, _]] @@ -161,12 +213,12 @@ trait ProcessValueWithSchema[Target, State] { case next :: _ => currentSchema = next.schema currentValue = map(next.name) - pushState(stateForRecordField(state, index, next)) + pushContext(contextForRecordField(currentContext, index, next)) push(processField(index, remaining, _)) case Nil => finishWith( processRecord( - state, + currentContext, s, structureChunk.map(_.name).zip(values.result()).foldLeft(ListMap.empty[String, Target]) { case (lm, pair) => @@ -181,13 +233,13 @@ trait ProcessValueWithSchema[Target, State] { currentStructure: List[Schema.Field[ListMap[String, _], _]], fieldResult: Target ): Unit = { - stateStack = stateStack.tail + contextStack = contextStack.tail values += fieldResult val remaining = currentStructure.tail processNext(index + 1, remaining) } - startProcessingRecord(state, s) + startProcessingRecord(currentContext, s) processNext(0, structureChunk.toList) case s @ Schema.Enum1(_, case1, _) => @@ -676,20 +728,20 @@ trait ProcessValueWithSchema[Target, State] { enumCases(s, cases.toSeq: _*) case Schema.Fail(message, _) => - finishWith(fail(state, 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 = { - stateStack = stateStack.tail + contextStack = contextStack.tail if (inputIdx == inputChunk.size) { - finishWith(processSequence(state, s, resultChunk.result())) + finishWith(processSequence(currentContext, s, resultChunk.result())) } else { currentSchema = schema currentValue = inputChunk(inputIdx) - pushState(stateForSequence(state, s, inputIdx)) + pushContext(contextForSequence(currentContext, s, inputIdx)) push { dv => resultChunk += dv processNext(inputIdx + 1) @@ -697,8 +749,8 @@ trait ProcessValueWithSchema[Target, State] { } } - startProcessingSequence(state, s, inputChunk.size) - pushState(stateForSequence(state, s, 0)) + startProcessingSequence(currentContext, s, inputChunk.size) + pushContext(contextForSequence(currentContext, s, 0)) processNext(0) case s @ Schema.Map(ks: Schema[k], vs: Schema[v], _) => @@ -707,21 +759,21 @@ trait ProcessValueWithSchema[Target, State] { def processNext(inputIdx: Int): Unit = if (inputIdx == inputChunk.size) { - finishWith(processDictionary(state, s, resultChunk.result())) + finishWith(processDictionary(currentContext, s, resultChunk.result())) } else { currentSchema = ks val currentTuple = inputChunk(inputIdx) currentValue = currentTuple._1 - pushState(stateForMap(state, s, inputIdx)) + pushContext(contextForMap(currentContext, s, inputIdx)) push { (a: Target) => - stateStack = stateStack.tail + contextStack = contextStack.tail currentSchema = vs currentValue = currentTuple._2 - pushState(stateForMap(state, s, inputIdx)) + pushContext(contextForMap(currentContext, s, inputIdx)) push { (b: Target) => - stateStack = stateStack.tail + contextStack = contextStack.tail val pair = (a, b) resultChunk += pair processNext(inputIdx + 1) @@ -729,7 +781,7 @@ trait ProcessValueWithSchema[Target, State] { } } - startProcessingDictionary(state, s, inputChunk.size) + startProcessingDictionary(currentContext, s, inputChunk.size) processNext(0) case s @ Schema.Set(as: Schema[a], _) => @@ -737,13 +789,13 @@ trait ProcessValueWithSchema[Target, State] { val resultChunk = ChunkBuilder.make[Target](inputChunk.size) def processNext(inputIdx: Int): Unit = { - stateStack = stateStack.tail + contextStack = contextStack.tail if (inputIdx == inputChunk.size) { - finishWith(processSet(state, s, resultChunk.result().toSet)) + finishWith(processSet(currentContext, s, resultChunk.result().toSet)) } else { currentSchema = as currentValue = inputChunk(inputIdx) - pushState(stateForSet(state, s, inputIdx)) + pushContext(contextForSet(currentContext, s, inputIdx)) push { dv => resultChunk += dv processNext(inputIdx + 1) @@ -751,74 +803,74 @@ trait ProcessValueWithSchema[Target, State] { } } - startProcessingSet(state, s, inputChunk.size) - pushState(stateForSet(state, s, 0)) + startProcessingSet(currentContext, s, inputChunk.size) + pushContext(contextForSet(currentContext, s, 0)) processNext(0) case s: Schema.Either[l, r] => - startProcessingEither(state, s) + startProcessingEither(currentContext, s) currentValue.asInstanceOf[Either[l, r]] match { case Left(value: l) => currentValue = value currentSchema = s.left - pushState(stateForEither(state, Left(()))) + pushContext(contextForEither(currentContext, Left(()))) push { dyn => - stateStack = stateStack.tail - finishWith(processEither(state, s, Left(dyn))) + contextStack = contextStack.tail + finishWith(processEither(currentContext, s, Left(dyn))) } case Right(value: r) => currentValue = value currentSchema = s.right - pushState(stateForEither(state, Right(()))) + pushContext(contextForEither(currentContext, Right(()))) push { dyn => - stateStack = stateStack.tail - finishWith(processEither(state, s, Right(dyn))) + contextStack = contextStack.tail + finishWith(processEither(currentContext, s, Right(dyn))) } } case s: Schema.Tuple2[a, b] => - startProcessingTuple(state, s) + startProcessingTuple(currentContext, s) val (a: a, b: b) = currentValue.asInstanceOf[(a, b)] currentValue = a currentSchema = s.left - pushState(stateForTuple(state, 1)) + pushContext(contextForTuple(currentContext, 1)) push { dynA => currentValue = b currentSchema = s.right - stateStack = stateStack.tail - pushState(stateForTuple(state, 2)) + contextStack = contextStack.tail + pushContext(contextForTuple(currentContext, 2)) push { dynB => - stateStack = stateStack.tail - finishWith(processTuple(state, s, dynA, dynB)) + contextStack = contextStack.tail + finishWith(processTuple(currentContext, s, dynA, dynB)) } } case s: Schema.Optional[a] => - startProcessingOption(state, s) + startProcessingOption(currentContext, s) currentValue.asInstanceOf[Option[a]] match { case Some(value: a) => currentValue = value currentSchema = s.schema - pushState(stateForOption(state, Some(()))) + pushContext(contextForOption(currentContext, Some(()))) push { dyn => - stateStack = stateStack.tail - finishWith(processOption(state, s, Some(dyn))) + contextStack = contextStack.tail + finishWith(processOption(currentContext, s, Some(dyn))) } case None => - finishWith(processOption(state, s, None)) + finishWith(processOption(currentContext, s, None)) } case Schema.Transform(schema, _, g, _, _) => g.asInstanceOf[Any => Either[String, Any]](currentValue) match { case Left(message) => - finishWith(fail(state, message)) + finishWith(fail(currentContext, message)) case Right(a) => currentValue = a currentSchema = schema } case s @ Schema.CaseClass0(_, _, _) => - finishWith(processRecord(state, s, ListMap())) + finishWith(processRecord(currentContext, s, ListMap())) case s @ Schema.CaseClass1(_, f, _, _) => fields(s, currentValue, f) @@ -1266,7 +1318,7 @@ trait ProcessValueWithSchema[Target, State] { f22 ) case Schema.Dynamic(_) => - processDynamic(state, currentValue.asInstanceOf[DynamicValue]) match { + processDynamic(currentContext, currentValue.asInstanceOf[DynamicValue]) match { case Some(target) => finishWith(target) case None => currentSchema = Schema.dynamicValue @@ -1277,7 +1329,8 @@ trait ProcessValueWithSchema[Target, State] { } } -trait ProcessSchemaAndValueWithoutState[Target] extends ProcessValueWithSchema[Target, Unit] { +/** 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 @@ -1301,70 +1354,78 @@ trait ProcessSchemaAndValueWithoutState[Target] extends ProcessValueWithSchema[T protected def fail(message: String): Target - override protected def processPrimitive(state: Unit, value: Any, typ: StandardType[Any]): Target = + override protected def processPrimitive(context: Unit, value: Any, typ: StandardType[Any]): Target = processPrimitive(value, typ) - override protected def processRecord(state: Unit, schema: Schema.Record[_], value: ListMap[String, Target]): Target = + override protected def processRecord( + context: Unit, + schema: Schema.Record[_], + value: ListMap[String, Target] + ): Target = processRecord(schema, value) - override protected def processEnum(state: Unit, schema: Schema.Enum[_], tuple: (String, Target)): Target = + override protected def processEnum(context: Unit, schema: Schema.Enum[_], tuple: (String, Target)): Target = processEnum(schema, tuple) - override protected def processSequence(state: Unit, schema: Schema.Sequence[_, _, _], value: Chunk[Target]): Target = + override protected def processSequence( + context: Unit, + schema: Schema.Sequence[_, _, _], + value: Chunk[Target] + ): Target = processSequence(schema, value) override protected def processDictionary( - state: Unit, + context: Unit, schema: Schema.Map[_, _], value: Chunk[(Target, Target)] ): Target = processDictionary(schema, value) - override protected def processSet(state: Unit, schema: Schema.Set[_], value: Set[Target]): Target = + override protected def processSet(context: Unit, schema: Schema.Set[_], value: Set[Target]): Target = processSet(schema, value) override protected def processEither( - state: Unit, + context: Unit, schema: Schema.Either[_, _], value: Either[Target, Target] ): Target = processEither(schema, value) - override protected def processOption(state: Unit, schema: Schema.Optional[_], value: Option[Target]): Target = + override protected def processOption(context: Unit, schema: Schema.Optional[_], value: Option[Target]): Target = processOption(schema, value) - override protected def processTuple(state: Unit, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target = + override protected def processTuple(context: Unit, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target = processTuple(schema, left, right) - override protected def fail(state: Unit, message: String): Target = + override protected def fail(context: Unit, message: String): Target = fail(message) - override protected def processDynamic(state: Unit, value: DynamicValue): Option[Target] = + override protected def processDynamic(context: Unit, value: DynamicValue): Option[Target] = processDynamic(value) - override protected val initialState: Unit = () + override protected val initialContext: Unit = () - override protected def stateForRecordField(state: Unit, index: Int, field: Schema.Field[_, _]): Unit = + override protected def contextForRecordField(context: Unit, index: Int, field: Schema.Field[_, _]): Unit = () - override protected def stateForEnumConstructor(state: Unit, index: Int, c: Schema.Case[_, _]): Unit = + override protected def contextForEnumConstructor(context: Unit, index: Int, c: Schema.Case[_, _]): Unit = () - override protected def stateForEither(state: Unit, e: Either[Unit, Unit]): Unit = + override protected def contextForEither(context: Unit, e: Either[Unit, Unit]): Unit = () - override protected def stateForOption(state: Unit, o: Option[Unit]): Unit = + override protected def contextForOption(context: Unit, o: Option[Unit]): Unit = () - override protected def stateForTuple(state: Unit, index: Int): Unit = + override protected def contextForTuple(context: Unit, index: Int): Unit = () - override protected def stateForSequence(state: Unit, schema: Schema.Sequence[_, _, _], index: Int): Unit = + override protected def contextForSequence(context: Unit, schema: Schema.Sequence[_, _, _], index: Int): Unit = () - override protected def stateForMap(state: Unit, schema: Schema.Map[_, _], index: Int): Unit = + override protected def contextForMap(context: Unit, schema: Schema.Map[_, _], index: Int): Unit = () - override protected def stateForSet(state: Unit, schema: Schema.Set[_], index: Int): Unit = + override protected def contextForSet(context: Unit, schema: Schema.Set[_], index: Int): Unit = () } From 4294bdaee24b18b2433bae6dabdbcbe1a1d90845 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Sun, 6 Nov 2022 19:20:53 +0100 Subject: [PATCH 17/18] scalafix --- .../scala/zio/schema/codec/ProtobufCodec.scala | 2 ++ .../scala/zio/schema/codec/ThriftCodec.scala | 16 +++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) 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 06bdd0e4f..cd237cccc 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 @@ -4,8 +4,10 @@ import java.nio.charset.StandardCharsets import java.nio.{ ByteBuffer, ByteOrder } import java.time._ import java.util.UUID + import scala.collection.immutable.ListMap import scala.util.control.{ NoStackTrace, NonFatal } + import zio.schema.MutableSchemaBasedValueBuilder.CreateValueFromSchemaError import zio.schema._ import zio.schema.codec.BinaryCodec.{ BinaryDecoder, BinaryEncoder, BinaryStreamDecoder, BinaryStreamEncoder } 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 363e2165a..9a0b34e10 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,6 +1,15 @@ package zio.schema.codec +import java.nio.ByteBuffer +import java.time._ +import java.util.UUID + +import scala.annotation.{ nowarn, tailrec } +import scala.collection.immutable.ListMap +import scala.util.control.NonFatal + import org.apache.thrift.protocol._ + import zio.schema.MutableSchemaBasedValueBuilder.CreateValueFromSchemaError import zio.schema._ import zio.schema.annotation.optionalField @@ -8,13 +17,6 @@ import zio.schema.codec.BinaryCodec.{ BinaryDecoder, BinaryEncoder, BinaryStream import zio.stream.ZPipeline import zio.{ Chunk, Unsafe, ZIO } -import java.nio.ByteBuffer -import java.time._ -import java.util.UUID -import scala.annotation.{ nowarn, tailrec } -import scala.collection.immutable.ListMap -import scala.util.control.NonFatal - object ThriftCodec extends BinaryCodec { override def encoderFor[A](schema: Schema[A]): BinaryEncoder[A] = From deff96fc4aa55382814c628dac2a99df811b3402 Mon Sep 17 00:00:00 2001 From: Daniel Vigovszky Date: Mon, 7 Nov 2022 09:04:40 +0100 Subject: [PATCH 18/18] Format --- .../src/main/scala/zio/schema/codec/ThriftCodec.scala | 8 ++++---- .../scala/zio/schema/MutableSchemaBasedValueBuilder.scala | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) 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 b4aaaf5f1..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 @@ -4,7 +4,7 @@ import java.nio.ByteBuffer import java.time._ import java.util.UUID -import scala.annotation.{nowarn, tailrec} +import scala.annotation.{ nowarn, tailrec } import scala.collection.immutable.ListMap import scala.util.control.NonFatal @@ -13,10 +13,10 @@ import org.apache.thrift.protocol._ import zio.schema.MutableSchemaBasedValueBuilder.CreateValueFromSchemaError import zio.schema._ import zio.schema.annotation.optionalField -import zio.schema.codec.BinaryCodec.{BinaryDecoder, BinaryEncoder, BinaryStreamDecoder, BinaryStreamEncoder} -import zio.schema.codec.DecodeError.{EmptyContent, MalformedFieldWithPath, ReadError, ReadErrorWithPath} +import zio.schema.codec.BinaryCodec.{ BinaryDecoder, BinaryEncoder, BinaryStreamDecoder, BinaryStreamEncoder } +import zio.schema.codec.DecodeError.{ EmptyContent, MalformedFieldWithPath, ReadError, ReadErrorWithPath } import zio.stream.ZPipeline -import zio.{Cause, Chunk, Unsafe, ZIO} +import zio.{ Cause, Chunk, Unsafe, ZIO } object ThriftCodec extends BinaryCodec { diff --git a/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueBuilder.scala b/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueBuilder.scala index ce4226896..a7ac2d11e 100644 --- a/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueBuilder.scala +++ b/zio-schema/shared/src/main/scala/zio/schema/MutableSchemaBasedValueBuilder.scala @@ -1149,7 +1149,12 @@ trait SimpleMutableSchemaBasedValueBuilder[Target] extends MutableSchemaBasedVal protected def createDynamic(): Option[Target] - override protected def transform(context: Unit, value: Target, f: Any => Either[String, Any], schema: Schema[_]): 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