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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
package forge.discover
import forge.define.Task
import forge.discover.Router.{EntryPoint, Result}
import play.api.libs.json.Format
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
class Discovered[T](val targets: Seq[(Seq[String], Format[_], T => Task[_])],
val mains: Seq[NestedEntry[T, _]]){
def apply(t: T) = targets.map{case (a, f, b) => (a, f, b(t)) }
}
case class Labelled[T](target: Task[T],
format: Format[T],
segments: Seq[String])
case class NestedEntry[T, V](path: Seq[String], resolve: T => V, entryPoint: EntryPoint[V]){
def invoke(target: T, groupedArgs: Seq[(String, Option[String])]): Result[Task[Any]] = {
entryPoint.invoke(resolve(target),groupedArgs)
}
}
object NestedEntry{
def make[T, V](path: Seq[String], resolve: T => V)
(entryPoint: EntryPoint[V]) = NestedEntry(path, resolve, entryPoint)
}
object Discovered {
def consistencyCheck[T](base: T, d: Discovered[T]) = {
val inconsistent = for{
(path, formatter, targetGen) <- d.targets
if targetGen(base) ne targetGen(base)
} yield path
inconsistent
}
def makeTuple[T, V](path: Seq[String], func: T => Task[V])(implicit f: Format[V]) = {
(path, f, func)
}
def mapping[T: Discovered](t: T): Map[Task[_], Labelled[_]] = {
implicitly[Discovered[T]].apply(t)
.map(x => x._3 -> Labelled(x._3.asInstanceOf[Task[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], Tree)], Seq[Seq[String]]) = {
val r = new Router(c)
val selfMains =
for(tree <- r.getAllRoutesForClass(t.asInstanceOf[r.c.Type]).asInstanceOf[Seq[c.Tree]])
yield (segments, tree)
val items = for {
m <- t.members.toSeq
if
(m.isTerm && (m.asTerm.isGetter || m.asTerm.isLazy)) ||
m.isModule ||
(m.isMethod && m.typeSignature.paramLists.isEmpty && m.typeSignature.resultType <:< c.weakTypeOf[Task[_]])
if !m.name.toString.contains('$')
} yield {
val extendedSegments = m.name.toString :: segments
val self =
if (m.typeSignature.resultType <:< c.weakTypeOf[Task[_]]) Seq(extendedSegments)
else Nil
val (mains, children) = rec(extendedSegments, m.typeSignature)
(mains, self ++ children)
}
val (mains, targets) = items.unzip
Tuple2(
selfMains ++ mains.flatten,
targets.flatten
)
}
val (entryPoints, 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.discover.Discovered.makeTuple($segments, ($base: $tpe) => $ident)"
}
val nested = for{
(reversedSegments, entry) <- entryPoints
} yield {
val segments = reversedSegments.reverse
val arg = TermName(c.freshName())
val select = segments.foldLeft[Tree](Ident(arg)) { (prefix, name) =>
q"$prefix.${TermName(name)}"
}
q"forge.discover.NestedEntry.make(Seq(..$segments), ($arg: $tpe) => $select)($entry)"
}
c.Expr[Discovered[T]](q"""
new _root_.forge.discover.Discovered(
$result,
${nested.toList}
)
""")
}
}
|