Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
tethys.derivation.impl.builder.WriterBuilderUtils.scala Maven / Gradle / Ivy
package tethys.derivation.impl.builder
import scala.annotation.tailrec
import scala.quoted.*
import tethys.derivation.builder.{WriterDerivationConfig, WriterDescription, FieldStyle as ConfigFieldStyle}
import tethys.derivation.impl.{FieldStyle, Reflection}
trait WriterBuilderUtils extends Reflection {
import context.reflect.*
protected lazy val emptyWriterConfig: Expr[WriterDerivationConfig] =
'{ tethys.derivation.builder.WriterDerivationConfig.empty }
case class MacroWriteDescription(
tpe: TypeRepr,
config: Expr[WriterDerivationConfig],
operations: Seq[WriterMacroOperation]
) { self =>
def lift[T: Type]: Expr[WriterDescription[T]] =
'{ WriterDescription[T]($config, ${ Expr.ofSeq(operations.map(_.lift[T])) }) }
}
object MacroWriteDescription {
def empty[T: Type]: MacroWriteDescription =
MacroWriteDescription(
tpe = TypeRepr.of[T],
config = emptyWriterConfig,
operations = Seq()
)
def unlift[T: Type](wd: Expr[WriterDescription[T]]): MacroWriteDescription = {
val withoutInlining = (wd.asTerm.underlying match {
case Inlined(_, _, expansion) => expansion
case notInlined => notInlined
}).asExprOf[WriterDescription[T]]
withoutInlining match {
case '{ WriterDescription.apply[T]($config, ${ Varargs(operations) }) } =>
MacroWriteDescription(TypeRepr.of[T], config, operations.map(WriterMacroOperation.unlift[T]))
}
}
}
sealed trait WriterMacroOperation { self =>
def lift[T: Type]: Expr[WriterDescription.BuilderOperation[T]] =
self match {
case WriterMacroOperation.Remove(tpe, field) =>
tpe.asType match {
case '[tpe] =>
'{
WriterDescription.BuilderOperation.Remove.apply[tpe](
${ Expr(field) }
)
}.asInstanceOf[Expr[WriterDescription.BuilderOperation[T]]]
}
case WriterMacroOperation.Update(tpe, field, name, fun, from, to) => {
(tpe.asType, from.asType, to.asType) match {
case ('[tpe], '[from], '[to]) =>
'{
WriterDescription.BuilderOperation.Update.apply[tpe, from, to](
${ Expr(field) },
$name,
${ fun.asExprOf[Function[from, to]] }
)
}.asInstanceOf[Expr[WriterDescription.BuilderOperation[T]]]
}
}
case WriterMacroOperation.UpdateFromRoot(tpe, field, name, fun, to) =>
(tpe.asType, to.asType) match {
case ('[tpe], '[to]) =>
'{
WriterDescription.BuilderOperation.UpdateFromRoot.apply[tpe, to](
${ Expr(field) },
$name,
${ fun.asExprOf[Function[tpe, to]] }
)
}.asInstanceOf[Expr[WriterDescription.BuilderOperation[T]]]
}
case WriterMacroOperation.UpdatePartial(tpe, field, name, fun, from, to) =>
(tpe.asType, from.asType, to.asType) match {
case ('[tpe], '[from], '[to]) =>
'{
WriterDescription.BuilderOperation.UpdatePartial.apply[tpe, from, to](
${ Expr(field) },
$name,
${ fun.asExprOf[PartialFunction[from, to]] }
)
}.asInstanceOf[Expr[WriterDescription.BuilderOperation[T]]]
}
case WriterMacroOperation.UpdatePartialFromRoot(tpe, field, name, fun, to) =>
(tpe.asType, to.asType) match {
case ('[tpe], '[to]) =>
'{
WriterDescription.BuilderOperation.UpdatePartialFromRoot.apply[tpe, to](
${ Expr(field) },
$name,
${ fun.asExprOf[PartialFunction[tpe, to]] }
)
}.asInstanceOf[Expr[WriterDescription.BuilderOperation[T]]]
}
case WriterMacroOperation.Add(tpe, field, fun, to) =>
(tpe.asType, to.asType) match {
case ('[tpe], '[to]) =>
'{
WriterDescription.BuilderOperation.Add.apply[tpe, to](
$field,
${ fun.asExprOf[Function[tpe, to]] }
)
}.asInstanceOf[Expr[WriterDescription.BuilderOperation[T]]]
}
}
}
object WriterMacroOperation {
case class Remove(tpe: TypeRepr, field: String) extends WriterMacroOperation
case class Update(tpe: TypeRepr, field: String, name: Expr[Option[String]], fun: Term, from: TypeRepr, to: TypeRepr)
extends WriterMacroOperation
case class UpdateFromRoot(tpe: TypeRepr, field: String, name: Expr[Option[String]], fun: Term, to: TypeRepr)
extends WriterMacroOperation
case class UpdatePartial(tpe: TypeRepr, field: String, name: Expr[Option[String]], fun: Term, from: TypeRepr, to: TypeRepr)
extends WriterMacroOperation
case class UpdatePartialFromRoot(tpe: TypeRepr, field: String, name: Expr[Option[String]], fun: Term, to: TypeRepr)
extends WriterMacroOperation
case class Add(tpe: TypeRepr, field: Expr[String], fun: Term, to: TypeRepr) extends WriterMacroOperation
def unlift[T: Type](bo: Expr[WriterDescription.BuilderOperation[T]]): WriterMacroOperation =
bo match {
case '{ WriterDescription.BuilderOperation.Remove.apply[tpe]($field) } =>
WriterMacroOperation.Remove(TypeRepr.of[tpe], field.valueOrAbort)
case '{ WriterDescription.BuilderOperation.Update.apply[tpe, from, to]($field, $name, $fun) } =>
WriterMacroOperation.Update(
TypeRepr.of[tpe],
field.valueOrAbort,
name,
fun.asTerm,
TypeRepr.of[from],
TypeRepr.of[to]
)
case '{ WriterDescription.BuilderOperation.UpdateFromRoot.apply[tpe, to]($field, $name, $fun) } =>
WriterMacroOperation.UpdateFromRoot(TypeRepr.of[tpe], field.valueOrAbort, name, fun.asTerm, TypeRepr.of[to])
case '{ WriterDescription.BuilderOperation.UpdatePartial.apply[tpe, from, to]($field, $name, $fun) } =>
WriterMacroOperation.UpdatePartial(
tpe = TypeRepr.of[tpe],
field = field.valueOrAbort,
name = name,
fun = fun.asTerm,
from = TypeRepr.of[from],
to = TypeRepr.of[to]
)
case '{ WriterDescription.BuilderOperation.UpdatePartialFromRoot.apply[tpe, to]($field, $name, $fun) } =>
WriterMacroOperation.UpdatePartialFromRoot(TypeRepr.of[tpe], field.valueOrAbort, name, fun.asTerm, TypeRepr.of[to])
case '{ WriterDescription.BuilderOperation.Add.apply[tpe, to]($field, $fun) } =>
WriterMacroOperation.Add(TypeRepr.of[tpe], field, fun.asTerm, TypeRepr.of[to])
}
}
protected def evalWriterConfig(configExpr: Expr[WriterDerivationConfig]): (Option[FieldStyle], Option[String]) = {
@tailrec
def parseConfigExpr(
confExpr: Expr[WriterDerivationConfig],
fieldStyleExpr: Option[Expr[ConfigFieldStyle]],
discriminatorExpr: Option[Expr[String]]
): (Option[Expr[ConfigFieldStyle]], Option[Expr[String]]) =
confExpr match {
case '{ WriterDerivationConfig.empty } =>
(fieldStyleExpr, discriminatorExpr)
case '{ WriterDerivationConfig.apply(None, None) } =>
(fieldStyleExpr, discriminatorExpr)
case '{ WriterDerivationConfig.apply(None, Some($discriminator)) } =>
(fieldStyleExpr, discriminatorExpr.orElse(Some(discriminator)))
case '{ WriterDerivationConfig.apply(Some($fieldStyle), None) } =>
(fieldStyleExpr.orElse(Some(fieldStyle)), discriminatorExpr)
case '{ WriterDerivationConfig.apply(Some($fieldStyle), Some($discriminator)) } =>
(fieldStyleExpr.orElse(Some(fieldStyle)), discriminatorExpr.orElse(Some(discriminator)))
case '{ WriterDerivationConfig.withFieldStyle($fieldStyle) } =>
(fieldStyleExpr.orElse(Some(fieldStyle)), discriminatorExpr)
case '{ WriterDerivationConfig.withDiscriminator($discriminator) } =>
(fieldStyleExpr, discriminatorExpr.orElse(Some(discriminator)))
case '{ ($config: WriterDerivationConfig).withFieldStyle($fieldStyle) } =>
parseConfigExpr(config, fieldStyleExpr.orElse(Some(fieldStyle)), discriminatorExpr)
case '{ ($config: WriterDerivationConfig).withDiscriminator($discriminator) } =>
parseConfigExpr(config, fieldStyleExpr, discriminatorExpr.orElse(Some(discriminator)))
case _ => report.errorAndAbort(s"Config parsing error. Unknown expr: ${confExpr.asTerm.show}")
}
val (fieldStyleExpr, discriminatorExpr) = parseConfigExpr(configExpr, None, None)
val fieldStyle: Option[FieldStyle] = fieldStyleExpr.map { fs =>
fs.asTerm.underlying.asExprOf[ConfigFieldStyle] match {
case '{ ConfigFieldStyle.LowerCase } => FieldStyle.lowercase
case '{ ConfigFieldStyle.UpperCase } => FieldStyle.uppercase
case '{ ConfigFieldStyle.Capitalize } => FieldStyle.capitalize
case '{ ConfigFieldStyle.Uncapitalize } => FieldStyle.uncapitalize
case '{ ConfigFieldStyle.KebabCase } => FieldStyle.kebabcase
case '{ ConfigFieldStyle.LowerKebabCase } => FieldStyle.lowerKebabcase
case '{ ConfigFieldStyle.UpperKebabCase } => FieldStyle.upperKebabcase
case '{ ConfigFieldStyle.CapitalizedKebabCase } => FieldStyle.capitalizedKebabCase
case '{ ConfigFieldStyle.UncapitalizedKebabCase } => FieldStyle.uncapitalizedKebabCase
case '{ ConfigFieldStyle.SnakeCase } => FieldStyle.snakecase
case '{ ConfigFieldStyle.LowerSnakeCase } => FieldStyle.lowerSnakecase
case '{ ConfigFieldStyle.UpperSnakeCase } => FieldStyle.upperSnakecase
case '{ ConfigFieldStyle.CapitalizedSnakeCase } => FieldStyle.capitalizedSnakecase
case '{ ConfigFieldStyle.UncapitalizedSnakeCase } => FieldStyle.uncapitalizedSnakecase
case '{ ConfigFieldStyle.lowercase } => FieldStyle.lowercase
case '{ ConfigFieldStyle.uppercase } => FieldStyle.uppercase
case '{ ConfigFieldStyle.kebabcase } => FieldStyle.kebabcase
case '{ ConfigFieldStyle.lowerKebabcase } => FieldStyle.lowerKebabcase
case '{ ConfigFieldStyle.upperKebabcase } => FieldStyle.upperKebabcase
case '{ ConfigFieldStyle.snakecase } => FieldStyle.snakecase
case '{ ConfigFieldStyle.lowerSnakecase } => FieldStyle.lowerSnakecase
case '{ ConfigFieldStyle.upperSnakecase } => FieldStyle.upperSnakecase
case _ => report.errorAndAbort("fieldStyle in writer config can not be computed in compile-time")
}
}
val discriminator = discriminatorExpr.map(
_.asTerm.underlying
.asExprOf[String]
.value
.getOrElse(report.errorAndAbort("discriminator in writer config can not be computed in compile-time"))
)
(fieldStyle, discriminator)
}
}