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

io.circe.Derivation.scala Maven / Gradle / Ivy

The newest version!
package io.circe

import cats.data.{NonEmptyList, Validated}
import scala.deriving.{ArrayProduct, Mirror}
import scala.collection.mutable.WrappedArray
import scala.compiletime.{constValue, erasedValue, error, summonFrom}

object Derivation {

  inline final def summonLabels[T <: Tuple]: Array[String] = summonLabelsRec[T].toArray
  inline final def summonDecoders[T <: Tuple]: Array[Decoder[_]] = summonDecodersRec[T].toArray
  inline final def summonEncoders[T <: Tuple]: Array[Encoder[_]] = summonEncodersRec[T].toArray

  inline final def summonEncoder[A]: Encoder[A] = summonFrom {
    case encodeA: Encoder[A] => encodeA
    case _: Mirror.Of[A] => Encoder.AsObject.derived[A]
  }

  inline final def summonDecoder[A]: Decoder[A] = summonFrom {
    case decodeA: Decoder[A] => decodeA
    case _: Mirror.Of[A] => Decoder.derived[A]
  }

  inline final def summonLabelsRec[T <: Tuple]: List[String] = inline erasedValue[T] match {
    case _: Unit => Nil
    case _: (t *: ts) => constValue[t].asInstanceOf[String] :: summonLabelsRec[ts]
  }

  inline final def summonDecodersRec[T <: Tuple]: List[Decoder[_]] =
    inline erasedValue[T] match {
      case _: Unit => Nil
      case _: (t *: ts) => summonDecoder[t] :: summonDecodersRec[ts]
    }

  inline final def summonEncodersRec[T <: Tuple]: List[Encoder[_]] =
    inline erasedValue[T] match {
      case _: Unit => Nil
      case _: (t *: ts) => summonEncoder[t] :: summonEncodersRec[ts]
    }
}

private[circe] trait EncoderDerivation {
  inline final def derived[A](using inline A: Mirror.Of[A]): Encoder.AsObject[A] =
    new DerivedEncoder[A]
        with DerivedInstance[A](
          constValue[A.MirroredLabel],
          Derivation.summonLabels[A.MirroredElemLabels]
        ) {
      protected[this] lazy val elemEncoders: Array[Encoder[_]] =
        Derivation.summonEncoders[A.MirroredElemTypes]

      final def encodeObject(a: A): JsonObject = inline A match {
        case m: Mirror.ProductOf[A] =>
          JsonObject.fromIterable(encodedIterable(a.asInstanceOf[Product]))
        case m: Mirror.SumOf[A] => encodeWith(m.ordinal(a))(a) match {
          case (k, v) => JsonObject.singleton(k, v)
        }
      }
    }
}

private[circe] trait DecoderDerivation {
  inline final def derived[A](using inline A: Mirror.Of[A]): Decoder[A] =
    new DerivedDecoder[A]
        with DerivedInstance[A](
          constValue[A.MirroredLabel],
          Derivation.summonLabels[A.MirroredElemLabels]
        ) {
      protected[this] lazy val elemDecoders: Array[Decoder[_]] =
        Derivation.summonDecoders[A.MirroredElemTypes]

      final def apply(c: HCursor): Decoder.Result[A] = inline A match {
        case m: Mirror.ProductOf[A] =>
          if (c.value.isObject) {
            val iter = resultIterator(c)
            val res = new Array[AnyRef](elemCount)
            var failed: Left[DecodingFailure, _] = null
            var i: Int = 0

            while (iter.hasNext && (failed eq null)) {
              iter.next match {
                case Right(value) => res(i) = value
                case l @ Left(_) => failed = l
              }
              i += 1
            }

            if (failed eq null) {
              Right(m.fromProduct(new ArrayProduct(res)))
            } else {
              failed.asInstanceOf[Decoder.Result[A]]
            }
          } else Left(DecodingFailure(name, c.history))
        case m: Mirror.SumOf[A] =>
          extractIndexFromWrapper(c) match {
            case -1 => Left(DecodingFailure(name, c.history))
            case index => decodeWith(index)(c).asInstanceOf[Decoder.Result[A]]
          }
      }
      final override def decodeAccumulating(c: HCursor): Decoder.AccumulatingResult[A] =
        inline A match {
          case m: Mirror.ProductOf[A] =>
            if (c.value.isObject) {
              val iter = resultAccumulatingIterator(c)
              val res = new Array[AnyRef](elemCount)
              val failed = List.newBuilder[DecodingFailure]
              var i: Int = 0

              while (iter.hasNext) {
                iter.next match {
                  case Validated.Valid(value) => res(i) = value
                  case Validated.Invalid(failures) => failed ++= failures.toList
                }
                i += 1
              }

              val failures = failed.result()
              if (failures.isEmpty) {
                Validated.valid(m.fromProduct(new ArrayProduct(res)))
              } else {
                Validated.invalid(NonEmptyList.fromListUnsafe(failures))
              }
            } else Validated.invalidNel(DecodingFailure(name, c.history))
          case m: Mirror.SumOf[A] =>
            extractIndexFromWrapper(c) match {
              case -1 => Validated.invalidNel(DecodingFailure(name, c.history))
              case index => decodeAccumulatingWith(index)(c).asInstanceOf[Decoder.AccumulatingResult[A]]
            }
        }
  }
}

private[circe] trait CodecDerivation {
  inline final def derived[A](using inline A: Mirror.Of[A]): Codec.AsObject[A] =
    new Codec.AsObject[A]
        with DerivedDecoder[A]
        with DerivedEncoder[A]
        with DerivedInstance[A](
          constValue[A.MirroredLabel],
          Derivation.summonLabels[A.MirroredElemLabels]
        ) {
      protected[this] lazy val elemDecoders: Array[Decoder[_]] =
        Derivation.summonDecoders[A.MirroredElemTypes]
      protected[this] lazy val elemEncoders: Array[Encoder[_]] =
        Derivation.summonEncoders[A.MirroredElemTypes]

      final def encodeObject(a: A): JsonObject = inline A match {
        case m: Mirror.ProductOf[A] =>
          JsonObject.fromIterable(encodedIterable(a.asInstanceOf[Product]))
        case m: Mirror.SumOf[A] => encodeWith(m.ordinal(a))(a) match {
          case (k, v) => JsonObject.singleton(k, v)
        }
      }

      final def apply(c: HCursor): Decoder.Result[A] = inline A match {
        case m: Mirror.ProductOf[A] =>
          if (c.value.isObject) {
            val iter = resultIterator(c)
            val res = new Array[AnyRef](elemCount)
            var failed: Left[DecodingFailure, _] = null
            var i: Int = 0

            while (iter.hasNext && (failed eq null)) {
              iter.next match {
                case Right(value) => res(i) = value
                case l @ Left(_) => failed = l
              }
              i += 1
            }

            if (failed eq null) {
              Right(m.fromProduct(new ArrayProduct(res)))
            } else {
              failed.asInstanceOf[Decoder.Result[A]]
            }
          } else Left(DecodingFailure(name, c.history))
        case m: Mirror.SumOf[A] =>
          extractIndexFromWrapper(c) match {
            case -1 => Left(DecodingFailure(name, c.history))
            case index => decodeWith(index)(c).asInstanceOf[Decoder.Result[A]]
          }
      }
      final override def decodeAccumulating(c: HCursor): Decoder.AccumulatingResult[A] =
        inline A match {
          case m: Mirror.ProductOf[A] =>
            if (c.value.isObject) {
              val iter = resultAccumulatingIterator(c)
              val res = new Array[AnyRef](elemCount)
              val failed = List.newBuilder[DecodingFailure]
              var i: Int = 0

              while (iter.hasNext) {
                iter.next match {
                  case Validated.Valid(value) => res(i) = value
                  case Validated.Invalid(failures) => failed ++= failures.toList
                }
                i += 1
              }

              val failures = failed.result()
              if (failures.isEmpty) {
                Validated.valid(m.fromProduct(new ArrayProduct(res)))
              } else {
                Validated.invalid(NonEmptyList.fromListUnsafe(failures))
              }
            } else Validated.invalidNel(DecodingFailure(name, c.history))
          case m: Mirror.SumOf[A] =>
            extractIndexFromWrapper(c) match {
              case -1 => Validated.invalidNel(DecodingFailure(name, c.history))
              case index => decodeAccumulatingWith(index)(c).asInstanceOf[Decoder.AccumulatingResult[A]]
            }
        }
  }
}

private[circe] trait DerivedInstance[A](
  final val name: String,
  protected[this] final val elemLabels: Array[String]
) {
  final def elemCount: Int = elemLabels.length
  protected[this] final def findLabel(name: String): Int = {
    var i = 0
    while (i < elemCount) {
      if (elemLabels(i) == name) return i
      i += 1
    }
    return -1
  }
}

private[circe] trait DerivedEncoder[A] extends DerivedInstance[A] with Encoder.AsObject[A] {
  protected[this] def elemEncoders: Array[Encoder[_]]

  final def encodeWith(index: Int)(value: Any): (String, Json) =
    (elemLabels(index), elemEncoders(index).asInstanceOf[Encoder[Any]].apply(value))

  final def encodedIterable(value: Product): Iterable[(String, Json)] =
    new Iterable[(String, Json)] {
      def iterator: Iterator[(String, Json)] = new Iterator[(String, Json)] {
        private[this] val elems: Iterator[Any] = value.productIterator
        private[this] var index: Int = 0
        def hasNext: Boolean = elems.hasNext
        def next(): (String, Json) = {
          val field = encodeWith(index)(elems.next())
          index += 1
          field
        }
      }
    }
}

private[circe] trait DerivedDecoder[A] extends DerivedInstance[A] with Decoder[A] {
  protected[this] def elemDecoders: Array[Decoder[_]]

  final def decodeWith(index: Int)(c: HCursor): Decoder.Result[AnyRef] =
    elemDecoders(index).asInstanceOf[Decoder[AnyRef]].tryDecode(c.downField(elemLabels(index)))

  final def decodeAccumulatingWith(index: Int)(c: HCursor): Decoder.AccumulatingResult[AnyRef] =
    elemDecoders(index).asInstanceOf[Decoder[AnyRef]].tryDecodeAccumulating(c.downField(elemLabels(index)))

  final def resultIterator(c: HCursor): Iterator[Decoder.Result[AnyRef]] =
    new Iterator[Decoder.Result[AnyRef]] {
      private[this] var i: Int = 0
      def hasNext: Boolean = i < elemCount
      def next: Decoder.Result[AnyRef] = {
        val result = decodeWith(i)(c)
        i += 1
        result
      }
    }

  final def resultAccumulatingIterator(c: HCursor): Iterator[Decoder.AccumulatingResult[AnyRef]] =
    new Iterator[Decoder.AccumulatingResult[AnyRef]] {
      private[this] var i: Int = 0
      def hasNext: Boolean = i < elemCount
      def next: Decoder.AccumulatingResult[AnyRef] = {
        val result = decodeAccumulatingWith(i)(c)
        i += 1
        result
      }
    }

  final def extractIndexFromWrapper(c: HCursor): Int = c.keys match {
    case Some(keys) =>
      val iter = keys.iterator
      if (iter.hasNext) {
        val key = iter.next
        if (iter.hasNext) {
          -1
        } else {
          findLabel(key)
        }
      } else {
        -1
      }
    case None => -1
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy