summaryrefslogtreecommitdiff
path: root/core/src/main/scala/mill/discover/Discovered.scala
blob: 0f3ced4e9ee24808ce1d2fd6ad47e49951584d6b (plain) (blame)
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
package mill.discover

import mill.define.Task.Module
import mill.define.{Target, Task}
import mill.discover.Mirror.LabelledTarget
import mill.discover.Router.{EntryPoint, Result}

import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context

/**
  * Allows you to implicitly summon up a build [[Mirror]] for arbitrary types
  */
class Discovered[T](val mirror: Mirror[T, T]){

  def targets(obj: T) = Mirror.traverse(mirror) { (h, p) =>
    h.labelled(obj, p)
  }

}

object Discovered {
  def consistencyCheck[T](base: T, d: Discovered[T]) = {
    val inconsistent = for{
      (t1, t2) <- d.targets(base).zip(d.targets(base))
      if t1.target ne t2.target
    } yield t1.segments
    inconsistent
  }


  def mapping[T: Discovered](t: T): Map[Target[_], LabelledTarget[_]] = {
    implicitly[Discovered[T]].targets(t).map(x => x.target -> x).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): Tree = {

      val r = new Router(c)


      val targets = for {
        m <- t.members.toList
        if m.isMethod &&
           m.typeSignature.paramLists.isEmpty &&
           m.typeSignature.resultType <:< c.weakTypeOf[Target[_]] &&
           !m.name.toString.contains('$') &&
           !m.name.toString.contains(' ')
      } yield {
        val x = Ident(TermName(c.freshName()))
        val t = q"""mill.discover.Mirror.makeTargetPoint(
          ${m.name.toString},
          ($x: ${m.typeSignature.resultType}) => $x.${m.name.toTermName}
        )"""

        c.internal.setPos(t, m.pos)
        t
      }



      val childHierarchies = for{
        m <- t.members.toList
        if m.typeSignature <:< c.weakTypeOf[Module]
      } yield {
        val name = m.name.toString.trim
        q"($name, ${rec(name :: segments, m.typeSignature)})"
      }

      val hierarchySelector = {
        val base = q"${TermName(c.freshName())}"
        val ident = segments.reverse.foldLeft[Tree](base) { (prefix, name) =>
          q"$prefix.${TermName(name)}"
        }
        q"($base: $tpe) => $ident"
      }

      val commands =
        r.getAllRoutesForClass(t.asInstanceOf[r.c.Type])
          .asInstanceOf[Seq[c.Tree]]
          .toList

      q"""mill.discover.Mirror[$tpe, $t](
        $hierarchySelector,
        $commands,
        $targets,
        $childHierarchies
      )"""
    }



    c.Expr[Discovered[T]](q"new _root_.mill.discover.Discovered(${rec(Nil, tpe)})")
  }
}