summaryrefslogtreecommitdiff
path: root/core/src/mill/main/Resolve.scala
blob: b01391d003d8d597bcda6aefd0099fb3c302c844 (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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package mill.main

import mill.define._
import mill.define.TaskModule
import ammonite.main.Router
import ammonite.main.Router.EntryPoint

object Resolve {
  def resolve[T, V](remainingSelector: List[Segment],
                    obj: mill.Module,
                    discover: Discover,
                    rest: Seq[String],
                    remainingCrossSelectors: List[List[String]],
                    revSelectorsSoFar: List[Segment]): Either[String, Seq[Task[Any]]] = {

    remainingSelector match{
      case Segment.Cross(_) :: Nil => Left("Selector cannot start with a [cross] segment")
      case Segment.Label(last) :: Nil =>
        val target =
          obj
            .millInternal
            .reflect[Target[_]]
            .find(_.label == last)
            .map(Right(_))

        def invokeCommand(target: mill.Module, name: String) = {

          for{
            (cls, entryPoints) <- discover.value.filterKeys(_.isAssignableFrom(target.getClass))
            ep <- entryPoints
            if ep.name == name
          } yield ep.asInstanceOf[EntryPoint[mill.Module]].invoke(target, ammonite.main.Scripts.groupArgs(rest.toList)) match {
            case Router.Result.Success(v) => Right(v)
            case _ => Left(s"Command failed $last")
          }
        }

        val runDefault = for{
          child <- obj.millInternal.reflectNestedObjects[mill.Module]
          if child.millOuterCtx.segment == Segment.Label(last)
          res <- child match{
            case taskMod: TaskModule => Some(invokeCommand(child, taskMod.defaultCommandName()).headOption)
            case _ => None
          }
        } yield res

        val command = invokeCommand(obj, last).headOption

        command orElse target orElse runDefault.flatten.headOption match{
          case None =>  Left("Cannot resolve task " +
            Segments((Segment.Label(last) :: revSelectorsSoFar).reverse:_*).render
          )
          // Contents of `either` *must* be a `Task`, because we only select
          // methods returning `Task` in the discovery process
          case Some(either) => either.right.map{ case x: Task[Any] => Seq(x) }
        }


      case head :: tail =>
        val newRevSelectorsSoFar = head :: revSelectorsSoFar
        head match{
          case Segment.Label(singleLabel) =>
            if (singleLabel == "__"){
              val matching =
                obj.millInternal
                  .modules
                  .map(resolve(tail, _, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar))
                  .collect{case Right(vs) => vs}.flatten

              if (matching.nonEmpty) Right(matching.toSeq)
              else Left("Cannot resolve module " + Segments(newRevSelectorsSoFar.reverse:_*).render)
            }else if (singleLabel == "_") {
              val matching =
                obj.millInternal
                  .reflectNestedObjects[mill.Module]
                  .map(resolve(tail, _, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar))
                  .collect{case Right(vs) => vs}.flatten

              if (matching.nonEmpty)Right(matching)
              else Left("Cannot resolve module " + Segments(newRevSelectorsSoFar.reverse:_*).render)
            }else{

              obj.millInternal.reflectNestedObjects[mill.Module].find{
                _.millOuterCtx.segment == Segment.Label(singleLabel)
              } match{
                case Some(child: mill.Module) => resolve(tail, child, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar)
                case None => Left("Cannot resolve module " + Segments(newRevSelectorsSoFar.reverse:_*).render)
              }
            }

          case Segment.Cross(cross) =>
            obj match{
              case c: Cross[_] =>
                if(cross == Seq("__")){
                  val matching =
                    for ((k, v) <- c.items)
                    yield resolve(tail, v.asInstanceOf[mill.Module], discover, rest, remainingCrossSelectors, newRevSelectorsSoFar)

                  val results = matching.collect{case Right(res) => res}.flatten

                  if (results.isEmpty) Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render)
                  else Right(results)
                } else if (cross.contains("_")){
                  val matching = for {
                    (k, v) <- c.items
                    if k.length == cross.length
                    if k.zip(cross).forall { case (l, r) => l == r || r == "_" }
                  } yield resolve(tail, v.asInstanceOf[mill.Module], discover, rest, remainingCrossSelectors, newRevSelectorsSoFar)

                  val results = matching.collect{case Right(res) => res}.flatten

                  if (results.isEmpty) Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render)
                  else Right(results)
                }else{
                  c.itemMap.get(cross.toList) match{
                    case Some(m: mill.Module) => resolve(tail, m, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar)
                    case None => Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render)
                  }
                }
              case _ => Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render)
            }
        }

      case Nil => Left("Selector cannot be empty")
    }
  }
}