Given a pair of case classes, Source
and Target
, that have nested case classes, and at each level of nesting, the fields in Target
are unaligned subsets of the ones in Source
, is there a way to write a generic Shapeless-powered transform from Source
to Target
?
For example, given the following Internal
and External
classes:
object Internal {
case class User(
firstName: String,
lastName: String,
isAdmin: Boolean,
address: Address
)
case class Address(
street: String,
country: String,
blacklisted: Boolean
)
}
object External {
// Note that isAdmin is missing and the fields are jumbled
case class User(
lastName: String,
firstName: String,
address: Address
)
// blacklisted is gone
case class Address(
street: String,
country: String
)
}
I'd like to be able to do something like
val internalUser = Internal.User(
firstName = "Joe",
lastName = "Blow",
isAdmin = false,
address = Internal.Address(
street = "Sesame",
country = "U-S-A",
blacklisted = false
)
)
val externalUser = Transform.into[External.User](internalUser)
I have some code that takes care of selecting a subset and aligning the fields, but the recursion part is a bit more challenging:
import shapeless._, ops.hlist.Align, ops.hlist.SelectAll, SelectAll._
class Transform[T] {
// The fun stuff. Given an S, returns a T, if S has the right (subset of) fields
def apply[S, SR <: HList, TR <: HList](s: S)(
implicit
genS: LabelledGeneric.Aux[S, SR],
genT: LabelledGeneric.Aux[T, TR],
selectAll: SelectAll[SR, TR],
align: Align[SelectAll[SR, TR]#Out, TR]): T =
genT.from(align(selectAll(genS.to(s))))
}
object Transform {
// Convenience method for building an instance of `Transform`
def into[T] = new Transform[T]
}
I've taken a look at this SO question, but the answer there doesn't take care of the fact that the fields are un-aligned subsets of the other.