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

cats.xml.utils.generic.ParamNameExtractor.scala Maven / Gradle / Ivy

package cats.xml.utils.generic

import scala.annotation.{tailrec, unused}
import scala.reflect.macros.blackbox
import cats.xml.utils.generic.ParamName
import scala.quoted.*

class ParamNameExtractor[T] private () {
  inline def param[U](path: T => U): ParamName[T] =
    ${ ParamNameExtractor.Scala3Macros.extractParamName[T, U]('path) }
}
object ParamNameExtractor {

  def of[T]: ParamNameExtractor[T] = new ParamNameExtractor[T]

  object Scala3Macros {
    def extractParamName[T: Type, U: Type](path: Expr[T => U])(using Quotes): Expr[ParamName[T]] =
      new ParamNameExtractor.Scala3Macros().extractParamNameImpl[T, U](path)
  }
  class Scala3Macros(using q: Quotes) {

    import q.reflect.*
    import ToFromExprInstances.given

    def extractParamNameImpl[T: Type, U: Type](path: Expr[T => U]): Expr[ParamName[T]] = {

      import quotes.reflect.*

      val expectedShapeInfo = "Path must have shape: _.field1.field2.each.field3.(...)"

      enum PathElement {
        case TermPathElement(term: String, xargs: String*) extends PathElement
        case FunctorPathElement(functor: String, method: String, xargs: String*) extends PathElement
      }

      @tailrec
      def toPath(tree: Tree, acc: List[PathElement]): Seq[PathElement] = {
        tree match {
          /** Field access */
          case Select(deep, ident) =>
            toPath(deep, PathElement.TermPathElement(ident) :: acc)

          /** The first segment from path (e.g. `_.age` -> `_`) */
          case i: Ident =>
            acc
          case t =>
            report.errorAndAbort(s"Unsupported path element $t")
        }
      }

      val pathElements: Seq[PathElement] = path.asTerm match {
        /** Single inlined path */
        case Inlined(_, _, Block(List(DefDef(_, _, _, Some(p))), _)) =>
          toPath(p, List.empty)
        case _ =>
          report.errorAndAbort(s"Unsupported path [$path]")
      }

      Expr(
        ParamName[T](
          pathElements
            .map {
              case PathElement.TermPathElement(c, _ @_*)            => c
              case PathElement.FunctorPathElement(_, method, _ @_*) => method
            }
            .mkString(".")
        )
      )
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy