in bijection-macros/src/main/scala/com/twitter/bijection/macros/impl/CaseClassToMap.scala [21:92]
def caseClassToMapImpl[T](
c: Context
)(
proof: c.Expr[IsCaseClass[T]]
)(implicit T: c.WeakTypeTag[T]): c.Expr[Injection[T, Map[String, Any]]] =
caseClassToMapNoProofImpl(c)(T)
def caseClassToMapImplNonRecursive[T](
c: Context
)(
proof: c.Expr[IsCaseClass[T]]
)(implicit T: c.WeakTypeTag[T]): c.Expr[Injection[T, Map[String, Any]]] =
caseClassToMapNoProofImplNonRecursive(c)(T)
def caseClassToMapNoProofImplNonRecursive[T](
c: Context
)(implicit T: c.WeakTypeTag[T]): c.Expr[Injection[T, Map[String, Any]]] =
caseClassToMapNoProofImplCommon(c, false)(T)
// TODO the only diff between this and the above is the case match and the converters. it's easy to gate this on the boolean
def caseClassToMapNoProofImpl[T](
c: Context
)(implicit T: c.WeakTypeTag[T]): c.Expr[Injection[T, Map[String, Any]]] =
caseClassToMapNoProofImplCommon(c, true)(T)
def caseClassToMapNoProofImplCommon[T](c: Context, recursivelyApply: Boolean)(implicit
T: c.WeakTypeTag[T]
): c.Expr[Injection[T, Map[String, Any]]] = {
import c.universe._
// TODO can make error handling better?
val companion = T.tpe.typeSymbol.companionSymbol
val getPutConv = T.tpe.declarations
.collect { case m: MethodSymbol if m.isCaseAccessor => m }
.zipWithIndex
.map { case (m, idx) =>
val returnType = m.returnType
val accStr = m.name.toTermName.toString
returnType match {
case tpe if recursivelyApply && IsCaseClassImpl.isCaseClassType(c)(tpe) =>
val conv = newTermName("c2m_" + idx)
(
q"""$conv.invert(m($accStr).asInstanceOf[_root_.scala.collection.immutable.Map[String, Any]]).get""",
q"""($accStr, $conv(t.$m))""",
Some(
q"""val $conv = implicitly[_root_.com.twitter.bijection.Injection[$tpe, _root_.scala.collection.immutable.Map[String, Any]]]"""
)
) // TODO cache these
case tpe =>
(q"""m($accStr).asInstanceOf[$returnType]""", q"""($accStr, t.$m)""", None)
}
}
val getters = getPutConv.map(_._1)
val putters = getPutConv.map(_._2)
val converters = getPutConv.flatMap(_._3)
c.Expr[Injection[T, Map[String, Any]]](q"""
new Injection[$T, _root_.scala.collection.immutable.Map[String, Any]] with MacroGenerated {
override def apply(t: $T): _root_.scala.collection.immutable.Map[String, Any] = {
..$converters
_root_.scala.collection.immutable.Map[String, Any](..$putters)
}
override def invert(m: _root_.scala.collection.immutable.Map[String, Any]): _root_.scala.util.Try[ $T ] = {
..$converters
try { _root_.scala.util.Success($companion(..$getters)) }
catch { case _root_.scala.util.control.NonFatal(e) =>
_root_.scala.util.Failure(new _root_.com.twitter.bijection.InversionFailure(m, e)) }
}
}
""")
}