Skip to content

Commit 0d3e47d

Browse files
committed
Introduce new mill.api.opt with Opt, OptGroup, Opts and OptSyntax
1 parent b6468bf commit 0d3e47d

File tree

5 files changed

+215
-0
lines changed

5 files changed

+215
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package mill.api.daemon.internal
2+
3+
trait OptsApi {
4+
def toStringSeq: Seq[String]
5+
def value: Seq[OptGroupApi]
6+
}
7+
8+
trait OptGroupApi {
9+
def startsWithArg(prefix: String): Boolean
10+
}
11+
12+
trait OptApi {
13+
def toString(): String
14+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package mill.api.opt
2+
3+
import mill.api.daemon.internal.OptApi
4+
5+
import mill.api.JsonFormatters.given
6+
7+
import scala.annotation.targetName
8+
import scala.language.implicitConversions
9+
10+
type OptTypes = (String | os.Path)
11+
12+
case class Opt private (value: Seq[OptTypes]) extends OptApi {
13+
override def toString(): String = value.mkString("")
14+
15+
def map(conv: OptTypes => OptTypes): Opt = Opt(value.map(conv)*)
16+
17+
private def startStrings: Seq[String] =
18+
value.takeWhile(_.isInstanceOf[String]).collect { case s: String => s }
19+
20+
def startsWith(prefix: String): Boolean = startStrings.mkString("").startsWith(prefix)
21+
22+
def mapStartString(rep: String => String): Opt = {
23+
val rest = value.dropWhile(_.isInstanceOf[String])
24+
Opt((rep(startStrings.mkString("")) +: rest)*)
25+
}
26+
27+
def containsPaths: Boolean = value.exists {
28+
case _: os.Path => true
29+
case _ => false
30+
}
31+
}
32+
33+
object Opt {
34+
@targetName("applyVarArg")
35+
def apply(value: OptTypes*): Opt = {
36+
// TODO: merge sequential strings
37+
new Opt(value.filter {
38+
case s: String if s.isEmpty => false
39+
case _ => true
40+
})
41+
}
42+
43+
/**
44+
* Constructs a path from multiple path elements and a separator string.
45+
* Can be used to render classpaths.
46+
* Each path component will still be handled properly, e.g. mapped according to the current [[MappedPaths]] mapping.
47+
*/
48+
def mkPath(paths: Seq[os.Path], prefix: String = "", sep: String, suffix: String = ""): Opt = {
49+
var needSep = false
50+
Opt(
51+
(
52+
Seq(prefix) ++
53+
paths.flatMap { path =>
54+
if (needSep)
55+
Seq(sep, path)
56+
else {
57+
needSep = true
58+
Seq(path)
59+
}
60+
} ++ Seq(suffix)
61+
)*
62+
)
63+
}
64+
65+
def mkPlatformPath(paths: Seq[os.Path]): Opt = mkPath(paths, sep = java.io.File.pathSeparator)
66+
67+
given jsonReadWriter: upickle.ReadWriter[Opt] =
68+
upickle.readwriter[Seq[(Option[String], Option[os.Path])]].bimap(
69+
_.value.map {
70+
case path: os.Path => (None, Some(path))
71+
case str: String => (Some(str), None)
72+
},
73+
seq =>
74+
Opt(seq.map {
75+
case (Some(str), _) => str
76+
case (_, Some(path)) => path
77+
}*)
78+
)
79+
80+
given stringToOpt: Conversion[String, Opt] = (value: String) => Opt(value)
81+
given osPathToOpt: Conversion[os.Path, Opt] = (value: os.Path) => Opt(value)
82+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package mill.api.opt
2+
3+
import mill.api.daemon.internal.OptGroupApi
4+
5+
import scala.annotation.targetName
6+
import scala.language.implicitConversions
7+
8+
/**
9+
* A set of options, which are used together
10+
*/
11+
case class OptGroup private (value: Seq[Opt]) extends OptGroupApi
12+
derives upickle.ReadWriter {
13+
override def toString(): String = value.mkString("(", ", ", ")")
14+
15+
// /**
16+
// * Unify multiple options into one option.
17+
// * Same as `mkString(" ")`` on a options list of type `Seq[String].
18+
// */
19+
// def mkOpt(sep: String = " "): Opt = Opt(value*)
20+
21+
def isEmpty: Boolean = value.isEmpty
22+
23+
def containsPaths: Boolean = value.exists(_.containsPaths)
24+
25+
def head: Opt = value.head
26+
@deprecated
27+
override def startsWithArg(prefix: String): Boolean = internalStartsWith(prefix)
28+
@deprecated
29+
def startsWithArg(prefix: os.Path): Boolean = internalStartsWith(prefix)
30+
@deprecated
31+
private inline def internalStartsWith(prefix: (String | os.Path)): Boolean =
32+
value.headOption.exists {
33+
head =>
34+
(head, prefix) match {
35+
case (h: String, s: String) => h == s
36+
case (h: os.Path, p: os.Path) => h == p
37+
case _ => false
38+
}
39+
}
40+
}
41+
42+
object OptGroup {
43+
@targetName("applyUnion")
44+
def apply(value: (Opt | Seq[Opt])*): OptGroup = OptGroup(value.flatMap {
45+
case a: Opt => Seq(a)
46+
case s: Seq[Opt] => s
47+
})
48+
49+
def when(cond: Boolean)(value: Opt*): OptGroup = if (cond) OptGroup(value*) else OptGroup()
50+
51+
// given optsToOptGroup: Conversion[(OptTypes, OptTypes), OptGroup] =
52+
// (tuple: (OptTypes, OptTypes)) =>
53+
// OptGroup(Opt(tuple._1), Opt(tuple._2))
54+
55+
implicit def StringToOptGroup(s: String): OptGroup = OptGroup(Seq(Opt(s)))
56+
57+
implicit def OsPathToOptGroup(p: os.Path): OptGroup = OptGroup(Seq(Opt(p)))
58+
59+
implicit def OptToOptGroup(o: Opt): OptGroup = OptGroup(Seq(o))
60+
61+
implicit def IterableToOptGroup[T](s: Iterable[T])(using f: T => OptGroup): OptGroup =
62+
OptGroup(s.toSeq.flatMap(f(_).value))
63+
64+
implicit def ArrayToOptGroup[T](s: Array[T])(using f: T => OptGroup): OptGroup =
65+
OptGroup(s.flatMap(f(_).value))
66+
67+
}
68+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package mill.api.opt
2+
3+
import mill.api.daemon.internal.{OptApi, OptGroupApi, OptsApi}
4+
5+
import scala.annotation.targetName
6+
import scala.language.implicitConversions
7+
8+
implicit class OptSyntax(ctx: StringContext) extends AnyVal {
9+
def opt(opts: Any*): Opt = {
10+
val vals = ctx.parts.take(opts.length).zip(opts).flatMap { case (p, a) => Seq(p, a) } ++
11+
ctx.parts.drop(opts.length)
12+
13+
val elems: Seq[(String | os.Path)] = vals.flatMap {
14+
case path: os.Path => Seq(path)
15+
case s => Seq(s.toString).filter(_.nonEmpty)
16+
}
17+
Opt(elems*)
18+
}
19+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package mill.api.opt
2+
3+
import mill.api.daemon.internal.OptsApi
4+
5+
import scala.language.implicitConversions
6+
7+
case class Opts private (override val value: OptGroup*) extends OptsApi
8+
derives upickle.ReadWriter {
9+
10+
def toStringSeq: Seq[String] = value.map(_.toString)
11+
override def toString(): String = value.mkString("Opts(", ", ", ")")
12+
13+
def concat(suffix: Opts): Opts = Opts(value ++ suffix.value)
14+
@`inline` final def ++(suffix: Opts): Opts = concat(suffix)
15+
16+
def containsPaths: Boolean = value.exists(_.containsPaths)
17+
18+
def isEmpty: Boolean = value.isEmpty
19+
def filterGroup(pred: OptGroup => Boolean): Opts = Opts(value.filter(pred)*)
20+
def mapGroup(f: OptGroup => OptGroup): Opts = Opts(value.map(f)*)
21+
def flatMap(f: OptGroup => Seq[OptGroup]): Opts = Opts(value.flatMap(f)*)
22+
}
23+
24+
object Opts {
25+
def apply(value: OptGroup*): Opts = new Opts(value.filter(!_.isEmpty))
26+
// @targetName("applyUnion")
27+
// def apply(value: (Opt | OptGroup | Seq[Opt])*): Opts = Opts(value.flatMap {
28+
// case a: Opt => Seq(OptGroup(a))
29+
// case a: OptGroup => Seq(a)
30+
// case s: Iterable[Opt] => s.map(OptGroup(_))
31+
// })
32+
}

0 commit comments

Comments
 (0)