zio.http.codec.internal.Mechanic.scala Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2021 - 2023 Sporta Technologies PVT LTD & the ZIO HTTP contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package zio.http.codec.internal
import zio.stacktracer.TracingImplicits.disableAutoTrace
import zio.http.codec.HttpCodec._
import zio.http.codec.{HttpCodec, HttpCodecError}
private[http] object Mechanic {
type InputsBuilder = Atomized[Array[Any]]
type Constructor[+A] = InputsBuilder => A
type Deconstructor[-A] = A => InputsBuilder
private def indexed[R, A](api: HttpCodec[R, A]): HttpCodec[R, A] =
indexedImpl(api, Atomized(0))._1
private def indexedImpl[R, A](api: HttpCodec[R, A], indices: Atomized[Int]): (HttpCodec[R, A], Atomized[Int]) =
api.asInstanceOf[HttpCodec[_, _]] match {
case Combine(left, right, inputCombiner) =>
val (left2, leftIndices) = indexedImpl(left, indices)
val (right2, rightIndices) = indexedImpl(right, leftIndices)
(Combine(left2, right2, inputCombiner).asInstanceOf[HttpCodec[R, A]], rightIndices)
case atom: Atom[_, _] =>
(atom.index(indices.get(atom.tag)).asInstanceOf[HttpCodec[R, A]], indices.update(atom.tag)(_ + 1))
case TransformOrFail(api, f, g) =>
val (api2, resultIndices) = indexedImpl(api, indices)
(TransformOrFail(api2, f, g).asInstanceOf[HttpCodec[R, A]], resultIndices)
case Annotated(api, _) => indexedImpl(api.asInstanceOf[HttpCodec[R, A]], indices)
case Empty => (Empty.asInstanceOf[HttpCodec[R, A]], indices)
case Halt => (Halt.asInstanceOf[HttpCodec[R, A]], indices)
case Fallback(_, _, _, _) => throw new UnsupportedOperationException("Cannot handle fallback at this level")
}
def makeConstructor[R, A](
api: HttpCodec[R, A],
): Constructor[A] =
makeConstructorLoop(indexed(api))
def makeDeconstructor[R, A](
api: HttpCodec[R, A],
): Deconstructor[A] = {
val flattened = AtomizedCodecs.flatten(api)
val deconstructor = makeDeconstructorLoop(indexed(api))
(a: A) => {
val inputsBuilder = flattened.makeInputsBuilder()
deconstructor(a, inputsBuilder)
inputsBuilder
}
}
private def makeConstructorLoop[A](
api: HttpCodec[Nothing, A],
): Constructor[A] = {
def coerce(any: Any): A = any.asInstanceOf[A]
api match {
case Combine(left, right, inputCombiner) =>
val leftThread = makeConstructorLoop(left)
val rightThread = makeConstructorLoop(right)
results => {
val leftValue = leftThread(results)
val rightValue = rightThread(results)
inputCombiner.combine(leftValue, rightValue)
}
case atom: Atom[_, _] =>
results => coerce(results.get(atom.tag)(atom.index))
case transform: TransformOrFail[_, _, A] =>
val threaded = makeConstructorLoop(transform.api)
results =>
transform.f(threaded(results)) match {
case Left(value) => throw HttpCodecError.CustomError("SchemaTransformationFailure", value)
case Right(value) => value
}
case Annotated(api, _) => makeConstructorLoop(api)
case Empty =>
_ => coerce(())
case Halt => throw HaltException
case Fallback(_, _, _, _) => throw new UnsupportedOperationException("Cannot handle fallback at this level")
}
}
private def makeDeconstructorLoop[R, A](
api: HttpCodec[R, A],
): (A, InputsBuilder) => Unit = {
api match {
case Combine(left, right, inputCombiner) =>
val leftDeconstructor = makeDeconstructorLoop(left)
val rightDeconstructor = makeDeconstructorLoop(right)
(input, inputsBuilder) => {
val (left, right) = inputCombiner.separate(input)
leftDeconstructor(left, inputsBuilder)
rightDeconstructor(right, inputsBuilder)
}
case atom: Atom[_, _] =>
(input, inputsBuilder) =>
val array = inputsBuilder.get(atom.tag)
array(atom.index) = input
case transform: TransformOrFail[_, _, A] =>
val deconstructor = makeDeconstructorLoop(transform.api)
(input, inputsBuilder) =>
deconstructor(
transform.g(input) match {
case Left(value) => throw HttpCodecError.CustomError("SchemaTransformationFailure", value)
case Right(value) => value
},
inputsBuilder,
)
case Annotated(api, _) => makeDeconstructorLoop(api)
case Empty => (_, _) => ()
case Halt => (_, _) => ()
case Fallback(_, _, _, _) => throw new UnsupportedOperationException("Cannot handle fallback at this level")
}
}
}