summaryrefslogtreecommitdiff
path: root/core/src/main/scala/mill/main/Resolve.scala
blob: df3c250d1953d11dd4239b4face4d66b95307a7c (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
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, Task[Any]] = {

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

        def invokeCommand[V](target: mill.Module, name: String) = for{
          cmd <- discover.value.get(target.getClass).toSeq.flatten.find(_.name == name)
        } yield cmd.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")
        }

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

        def command = invokeCommand(obj, last)

        command orElse target orElse runDefault.headOption.flatten 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] => x }
        }


      case head :: tail =>
        val newRevSelectorsSoFar = head :: revSelectorsSoFar
        head match{
          case Segment.Label(singleLabel) =>
            obj.reflectNestedObjects[mill.Module].find{
              _.ctx.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[_] =>
                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")
    }
  }
}