summaryrefslogtreecommitdiff
path: root/core/src/main/scala/mill/discover/Mirror.scala
blob: bb6e44adb79ecf4d4f38be976c851c3fcc0e5a05 (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
package mill.discover

import mill.define.{Segment, Segments, Target, Task}
import ammonite.main.Router.EntryPoint

import scala.language.experimental.macros

/**
  * Metadata about a build that is extracted & materialized at compile-time,
  * letting us use it at run-time without needing to use the heavy weight
  * scala-reflect library.
  *
  * Note that [[Mirror]] allows you to store and inspect metadata of a type
  * [[T]] even without a concrete instance of [[T]] itself.
  */
case class Mirror[-T, V](node: (T, List[List[Any]]) => V,
                         commands: Seq[EntryPoint[V]],
                         targets: Seq[Mirror.TargetPoint[V, _]],
                         children: List[(String, Mirror[T, _])],
                         crossChildren: Option[(V => List[List[Any]], Mirror[T, _])])

object Mirror{


  def traverseNode[T, V, R](t: T, hierarchy: Mirror[T, V])
                           (f: (Mirror[T, _], => Segments, => Any) => Seq[R]): Seq[R] = {
    traverse(t, hierarchy){ (mirror, segmentsRev) =>
      lazy val resolvedNode = mirror.node(
        t,
        segmentsRev.value.map{case Segment.Cross(vs) => vs.toList case _ => Nil}.toList
      )
      f(mirror, segmentsRev, resolvedNode)
    }
  }
  def traverse[T, V, R](t: T, hierarchy: Mirror[T, V])
                       (f: (Mirror[T, _], => Segments) => Seq[R]): Seq[R] = {
    def rec[C](segmentsRev: List[Segment], h: Mirror[T, C]): Seq[R]= {
      val crossValues = segmentsRev.map{case Segment.Cross(vs) => vs case _ => Nil}
      val self = f(h, Segments(segmentsRev.reverse:_*))
      self ++
      h.children.flatMap{case (label, c) => rec(Segment.Label(label) :: segmentsRev, c)} ++
      h.crossChildren.toSeq.flatMap{
        case (crossGen, c) =>
          crossGen(h.node(t, crossValues.reverse.map(_.toList))).flatMap(cross =>
            rec(Segment.Cross(cross) :: segmentsRev, c)
          )
      }
    }
    rec(Nil, hierarchy)
  }


  /**
    * Represents metadata about a particular target, before the target is
    * materialized for a concrete build
    */
  case class TargetPoint[T, V](label: String, run: T => Target[V])
}