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

zio.internal.macros.LayerMacroUtils.scala Maven / Gradle / Ivy

There is a newer version: 2.1.16
Show newest version
package zio.internal.macros

import zio._
import scala.quoted._
import scala.compiletime._
import zio.internal.ansi.AnsiStringOps
import zio.internal.macros.StringUtils.StringOps
import zio.internal.stacktracer.Tracer

private[zio] object LayerMacroUtils {
  type LayerExpr[E] = Expr[ZLayer[_, E, _]]

  def composeLayer[R1, E, O1, O2](lhs: ZLayer[R1, E, O1], rhs: ZLayer[O1, E, O2])(using Trace): ZLayer[R1, E, O2] =
    lhs.to(rhs)

  def constructLayer[R0: Type, R: Type, E: Type](using Quotes)(
    layers: Seq[LayerExpr[E]],
    provideMethod: ProvideMethod
  ): Expr[ZLayer[R0, E, R]] = {
    import quotes.reflect._

    def renderExpr[A](expr: Expr[A]): String =
      scala.util.Try(expr.asTerm.pos.sourceCode).toOption.flatten.getOrElse(expr.show)

    def getNode(layer: LayerExpr[E]): Node[TypeRepr, LayerExpr[E]] = layer match {
      case '{ $layer: ZLayer[in, e, out] } =>
        val inputs  = getRequirements[in]
        val outputs = getRequirements[out]
        Node(inputs, outputs, layer)
    }

    def getRequirements[T: Type]: List[TypeRepr] = {
      def loop(tpe: TypeRepr): List[TypeRepr] =
        tpe.dealias.simplified match {
          case AndType(lhs, rhs)                          => loop(lhs) ++ loop(rhs)
          case AppliedType(_, TypeBounds(_, _) :: _)      => Nil
          case other if other =:= TypeRepr.of[Any]        => Nil
          case other if other.dealias.simplified != other => loop(other)
          case other                                      => List(other.dealias)
        }

      loop(TypeRepr.of[T])
    }

    val targetTypes    = getRequirements[R]
    val remainderTypes = getRequirements[R0]
    val layerToDebug: PartialFunction[LayerExpr[E], ZLayer.Debug] = {
      case '{ ZLayer.Debug.tree }    => ZLayer.Debug.Tree
      case '{ ZLayer.Debug.mermaid } => ZLayer.Debug.Mermaid
    }

    '{
      val trace: Trace = Tracer.newTrace

      ${
        def typeToNode(tpe: TypeRepr): Node[TypeRepr, LayerExpr[E]] =
          Node(Nil, List(tpe), tpe.asType match { case '[t] => '{ ZLayer.environment[t](trace) } })

        def composeH(lhs: LayerExpr[E], rhs: LayerExpr[E]): LayerExpr[E] =
          lhs match {
            case '{ $lhs: ZLayer[i, E, o] } =>
              rhs match {
                case '{ $rhs: ZLayer[i2, E, o2] } =>
                  val tag = Expr
                    .summon[EnvironmentTag[o2]]
                    .getOrElse(
                      report.errorAndAbort(s"Cannot find EnvironmentTag[${TypeRepr.of[o2].show}] in implicit scope")
                    )
                  '{ $lhs.++($rhs)($tag) }
              }
          }

        def composeV(lhs: LayerExpr[E], rhs: LayerExpr[E]): LayerExpr[E] =
          lhs match {
            case '{ $lhs: ZLayer[i, E, o] } =>
              rhs match {
                case '{ $rhs: ZLayer[`o`, E, o2] } =>
                  '{ composeLayer($lhs, $rhs)(using trace) }
              }
          }

        def buildFinalTree(tree: LayerTree[LayerExpr[E]]): LayerExpr[E] = {
          val layerExprs = tree.toList
          ValDef
            .let(Symbol.spliceOwner, layerExprs.map(_.asTerm)) { idents =>
              val exprMap = layerExprs.zip(idents.map(_.asExprOf[ZLayer[_, E, _]])).toMap
              tree.fold('{ ZLayer.unit }, exprMap, composeH, composeV).asTerm
            }
            .asExprOf[ZLayer[_, E, _]]
        }

        val builder = LayerBuilder[TypeRepr, LayerExpr[E]](
          target0 = targetTypes,
          remainder = remainderTypes,
          providedLayers0 = layers.toList,
          layerToDebug = layerToDebug,
          typeEquals = _ <:< _,
          sideEffectType = TypeRepr.of[Unit],
          anyType = TypeRepr.of[Any],
          foldTree = buildFinalTree,
          method = provideMethod,
          exprToNode = getNode,
          typeToNode = typeToNode,
          showExpr = renderExpr,
          showType = _.show,
          reportWarn = report.warning,
          reportError = report.errorAndAbort
        )

        builder.build.asTerm.asExprOf[ZLayer[R0, E, R]]
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy