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

io.scalaland.chimney.internal.compiletime.datatypes.ValueClassesPlatform.scala Maven / Gradle / Ivy

package io.scalaland.chimney.internal.compiletime.datatypes

import io.scalaland.chimney.internal.compiletime.DefinitionsPlatform

trait ValueClassesPlatform extends ValueClasses { this: DefinitionsPlatform =>

  import quotes.*, quotes.reflect.*
  import Type.platformSpecific.*

  protected object WrapperClassType extends WrapperClassTypeModule {

    def parse[A: Type]: Option[Existential[WrapperClass[A, *]]] = {
      val A: TypeRepr = TypeRepr.of[A]
      val sym: Symbol = A.typeSymbol

      val getterOpt: Option[Symbol] = sym.declarations.filter(isPublic).headOption
      val primaryConstructorOpt: Option[Symbol] =
        Option(sym.primaryConstructor).filterNot(_.isNoSymbol).filter(_.isClassConstructor).filter(isPublic)
      val argumentOpt: Option[Symbol] = primaryConstructorOpt.flatMap { primaryConstructor =>
        paramListsOf(A, primaryConstructor).flatten match {
          case argument :: Nil => Some(argument)
          case _               => None
        }
      }

      (getterOpt, primaryConstructorOpt, argumentOpt) match {
        case (Some(getter), Some(primaryConstructor), Some(argument))
            if !Type[A].isPrimitive && getter.name == argument.name =>
          val Argument = fromUntyped[Any](paramsWithTypes(A, primaryConstructor, isConstructor = true)(argument.name))
          val inner = returnTypeOf[Any](A, getter).as_??
          import inner.Underlying as Inner
          assert(
            Argument <:< Inner,
            s"Wrapper/AnyVal ${Type.prettyPrint[A]} only property's type (${Type
                .prettyPrint(Argument)}) was expected to be the same as only constructor argument's type (${Type
                .prettyPrint(Inner)})"
          )
          Some(
            Existential[WrapperClass[A, *], Inner](
              WrapperClass[A, Inner](
                fieldName = getter.name,
                unwrap = (expr: Expr[A]) => expr.asTerm.select(getter).appliedToArgss(Nil).asExprOf[Inner],
                wrap = (expr: Expr[Inner]) => {
                  val select = New(TypeTree.of[A]).select(primaryConstructor)
                  val tree = if A.typeArgs.nonEmpty then select.appliedToTypes(A.typeArgs) else select
                  tree.appliedToArgss(List(List(expr.asTerm))).asExprOf[A]
                }
              )
            )
          )
        case _ => None
      }
    }

    private def isPublic(sym: Symbol): Boolean =
      !sym.isNoSymbol &&
        (!(sym.flags.is(Flags.Private) || sym.flags.is(Flags.PrivateLocal) || sym.flags.is(Flags.Protected)))
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy