All Downloads are FREE. Search and download functionalities are using the official Maven repository.

zio.schema.MutableSchemaBasedValueBuilder.scala Maven / Gradle / Ivy

package zio.schema

import scala.util.control.NonFatal

import zio.prelude.NonEmptyMap
import zio.schema.MutableSchemaBasedValueBuilder.{ CreateValueFromSchemaError, ReadingFieldResult }
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 result is either Finished 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.
   * A third option is to just update the context without reading any field - this can be used to skip data,
   * for example when reading a newer format of a record that has more fields than the older one.
   *
   * 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): ReadingFieldResult[Context]

  /** 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,
    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 fallback value with the given Fallback schema. Similarly to optional values,
   * this method is responsible for gathering enough information to decide whether the created value will
   * be a Left, a Right or a Both. The result value represents this, and for each case allows specifying a context
   * that will be used to create the inner value. */
  protected def startCreatingFallback(context: Context, schema: Schema.Fallback[_, _]): Fallback[Context, Context]

  /** Create the fallback value from an inner value */
  protected def createFallback(context: Context, schema: Schema.Fallback[_, _], value: Fallback[Target, Target]): Target

  /** Called for reading right element of a `Fallback.Both` */
  protected def startReadingRightFallback(context: Context, schema: Schema.Fallback[_, _]): Context

  /** The next value to be created is a tuple with the given schema. The returned context is used to
   * construct the first element of the tuple. */
  protected def startCreatingTuple(context: Context, schema: Schema.Tuple2[_, _]): Context

  /** Called after finished constructing the first element, before constructing the second. The returned
   * context is used to construct the second value.
   */
  protected def startReadingSecondTupleElement(context: Context, schema: Schema.Tuple2[_, _]): Context

  /** Creates the tuple from the constructed first and second values */
  protected def createTuple(context: Context, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target

  /** Creates a Dynamic value. If the returned value is None, it indicates that the builder does
   * not have any built-in support for Dynamic values, and it will be built using Dynamic's schema. */
  protected def createDynamic(context: Context): Option[Target]

  /** Transforms a value with the given function that can fail. Making this customizable allows encoding the failure
   * in Target.
   */
  protected def transform(context: Context, value: Target, f: Any => Either[String, Any], schema: Schema[_]): Target

  /** Fail the builder with the given message */
  protected def fail(context: Context, message: String): Target

  /** The initial (top-level) context value */
  protected val initialContext: Context

  /** Create a value of type A with the provided schema using this builder */
  def create[A](schema: Schema[A]): Target = {
    var currentSchema: Schema[_]    = schema
    var result: Option[Target]      = None
    var stack: List[Target => Unit] = List.empty[Target => Unit]
    var contextStack: List[Context] = List(initialContext)

    def finishWith(resultValue: Target): Unit =
      if (stack.nonEmpty) {
        val head = stack.head
        stack = stack.tail
        head(resultValue)
      } else {
        result = Some(resultValue)
      }

    def push(f: Target => Unit): Unit =
      stack = f :: stack

    def pushContext(s: Context): Unit =
      contextStack = s :: contextStack

    def record(record: Schema.Record[_]): Unit = {
      val values = ChunkBuilder.make[(Int, Target)](record.fields.size)

      def readField(index: Int): Unit = {
        contextStack = contextStack.tail
        startReadingField(contextStack.head, record, index) match {
          case ReadingFieldResult.ReadField(updatedState, idx) =>
            pushContext(updatedState)
            currentSchema = record.fields(idx).schema
            push { field =>
              val elem = (idx, field)
              values += elem
              readField(index + 1)
            }
          case ReadingFieldResult.UpdateContext(updatedState) =>
            pushContext(updatedState)
            readField(index)
          case ReadingFieldResult.Finished() =>
            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, 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 nes @ Schema.NonEmptySequence(elementSchema, _, _, _, _) =>
            val s     = Schema.Sequence(elementSchema, nes.fromChunk, nes.toChunk, nes.annotations, nes.identity)
            val elems = ChunkBuilder.make[Target]()

            def readOne(index: Int): Unit =
              push { elem =>
                elems += elem

                contextStack = contextStack.tail
                val continue = finishedCreatingOneSequenceElement(contextStack.head, 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 nem @ Schema.NonEmptyMap(ks: Schema[k], vs: Schema[v], _) =>
            val s     = Schema.Map(ks, vs, nem.annotations)
            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(
                      NonEmptyMap
                        .fromMapOption(createDictionary(state, s, elems.result()).asInstanceOf[Map[k, v]])
                        .getOrElse(throw new IllegalStateException("NonEmpty map requires at least on element"))
                        .asInstanceOf[Target]
                    )
                  }
                }
              }

            startCreatingDictionary(currentContext, s) match {
              case Some(startingState) =>
                currentSchema = ks
                pushContext(startingState)
                pushContext(startCreatingOneDictionaryElement(startingState, s))
                readOne(0)
              case None =>
                finishWith(
                  NonEmptyMap
                    .fromMapOption(createDictionary(contextStack.head, s, Chunk.empty).asInstanceOf[Map[k, v]])
                    .getOrElse(throw new IllegalStateException("NonEmpty map requires at least on element"))
                    .asInstanceOf[Target]
                )
            }

          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.Fallback[l, r] =>
            startCreatingFallback(currentContext, s) match {
              case Fallback.Left(newState) =>
                currentSchema = s.left
                pushContext(newState)
                push { value =>
                  contextStack = contextStack.tail
                  finishWith(createFallback(contextStack.head, s, Fallback.Left(value)))
                }
              case Fallback.Right(newState) =>
                currentSchema = s.right
                pushContext(newState)
                push { value =>
                  contextStack = contextStack.tail
                  finishWith(createFallback(contextStack.head, s, Fallback.Right(value)))
                }
              case Fallback.Both(leftState, rightState) =>
                currentSchema = s.left
                pushContext(leftState)
                push { left =>
                  contextStack = contextStack.tail
                  currentSchema = s.right
                  pushContext(startReadingRightFallback(rightState, s))
                  push { right =>
                    contextStack = contextStack.tail
                    finishWith(
                      createFallback(
                        contextStack.head,
                        s,
                        if (s.fullDecode) Fallback.Both(left, right)
                        else Fallback.Left(left)
                      )
                    )
                  }
                }
            }

          case s: Schema.Tuple2[a, b] =>
            currentSchema = s.left
            pushContext(startCreatingTuple(currentContext, s))
            push { left =>
              contextStack = contextStack.tail
              val newState = startReadingSecondTupleElement(contextStack.head, s)
              currentSchema = s.right
              pushContext(newState)
              push { right =>
                contextStack = contextStack.tail
                finishWith(createTuple(contextStack.head, s, left, right))
              }
            }

          case s: Schema.Optional[a] =>
            startCreatingOptional(currentContext, s) match {
              case Some(newState) =>
                currentSchema = s.schema
                pushContext(newState)
                push { value =>
                  contextStack = contextStack.tail
                  finishWith(createOptional(contextStack.head, s, Some(value)))
                }
              case None =>
                finishWith(createOptional(contextStack.head, s, None))
            }

          case s @ Schema.Transform(schema, f, _, _, _) =>
            currentSchema = schema
            push { result =>
              finishWith(transform(currentContext, result, f.asInstanceOf[Any => Either[String, Any]], s))
            }

          case s @ Schema.CaseClass0(_, _, _) =>
            record(s)

          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 = DynamicValue.schema
            }
          case _ => throw new Exception(s"Missing a handler for schema ${currentSchema.toString()}.")
        }
      }
    } catch {
      case NonFatal(reason) =>
        throw CreateValueFromSchemaError(contextStack.head, reason)
    }
    result.get
  }
}

