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

scalaz.macros.DerivingMacros.scala Maven / Gradle / Ivy

// Copyright: 2017 - 2024 Sam Halliday
// License: http://www.gnu.org/licenses/lgpl-3.0.en.html

package scalaz.macros

import scala.Predef._
import scala.reflect.macros.blackbox

final class DerivingMacrosImpl(val c: blackbox.Context) {
  import c.universe._

  def deriving[F[_], A](implicit
    evF: c.WeakTypeTag[F[_]],
    evA: c.WeakTypeTag[A]
  ): Tree = {
    val F   = evF.tpe
    val A   = evA.tpe
    val fqn = F.dealias.typeSymbol.fullName
    readConfig().targets.get(fqn) match {
      case Some(target) => parseTerm(target)
      case None         => q"_root_.scalaz.Deriving.gen[$F, $A]"
    }
  }

  def xderiving[F[_], A](implicit
    evF: c.WeakTypeTag[F[_]],
    evA: c.WeakTypeTag[A]
  ): Tree = {
    val F = evF.tpe
    val A = evA.tpe
    A.decls.collect {
      case m: MethodSymbol if m.isParamAccessor => m.asMethod
    }.toList match {
      case value :: Nil =>
        val hasXmap   = F.decls.find(_.name.toString == "xmap").isDefined
        val access    = value.name
        val U         = value.typeSignatureIn(A).resultType
        def invariant =
          q"""_root_.scalaz.InvariantFunctor[${F.typeSymbol}].xmap(
              _root_.scala.Predef.implicitly[${F.typeSymbol}[$U]],
              (u: $U) => new $A(u),
              (a: $A) => a.$access)"""
        def xmap      =
          q"""_root_.scala.Predef.implicitly[${F.typeSymbol}[$U]].xmap(
              (u: $U) => new $A(u),
              (a: $A) => a.$access)"""
        if (hasXmap) xmap else invariant
      case _            =>
        c.abort(c.enclosingPosition, "only supports classes with one parameter")
    }
  }

  private def readConfig(): DerivingConfig =
    DerivingConfig.targets
      .fold(
        error => {
          c.error(c.prefix.tree.pos, s"Failed to parse deriving config: $error")
          throw new IllegalStateException
        },
        success => DerivingConfig(success)
      )

  private def parseTerm(s: String): Tree = {
    def toSelect(parts: List[TermName]): Tree =
      parts match {
        case Nil          => Ident(termNames.ROOTPKG)
        case head :: tail => Select(toSelect(tail), head)
      }
    toSelect(s.split("[.]").toList.map(TermName(_)).reverse)
  }

}

object DerivingMacros {
  def deriving[F[_], A]: F[A] = macro DerivingMacrosImpl.deriving[F, A]
  def xderiving[F[_], A]: F[A] = macro DerivingMacrosImpl.xderiving[F, A]
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy