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

sttp.tapir.internal.CaseClassUtil.scala Maven / Gradle / Ivy

The newest version!
package sttp.tapir.internal

import scala.reflect.macros.blackbox

private[tapir] class CaseClassUtil[C <: blackbox.Context, T: C#WeakTypeTag](val c: C, name: String) {
  import c.universe._

  val t: Type = weakTypeOf[T]
  if (!t.typeSymbol.isClass || !t.typeSymbol.asClass.isCaseClass) {
    c.error(c.enclosingPosition, s"${name.capitalize} can only be generated for a case class, but got: ${t.typeSymbol.fullName}.")
  }

  lazy val fields: List[Symbol] = t.decls
    .collectFirst {
      case m: MethodSymbol if m.isPrimaryConstructor => m
    }
    .get
    .paramLists
    .head

  lazy val companion: Ident = Ident(TermName(t.typeSymbol.name.decodedName.toString))

  lazy val instanceFromValues: Tree = if (fields.size == 1) {
    q"$companion.apply(values.head.asInstanceOf[${fields.head.typeSignature}])"
  } else {
    q"($companion.apply _).tupled.asInstanceOf[Any => $t].apply(sttp.tapir.internal.SeqToParams(values))"
  }

  lazy val schema: Tree = c.typecheck(q"_root_.scala.Predef.implicitly[_root_.sttp.tapir.Schema[$t]]")

  lazy val classSymbol: ClassSymbol = t.typeSymbol.asClass
  lazy val className: TermName = classSymbol.asType.name.toTermName

  def annotated(field: Symbol, annotationType: c.Type): Boolean =
    findAnnotation(field, annotationType).isDefined

  def findAnnotation(field: Symbol, annotationType: c.Type): Option[Annotation] =
    field.annotations.find(_.tree.tpe <:< annotationType)

  def extractStringArgFromAnnotation(field: Symbol, annotationType: c.Type): Option[String] = {
    // https://stackoverflow.com/questions/20908671/scala-macros-how-to-read-an-annotation-object
    field.annotations.collectFirst {
      case a if a.tree.tpe <:< annotationType =>
        a.tree.children.tail match {
          case List(Literal(Constant(str: String))) => str
          case _ => throw new IllegalStateException(s"Cannot extract annotation argument from: ${c.universe.showRaw(a.tree)}")
        }
    }
  }

  def extractOptStringArgFromAnnotation(field: Symbol, annotationType: c.Type): Option[Option[String]] =
    field.annotations.collectFirst {
      case a if a.tree.tpe <:< annotationType =>
        a.tree.children.tail match {
          case List(Select(_, name @ TermName(_))) if name.decodedName.toString.startsWith("$default") =>
            None
          case List(Literal(Constant(str: String))) =>
            Some(str)
          case _ => throw new IllegalStateException(s"Cannot extract annotation argument from: ${c.universe.showRaw(a.tree)}")
        }
    }

  def extractTreeFromAnnotation(field: Symbol, annotationType: c.Type): Option[Tree] = {
    // https://stackoverflow.com/questions/20908671/scala-macros-how-to-read-an-annotation-object
    field.annotations.collectFirst {
      case a if a.tree.tpe <:< annotationType =>
        a.tree.children.tail match {
          case List(t) => t
          case _       => throw new IllegalStateException(s"Cannot extract annotation argument from: ${c.universe.showRaw(a.tree)}")
        }
    }
  }

  def extractFirstTreeArgFromAnnotation(field: Symbol, annotationType: c.Type): Option[Tree] = {
    field.annotations.collectFirst {
      case a if a.tree.tpe <:< annotationType =>
        a.tree.children.tail match {
          case List(t, _*) => t
          case _           => throw new IllegalStateException(s"Cannot extract annotation argument from: ${c.universe.showRaw(a.tree)}")
        }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy