Last active
June 13, 2019 09:05
-
-
Save joprice/c9f9c42fe0e99c9ada87 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// allows converting one class to another by providing missing fields | |
object convert { | |
@annotation.implicitNotFound(""" | |
You have not provided enough arguments to convert from ${In} to ${Out}. | |
${Args} | |
""") | |
trait Convertible[Args, In, Out] { | |
def apply(args: Args, in: In): Out | |
} | |
object Convertible { | |
// This is a hack to allow 'to' to be a single-parameter method. The implicit | |
// search in Converter for the Convertible instance drives the lookup for the other dependenices | |
implicit def makeConvertible[Args <: HList, In, RIn <: HList, Out, ROut <: HList, MR <: HList, IR <: HList]( | |
implicit | |
ingen: LabelledGeneric.Aux[In, RIn], | |
outgen: LabelledGeneric.Aux[Out, ROut], | |
merger: Merger.Aux[RIn, Args, MR], | |
intersection: Intersection.Aux[MR, ROut, IR], | |
align: Align[IR, ROut] | |
): Convertible[Args, In, Out] = new Convertible[Args, In, Out] { | |
def apply(args: Args, in: In) = { | |
outgen.from(align(intersection(merger(ingen.to(in), args)))) | |
} | |
} | |
} | |
// this builder pattern is needed since RecordArgs blows up if a type parameter is added to it, | |
// preventing the nicer syntax of convert[T](id =...) | |
class ConvertibleBuilder[Args <: HList, In](args: Args, in: In) { | |
def to[Out](implicit c: Convertible[Args, In, Out]) = c(args, in) | |
} | |
object syntax { | |
implicit class ConvertOps[T <: Product](t: T) { | |
object convert extends RecordArgs { | |
object to { | |
def apply[U](implicit c: Convertible[HNil, T, U]) = c(HNil, t) | |
} | |
def applyRecord[R <: HList](rec: R) = new ConvertibleBuilder(rec, t) | |
} | |
} | |
} | |
} | |
import convert.syntax._ | |
case class NewUser(name: String, age: Int) | |
case class User(id: Int, name: String, age: Int) | |
val x: NewUser = NewUser(name = "abc", age = 23) | |
val y: User = x.convert(id = 1).to[User] | |
val z: NewUser = y.convert.to[NewUser] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment