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

smithy4s.schema.Alt.scala Maven / Gradle / Ivy

There is a newer version: 0.19.0-41-91762fb
Show newest version
/*
 *  Copyright 2021-2024 Disney Streaming
 *
 *  Licensed under the Tomorrow Open Source Technology License, Version 1.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     https://disneystreaming.github.io/TOST-1.0.txt
 *
 *  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 smithy4s
package schema

import smithy4s.capability.EncoderK
import kinds._

/**
  * Represents a member of coproduct type (sealed trait)
  */
final case class Alt[U, A](
    label: String,
    schema: Schema[A],
    inject: A => U,
    project: PartialFunction[U, A]
) {

  @deprecated("use .schema instead", since = "0.18.0")
  def instance: Schema[A] = schema

  def hints: Hints = schema.hints
  def memberHints: Hints = schema.hints.memberHints

  def addHints(newHints: Hints): Alt[U, A] =
    copy(schema = schema.addMemberHints(newHints))

  def addHints(newHints: Hint*): Alt[U, A] =
    addHints(Hints(newHints: _*))

  def transformHintsLocally(f: Hints => Hints): Alt[U, A] =
    copy(schema = schema.transformHintsLocally(f))

  def transformHintsTransitively(f: Hints => Hints): Alt[U, A] =
    copy(schema = schema.transformHintsTransitively(f))

  def validated[C](c: C)(implicit
      constraint: RefinementProvider.Simple[C, A]
  ): Alt[U, A] =
    copy(schema = schema.validated(c)(constraint))

}
object Alt {

  /**
    * Precompiles an Alt to produce an instance of `G`
    */
  trait Precompiler[G[_]] { self =>
    def apply[A](label: String, schema: Schema[A]): G[A]
    def toPolyFunction[U]: PolyFunction[Alt[U, *], G] =
      new PolyFunction[Alt[U, *], G] {
        def apply[A](fa: Alt[U, A]): G[A] = self.apply(fa.label, fa.schema)
      }
  }

  /**
    * Construct that does the heavily lifting for encoding union values, by
    * memoising the compilation of the alternatives, dispatching the union
    * instance to the correct pre-compiled encoder, and lift the resulting
    * function into an encoder that works on the union.
    */
  trait Dispatcher[U] {

    def compile[G[_], Result](precompile: Precompiler[G])(implicit
        encoderK: EncoderK[G, Result]
    ): G[U]

    def ordinal(u: U): Int

  }

  object Dispatcher {

    def fromUnion[U](union: Schema.UnionSchema[U]): Dispatcher[U] =
      apply(
        alts = union.alternatives,
        ordinal = union.ordinal
      )

    private[smithy4s] def apply[U](
        alts: Vector[Alt[U, _]],
        ordinal: U => Int
    ): Dispatcher[U] = new Impl[U](alts, ordinal)

    private[smithy4s] case class Impl[U](
        alts: Vector[Alt[U, _]],
        ord: U => Int
    ) extends Dispatcher[U] {
      def compile[F[_], Result](precompile: Precompiler[F])(implicit
          encoderK: EncoderK[F, Result]
      ): F[U] = {
        val compiler = precompile.toPolyFunction[U]
        val builder = scala.collection.mutable.ArrayBuffer[Any]()
        alts.foreach { alt =>
          builder += compiler(alt)
        }
        val precompiledAlts = builder.toArray

        encoderK.absorb[U] { u =>
          val ord = ordinal(u)
          encoderK(
            precompiledAlts(ord).asInstanceOf[F[Any]],
            alts(ord).project(u)
          )
        }
      }

      def ordinal(u: U): Int = ord(u)

    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy