1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
package forge
import forge.util.Labelled
import play.api.libs.json.Format
import language.experimental.macros
import reflect.macros.blackbox.Context
class Discovered[T](val value: Seq[(Seq[String], Format[_], T => Target[_])]){
def apply(t: T) = value.map{case (a, f, b) => (a, f, b(t)) }
}
object Discovered {
def makeTuple[T, V](path: Seq[String], func: T => Target[V])(implicit f: Format[V]) = {
(path, f, func)
}
def mapping[T: Discovered](t: T): Map[Target[_], Labelled[_]] = {
implicitly[Discovered[T]].apply(t)
.map(x => x._3 -> Labelled(x._3.asInstanceOf[Target[Any]], x._2.asInstanceOf[Format[Any]], x._1))
.toMap
}
implicit def apply[T]: Discovered[T] = macro applyImpl[T]
def applyImpl[T: c.WeakTypeTag](c: Context): c.Expr[Discovered[T]] = {
import c.universe._
val tpe = c.weakTypeTag[T].tpe
def rec(segments: List[String], t: c.Type): Seq[Seq[String]] = for {
m <- t.members.toSeq
if m.isTerm && (m.asTerm.isGetter || m.asTerm.isLazy) || m.isModule
res <- {
val extendedSegments = m.name.toString :: segments
val self =
if (m.typeSignature.resultType <:< c.weakTypeOf[Target[_]]) Seq(extendedSegments)
else Nil
val children = rec(extendedSegments, m.typeSignature)
self ++ children
}
} yield res
val reversedPaths = rec(Nil, tpe)
val result = for(reversedPath <- reversedPaths.toList) yield {
val base = q"${TermName(c.freshName())}"
val segments = reversedPath.reverse.toList
val ident = segments.foldLeft[Tree](base)((prefix, name) =>
q"$prefix.${TermName(name)}"
)
q"forge.Discovered.makeTuple($segments, ($base: $tpe) => $ident)"
}
c.Expr[Discovered[T]](q"new _root_.forge.Discovered($result)")
}
}
|