diff --git a/chimney/src/main/scala-2/io/scalaland/chimney/dsl/TransformerInto.scala b/chimney/src/main/scala-2/io/scalaland/chimney/dsl/TransformerInto.scala index 1e8be563f..d9e5352f0 100644 --- a/chimney/src/main/scala-2/io/scalaland/chimney/dsl/TransformerInto.scala +++ b/chimney/src/main/scala-2/io/scalaland/chimney/dsl/TransformerInto.scala @@ -205,6 +205,18 @@ final class TransformerInto[From, To, Overrides <: TransformerOverrides, Flags < ): TransformerInto[From, To, ? <: TransformerOverrides, Flags] = macro TransformerIntoMacros.withConstructorImpl[From, To, Overrides, Flags] + /** Require that all fields of the source object except fields mentioned in `selectorFrom` are used in the + * transformation. and fail compilation otherwise. + * + * @param selectorFrom + * exception fields that are not required to be used in the transformation + * @return + */ + def requireSourceFieldsUsedExcept[T, U]( + selectorFrom: From => Any* + ): TransformerInto[From, To, ? <: TransformerOverrides, Flags] = + macro TransformerIntoMacros.requireSourceFieldsUsedExceptImpl[From, To, Overrides, Flags] + /** Apply configured transformation in-place. * * It runs macro that tries to derive instance of `Transformer[From, To]` and immediately apply it to captured diff --git a/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/ChimneyTypesPlatform.scala b/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/ChimneyTypesPlatform.scala index 241cfa616..64e4cd268 100644 --- a/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/ChimneyTypesPlatform.scala +++ b/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/ChimneyTypesPlatform.scala @@ -249,6 +249,17 @@ private[compiletime] trait ChimneyTypesPlatform extends ChimneyTypes { this: Chi ) } } + object RequireSourceFieldsExcept extends RequireSourceFieldsExceptModule { + def apply[ + FromPathList <: runtime.PathList: Type, + Tail <: runtime.TransformerOverrides: Type + ]: Type[runtime.TransformerOverrides.RequireSourceFieldsExcept[FromPathList, Tail]] = + weakTypeTag[runtime.TransformerOverrides.RequireSourceFieldsExcept[FromPathList, Tail]] + def unapply[A](A: Type[A]): Option[(?<[runtime.PathList], ?<[runtime.TransformerOverrides])] = + A.asCtor[runtime.TransformerOverrides.RequireSourceFieldsExcept[?, ?]].map { A0 => + (A0.param_<[runtime.PathList](0), A0.param_<[runtime.TransformerOverrides](1)) + } + } } object TransformerFlags extends TransformerFlagsModule { @@ -406,6 +417,17 @@ private[compiletime] trait ChimneyTypesPlatform extends ChimneyTypes { this: Chi } } + object PathList extends PathListModule { + val Empty: Type[runtime.PathList.Empty] = weakTypeTag[runtime.PathList.Empty] + object List extends ListModule { + def apply[Head <: runtime.Path: Type, Tail <: runtime.PathList: Type]: Type[runtime.PathList.List[Head, Tail]] = + weakTypeTag[runtime.PathList.List[Head, Tail]] + def unapply[A](A: Type[A]): Option[(?<[runtime.Path], ?<[runtime.PathList])] = + A.asCtor[runtime.PathList.List[?, ?]] + .map(A0 => A0.param_<[runtime.Path](0) -> A0.param_<[runtime.PathList](1)) + } + } + object DefaultValue extends DefaultValueModule { def apply[Value: Type]: Type[integrations.DefaultValue[Value]] = weakTypeTag[integrations.DefaultValue[Value]] diff --git a/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/dsl/TransformerIntoMacros.scala b/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/dsl/TransformerIntoMacros.scala index f4b399ee1..7fd656f32 100644 --- a/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/dsl/TransformerIntoMacros.scala +++ b/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/dsl/TransformerIntoMacros.scala @@ -1,7 +1,7 @@ package io.scalaland.chimney.internal.compiletime.dsl import io.scalaland.chimney.dsl.TransformerInto -import io.scalaland.chimney.internal.runtime.{ArgumentLists, Path, TransformerFlags, TransformerOverrides} +import io.scalaland.chimney.internal.runtime.{ArgumentLists, Path, PathList, TransformerFlags, TransformerOverrides} import io.scalaland.chimney.internal.runtime.TransformerOverrides.* import scala.annotation.unused @@ -96,4 +96,17 @@ class TransformerIntoMacros(val c: whitebox.Context) extends utils.DslMacroUtils .addOverride(f) .asInstanceOfExpr[TransformerInto[From, To, Constructor[Args, Path.Root, Overrides], Flags]] }.applyFromBody(f) + + def requireSourceFieldsUsedExceptImpl[ + From: WeakTypeTag, + To: WeakTypeTag, + Overrides <: TransformerOverrides: WeakTypeTag, + Flags <: TransformerFlags: WeakTypeTag + ](selectorFrom: Tree*): Tree = c.prefix.tree + .asInstanceOfExpr( + new ApplyFieldNamesType { + def apply[FromPathList <: PathList: WeakTypeTag]: c.WeakTypeTag[?] = + weakTypeTag[TransformerInto[From, To, RequireSourceFieldsExcept[FromPathList, Overrides], Flags]] + }.applyFromSelector(selectorFrom) + ) } diff --git a/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala b/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala index 46bfd8f1d..5dbe938b8 100644 --- a/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala +++ b/chimney/src/main/scala-2/io/scalaland/chimney/internal/compiletime/dsl/utils/DslMacroUtils.scala @@ -266,6 +266,99 @@ private[chimney] trait DslMacroUtils { s"Expected function, instead got: $MAGENTA$t$RESET: $MAGENTA${t.tpe}$RESET" } + private trait ExistentialPathList { + type Underlying <: runtime.PathList + val Underlying: c.WeakTypeTag[Underlying] + } + private object ExistentialPathList { + def parse(selectors: Seq[Tree]): Either[String, ExistentialPathList] = + selectors + .map(selector => ExistentialPath.parse(selector)) + .foldLeft[Either[String, List[ExistentialPath]]](Right(Nil)) { + case (err @ Left(_), _) => err + case (_, Left(error)) => Left(error) + case (Right(acc), Right(path)) => Right(acc :+ path) + } + .map { params => + new ExistentialPathList { + type Underlying = runtime.PathList + implicit val Underlying: WeakTypeTag[Underlying] = combine(params) + } + } + + private def combine(paths: Seq[ExistentialPath]): WeakTypeTag[runtime.PathList] = { + object Combine { + def apply[A <: runtime.Path: WeakTypeTag, Args <: runtime.PathList: WeakTypeTag] + : WeakTypeTag[runtime.PathList.List[A, Args]] = + weakTypeTag[runtime.PathList.List[A, Args]] + } + + paths + .foldLeft[WeakTypeTag[? <: runtime.PathList]](weakTypeTag[runtime.PathList.Empty]) { (acc, path) => + Combine(path.Underlying, acc) + } + .asInstanceOf[WeakTypeTag[runtime.PathList]] + } + +// selectors +// .map(ExistentialPath.parse) +// +// extractParams(t).map { params => +// new ExistentialCtor { +// type Underlying = runtime.ArgumentLists +// implicit val Underlying: WeakTypeTag[runtime.ArgumentLists] = paramsToType(params) +// } +// } +// } +// +// private def paramsToType(paramsLists: List[List[ValDef]]): WeakTypeTag[runtime.ArgumentLists] = +// paramsLists +// .map { paramList => +// paramList.foldRight[WeakTypeTag[? <: runtime.ArgumentList]](weakTypeTag[runtime.ArgumentList.Empty])( +// constructArgumentListType +// ) +// } +// .foldRight[WeakTypeTag[? <: runtime.ArgumentLists]](weakTypeTag[runtime.ArgumentLists.Empty])( +// constructArgumentListsType +// ) +// .asInstanceOf[WeakTypeTag[runtime.ArgumentLists]] +// +// private def constructArgumentListsType( +// head: WeakTypeTag[? <: runtime.ArgumentList], +// tail: WeakTypeTag[? <: runtime.ArgumentLists] +// ): WeakTypeTag[? <: runtime.ArgumentLists] = { +// object ApplyParams { +// def apply[Head <: runtime.ArgumentList: WeakTypeTag, Tail <: runtime.ArgumentLists: WeakTypeTag] +// : WeakTypeTag[runtime.ArgumentLists.List[Head, Tail]] = +// weakTypeTag[runtime.ArgumentLists.List[Head, Tail]] +// } +// +// ApplyParams(head, tail) +// } +// +// private def constructArgumentListType( +// t: ValDef, +// args: WeakTypeTag[? <: runtime.ArgumentList] +// ): WeakTypeTag[? <: runtime.ArgumentList] = { +// object ApplyParam { +// def apply[ParamName <: String: WeakTypeTag, ParamType: WeakTypeTag, Args <: runtime.ArgumentList: WeakTypeTag] +// : WeakTypeTag[runtime.ArgumentList.Argument[ParamName, ParamType, Args]] = +// weakTypeTag[runtime.ArgumentList.Argument[ParamName, ParamType, Args]] +// } +// +// ApplyParam( +// c.WeakTypeTag(c.internal.constantType(Constant(t.name.decodedName.toString))), +// c.WeakTypeTag(t.tpt.tpe), +// args +// ) +// } +// +// import Console.* +// +// private def invalidConstructor(t: Tree): String = +// s"Expected function, instead got: $MAGENTA$t$RESET: $MAGENTA${t.tpe}$RESET" + } + // If we try to do: // // implicit val toPath = fieldName.Underlying @@ -304,6 +397,16 @@ private[chimney] trait DslMacroUtils { } } + protected trait ApplyFieldNamesType { + def apply[A <: runtime.PathList: WeakTypeTag]: WeakTypeTag[?] + + final def applyFromSelector(t: Seq[Tree]): WeakTypeTag[?] = + apply(extractSelectorAsType(t).Underlying) + + private def extractSelectorAsType(t: Seq[Tree]): ExistentialPathList = + ExistentialPathList.parse(t).fold(error => c.abort(c.enclosingPosition, error), path => path) + } + /** Workaround for Java Enums, see [[io.scalaland.chimney.internal.runtime.RefinedJavaEnum]]. */ protected trait ApplyFixedCoproductType {