object MutableSchemaBasedValueBuilder {
  case class CreateValueFromSchemaError[Context](context: Context, cause: Throwable) extends RuntimeException

  sealed trait ReadingFieldResult[Context]

  object ReadingFieldResult {
    final case class Finished[Context]()                              extends ReadingFieldResult[Context]
    final case class ReadField[Context](context: Context, index: Int) extends ReadingFieldResult[Context]
    final case class UpdateContext[Context](context: Context)         extends ReadingFieldResult[Context]
  }
}

/** 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
  ): ReadingFieldResult[Unit] =
    startReadingField(record, index) match {
      case Some(idx) => ReadingFieldResult.ReadField((), idx)
      case None      => ReadingFieldResult.Finished()
    }
  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 startCreatingFallback(context: Unit, schema: Schema.Fallback[_, _]): Fallback[Unit, Unit] =
    startCreatingFallback(schema)

  protected def startCreatingFallback(schema: Schema.Fallback[_, _]): Fallback[Unit, Unit]

  override protected def startReadingRightFallback(context: Unit, schema: Schema.Fallback[_, _]): Unit =
    startReadingRightFallback(schema)

  protected def startReadingRightFallback(schema: Schema.Fallback[_, _]): Unit

  override protected def createFallback(
    context: Unit,
    schema: Schema.Fallback[_, _],
    value: zio.schema.Fallback[Target, Target]
  ): Target =
    createFallback(schema, value)

  protected def createFallback(schema: Schema.Fallback[_, _], value: zio.schema.Fallback[Target, Target]): Target

  override protected def startCreatingTuple(context: Unit, schema: Schema.Tuple2[_, _]): Unit =
    startCreatingTuple(schema)

  protected def startCreatingTuple(schema: Schema.Tuple2[_, _]): Unit

  override protected def startReadingSecondTupleElement(context: Unit, schema: Schema.Tuple2[_, _]): Unit =
    startReadingSecondTupleElement(schema)

  protected def startReadingSecondTupleElement(schema: Schema.Tuple2[_, _]): Unit

  override protected def createTuple(context: Unit, schema: Schema.Tuple2[_, _], left: Target, right: Target): Target =
    createTuple(schema, left, right)

  protected def createTuple(schema: Schema.Tuple2[_, _], left: Target, right: Target): Target

  override protected def createDynamic(context: Unit): Option[Target] =
    createDynamic()

  protected def createDynamic(): Option[Target]

  override protected def transform(
    context: Unit,
    value: Target,
    f: Any => Either[String, Any],
    schema: Schema[_]
  ): Target =
    transform(value, f, schema)

  protected def transform(value: Target, f: Any => Either[String, Any], schema: Schema[_]): Target

  override protected def fail(context: Unit, message: String): Target =
    fail(message)

  protected def fail(message: String): Target

  override protected val initialContext: Unit = ()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy