mirror of https://github.com/sbt/sbt.git
a standard task system on top of the 'tasks' module
map, flatMap on single/parallel tasks dependsOn for side-effect-only dependencies variants to handle failure- operations similar to catch/finally fork, join, reduce, ... initial structure to handle tags, needs to be moved to generic map per-task streams: file-backed, named input/output streams and readers keyed by task name grab streams of current task or another task's streams pipe between tasks, ProcessBuilders (partially unify tasks/processes) access to command line, current State from any task multi-project aggregation controllable per-task open issue: overloading #| with multiple type classes does not work
This commit is contained in:
parent
23bf3e55c7
commit
1fef28d812
|
|
@ -43,8 +43,9 @@ class XSbt(info: ProjectInfo) extends ParentProject(info) with NoCrossPaths
|
|||
val buildSub = baseProject("main" / "build", "Project Builder",
|
||||
classfileSub, classpathSub, compilePersistSub, compilerSub, compileIncrementalSub, interfaceSub, ivySub, launchInterfaceSub, logSub, discoverySub, processSub)
|
||||
|
||||
val stdTaskSub = testedBase(tasksPath / "standard", "Task System", taskSub, collectionSub, logSub, ioSub, processSub)
|
||||
val altCompilerSub = project("main", "Alternate Compiler Test", (i: ProjectInfo) => new Base(i) { override def normalizedName = "sbt" }, // temporary
|
||||
buildSub, compileIncrementalSub, compilerSub, completeSub, discoverySub, ioSub, logSub, processSub, taskSub)
|
||||
buildSub, compileIncrementalSub, compilerSub, completeSub, discoverySub, ioSub, logSub, processSub, taskSub, stdTaskSub)
|
||||
|
||||
/** following modules are not updated for 2.8 or 0.9 */
|
||||
/*val testSub = project("scripted", "Test", new TestProject(_), ioSub)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
|
||||
import Types._
|
||||
import Task._
|
||||
|
||||
// Action, Task, and Info are intentionally invariant in its type parameter.
|
||||
// Various natural transformations used, such as PMap, require invariant type constructors for correctness
|
||||
|
||||
sealed trait Action[T]
|
||||
sealed case class Pure[T](f: () => T) extends Action[T]
|
||||
|
||||
final case class Mapped[T, In <: HList](in: Tasks[In], f: Results[In] => T) extends Action[T]
|
||||
final case class MapAll[T, In <: HList](in: Tasks[In], f: In => T) extends Action[T]
|
||||
final case class MapFailure[T, In <: HList](in: Tasks[In], f: Seq[Incomplete] => T) extends Action[T]
|
||||
|
||||
final case class FlatMapAll[T, In <: HList](in: Tasks[In], f: In => Task[T]) extends Action[T]
|
||||
final case class FlatMapFailure[T, In <: HList](in: Tasks[In], f: Seq[Incomplete] => Task[T]) extends Action[T]
|
||||
final case class FlatMapped[T, In <: HList](in: Tasks[In], f: Results[In] => Task[T]) extends Action[T]
|
||||
|
||||
final case class DependsOn[T](in: Task[T], deps: Seq[Task[_]]) extends Action[T]
|
||||
|
||||
final case class Join[T, U](in: Seq[Task[U]], f: Seq[U] => Either[Task[T], T]) extends Action[T]
|
||||
|
||||
object Task
|
||||
{
|
||||
type Tasks[HL <: HList] = KList[Task, HL]
|
||||
type Results[HL <: HList] = KList[Result, HL]
|
||||
}
|
||||
|
||||
final case class Task[T](info: Info[T], work: Action[T])
|
||||
{
|
||||
def original = info.original getOrElse this
|
||||
}
|
||||
/** `original` is used during transformation only.*/
|
||||
final case class Info[T](name: Option[String] = None, description: Option[String] = None, implied: Boolean = false, original: Option[Task[T]] = None)
|
||||
{
|
||||
assert(name forall (_ != null))
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
package std
|
||||
|
||||
import java.io.{InputStream, OutputStream, Reader, Writer}
|
||||
import java.io.{BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter, PrintWriter}
|
||||
import java.io.{Closeable, File, FileInputStream, FileOutputStream, InputStreamReader, OutputStreamWriter}
|
||||
|
||||
import Path._
|
||||
|
||||
sealed trait TaskStreams
|
||||
{
|
||||
def default = outID
|
||||
def outID = "out"
|
||||
def errorID = "err"
|
||||
|
||||
def readText(a: Task[_], sid: String = default, update: Boolean = true): Task[BufferedReader]
|
||||
def readBinary(a: Task[_], sid: String = default, update: Boolean = true): Task[BufferedInputStream]
|
||||
|
||||
final def readText(a: Task[_], sid: Option[String], update: Boolean): Task[BufferedReader] =
|
||||
readText(a, getID(sid), update)
|
||||
|
||||
final def readBinary(a: Task[_], sid: Option[String], update: Boolean): Task[BufferedInputStream] =
|
||||
readBinary(a, getID(sid), update)
|
||||
|
||||
def text(sid: String = default): PrintWriter
|
||||
def binary(sid: String = default): BufferedOutputStream
|
||||
|
||||
// default logger
|
||||
/*val log: Logger
|
||||
def log(sid: String): Logger*/
|
||||
|
||||
private[this] def getID(s: Option[String]) = s getOrElse default
|
||||
}
|
||||
private[sbt] sealed trait ManagedTaskStreams extends TaskStreams
|
||||
{
|
||||
def open()
|
||||
def close()
|
||||
}
|
||||
|
||||
sealed trait Streams
|
||||
{
|
||||
def apply(a: Task[_], update: Boolean = true): ManagedTaskStreams
|
||||
}
|
||||
object Streams
|
||||
{
|
||||
private[this] val closeQuietly = (_: Closeable).close()
|
||||
|
||||
def multi[Owner](bases: Owner => File, taskOwner: Task[_] => Option[Owner]): Streams =
|
||||
{
|
||||
val taskDirectory = (t: Task[_]) => taskOwner(t) map bases getOrElse error("Cannot get streams for task '" + name(t) + "' with no owner.")
|
||||
apply(taskDirectory)
|
||||
}
|
||||
|
||||
def apply(taskDirectory: Task[_] => File): Streams = new Streams { streams =>
|
||||
|
||||
def apply(a: Task[_], update: Boolean): ManagedTaskStreams = new ManagedTaskStreams {
|
||||
private[this] var opened: List[Closeable] = Nil
|
||||
private[this] var closed = false
|
||||
|
||||
def readText(a: Task[_], sid: String = default, update: Boolean = true): Task[BufferedReader] =
|
||||
maybeUpdate(a, readText0(a, sid), update)
|
||||
|
||||
def readBinary(a: Task[_], sid: String = default, update: Boolean = true): Task[BufferedInputStream] =
|
||||
maybeUpdate(a, readBinary0(a, sid), update)
|
||||
|
||||
def text(sid: String = default): PrintWriter =
|
||||
make(a, sid)(f => new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), IO.defaultCharset))) )
|
||||
|
||||
def binary(sid: String = default): BufferedOutputStream =
|
||||
make(a, sid)(f => new BufferedOutputStream(new FileOutputStream(f)))
|
||||
|
||||
def make[T <: Closeable](a: Task[_], sid: String)(f: File => T): T = synchronized {
|
||||
checkOpen()
|
||||
val file = taskDirectory(a) / sid
|
||||
IO.touch(file)
|
||||
val t = f( file )
|
||||
opened ::= t
|
||||
t
|
||||
}
|
||||
|
||||
def readText0(a: Task[_], sid: String): BufferedReader =
|
||||
make(a, sid)(f => new BufferedReader(new InputStreamReader(new FileInputStream(f), IO.defaultCharset)) )
|
||||
|
||||
def readBinary0(a: Task[_], sid: String): BufferedInputStream =
|
||||
make(a, sid)(f => new BufferedInputStream(new FileInputStream(f)))
|
||||
|
||||
def maybeUpdate[T](base: Task[_], result: => T, update: Boolean) =
|
||||
{
|
||||
def basic(a: Action[T]) = Task(Info(), a)
|
||||
val main = Pure(result _)
|
||||
val act = if(update) DependsOn(basic(main), base :: Nil) else main
|
||||
basic(act)
|
||||
}
|
||||
|
||||
def open() {}
|
||||
|
||||
def close(): Unit = synchronized {
|
||||
if(!closed)
|
||||
{
|
||||
closed = true
|
||||
opened foreach closeQuietly
|
||||
}
|
||||
}
|
||||
def checkOpen(): Unit = synchronized {
|
||||
if(closed) error("Streams for '" + name(a) + "' have been closed.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def name(a: Task[_]): String = a.info.name getOrElse anonName(a)
|
||||
def anonName(a: Task[_]) = "anon" + java.lang.Integer.toString(java.lang.System.identityHashCode(a), 36)
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
package std
|
||||
|
||||
import Types._
|
||||
import Task._
|
||||
import Execute._
|
||||
|
||||
object System
|
||||
{
|
||||
def fromDummy[T](original: Task[T])(action: => T): Task[T] = Task(original.info, Pure(action _))
|
||||
def fromDummyStrict[T](original: Task[T], value: T): Task[T] = fromDummy(original)( value)
|
||||
|
||||
implicit def to_~>| [K[_], V[_]](map: RMap[K,V]) : K ~>| V = new (K ~>| V) { def apply[T](k: K[T]): Option[V[T]] = map.get(k) }
|
||||
|
||||
def dummyMap[Input, State](dummyIn: Task[Input], dummyState: Task[State])(in: Input, state: State): Task ~>| Task =
|
||||
{
|
||||
// helps ensure that the same Task[Nothing] can't be passed for dummyIn and dummyState
|
||||
assert(dummyIn ne dummyState, "Dummy tasks for Input and State must be distinct.")
|
||||
val pmap = new DelegatingPMap[Task, Task](new collection.mutable.ListMap)
|
||||
pmap(dummyIn) = fromDummyStrict(dummyIn, in)
|
||||
pmap(dummyState) = fromDummyStrict(dummyState, state)
|
||||
pmap
|
||||
}
|
||||
|
||||
/** Applies `map`, returning the result if defined or returning the input unchanged otherwise.*/
|
||||
implicit def getOrId(map: Task ~>| Task): Task ~> Task =
|
||||
new (Task ~> Task) {
|
||||
def apply[T](in: Task[T]): Task[T] = map(in).getOrElse(in)
|
||||
}
|
||||
|
||||
def implied[Owner](owner: Task[_] => Option[Owner], subs: Owner => Iterable[Owner], static: (Owner, String) => Option[Task[_]]): Task ~> Task =
|
||||
new (Task ~> Task) {
|
||||
|
||||
def impliedDeps(t: Task[_]): Seq[Task[_]] =
|
||||
for( n <- t.info.name.toList; o <- owner(t.original).toList; agg <- subs(o); implied <- static(agg, n) ) yield implied
|
||||
|
||||
def withImplied[T](in: Task[T]): Task[T] = Task(Info(), DependsOn(in, impliedDeps(in)))
|
||||
|
||||
def apply[T](in: Task[T]): Task[T] = if(in.info.implied) withImplied(in) else in
|
||||
}
|
||||
|
||||
def name(staticName: Task[_] => Option[String]): Task ~> Task =
|
||||
new (Task ~> Task) {
|
||||
def apply[T](in: Task[T]): Task[T] = {
|
||||
val finalName = in.info.name orElse staticName(in.original)
|
||||
in.copy(info = in.info.copy(name = finalName) )
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a natural transformation that replaces occurrences of 'a' with 'b'.
|
||||
* Only valid for M invariant in its type parameter. */
|
||||
def replace[M[_] <: AnyRef, A](a: M[A], b: M[A]) = new (M ~> M) {
|
||||
def apply[T](t: M[T]): M[T] =
|
||||
if(t eq a) b.asInstanceOf[M[T]] else t
|
||||
}
|
||||
|
||||
/** Returns the inputs to the Task that do not have their value discarded.
|
||||
* For example, this does not include the inputs of MapFailure or the dependencies of DependsOn. */
|
||||
def usedInputs(t: Action[_]): Seq[Task[_]] = t match {
|
||||
case m: Mapped[_,_] => m.in.toList
|
||||
case m: MapAll[_,_] => m.in.toList
|
||||
case m: FlatMapped[_,_] => m.in.toList
|
||||
case m: FlatMapAll[_,_] => m.in.toList
|
||||
case j: Join[_,_] => j.in
|
||||
case _ => Nil
|
||||
}
|
||||
|
||||
def streamed(streams: Streams, dummy: Task[TaskStreams]): Task ~> Task =
|
||||
new (Task ~> Task) {
|
||||
def apply[T](t: Task[T]): Task[T] = if(usedInputs(t.work) contains dummy) substitute(t) else t
|
||||
|
||||
def substitute[T](t: Task[T]): Task[T] =
|
||||
{
|
||||
val inStreams = streams(t)
|
||||
val streamsTask = fromDummy(dummy){ inStreams.open(); inStreams }
|
||||
|
||||
val depMap = replace( dummy, streamsTask )
|
||||
def wrap0[A,B](f: A => B) = (a: A) => try { f(a) } finally { inStreams.close() }
|
||||
def fin(k: Task[T]): Task[T] = {
|
||||
import TaskExtra._
|
||||
k andFinally { inStreams.close() }
|
||||
}
|
||||
def newWork(a: Action[T]): Task[T] = t.copy(work = a)
|
||||
|
||||
t.work match {
|
||||
case m: Mapped[_,_] => newWork( m.copy(in = m.in map depMap, f = wrap0(m.f) ) ) // the Streams instance is valid only within the mapping function
|
||||
case m: MapAll[_,_] => newWork( m.copy(in = m.in map depMap, f = wrap0(m.f) ) )
|
||||
case fm: FlatMapped[_,_] => fin(newWork( fm.copy(in = fm.in map depMap) )) // the Streams instance is valid until a result is produced for the task
|
||||
case fm: FlatMapAll[_,_] => fin(newWork( fm.copy(in = fm.in map depMap) ))
|
||||
case j: Join[_,u] => newWork( j.copy(j.in map depMap.fn[u], f = wrap0(j.f)) )
|
||||
case _ => t // can't get a TaskStreams value from the other types, so no need to transform here (shouldn't get here anyway because of usedInputs check)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
object Transform
|
||||
{
|
||||
final class Dummies[Input, State](val dummyIn: Task[Input], val dummyState: Task[State], val dummyStreams: Task[TaskStreams])
|
||||
final class Injected[Input, State](val in: Input, val state: State, val streams: Streams)
|
||||
trait Context[Owner]
|
||||
{
|
||||
def forName(s: String): Option[Task[_]]
|
||||
def staticName: Task[_] => Option[String]
|
||||
def owner: Task[_] => Option[Owner]
|
||||
def subs: Owner => Iterable[Owner]
|
||||
def static: (Owner, String) => Option[Task[_]]
|
||||
}
|
||||
|
||||
def apply[Input, State, Owner](dummies: Dummies[Input, State], injected: Injected[Input, State], context: Context[Owner]) =
|
||||
{
|
||||
import dummies._
|
||||
import injected._
|
||||
import context._
|
||||
import System._
|
||||
import Convert._
|
||||
val inputs = dummyMap(dummyIn, dummyState)(in, state)
|
||||
Convert.taskToNode ∙ streamed(streams, dummyStreams) ∙ implied(owner, subs, static) ∙ name(staticName) ∙ getOrId(inputs)
|
||||
}
|
||||
}
|
||||
object Convert
|
||||
{
|
||||
def taskToNode = new (Task ~> NodeT[Task]#Apply) {
|
||||
def apply[T](t: Task[T]): Node[Task, T] = t.work match {
|
||||
case Pure(eval) => toNode(KNil)( _ => Right(eval()) )
|
||||
case Mapped(in, f) => toNode(in)( right ∙ f )
|
||||
case MapAll(in, f) => toNode(in)( right ∙ (f compose allM) )
|
||||
case MapFailure(in, f) => toNode(in)( right ∙ (f compose anyFailM))
|
||||
case FlatMapped(in, f) => toNode(in)( left ∙ f )
|
||||
case FlatMapAll(in, f) => toNode(in)( left ∙ (f compose allM) )
|
||||
case FlatMapFailure(in, f) => toNode(in)( left ∙ (f compose anyFailM) )
|
||||
case DependsOn(in, deps) => toNode(KList.fromList(deps))( _ => Left(in) )
|
||||
case Join(in, f) => uniform(in)(f compose all)
|
||||
}
|
||||
}
|
||||
|
||||
def uniform[T, D](tasks: Seq[Task[D]])(f: Seq[Result[D]] => Either[Task[T], T]): Node[Task, T] = new Node[Task, T] {
|
||||
type Mixed = HNil
|
||||
val mixedIn = KNil
|
||||
type Uniform = D
|
||||
val uniformIn = tasks
|
||||
def work(mixed: Results[HNil], uniform: Seq[Result[Uniform]]) = f(uniform)
|
||||
}
|
||||
def toNode[T, In <: HList](in: Tasks[In])(f: Results[In] => Either[Task[T], T]): Node[Task, T] = new Node[Task, T] {
|
||||
type Mixed = In
|
||||
val mixedIn = in
|
||||
type Uniform = Nothing
|
||||
val uniformIn = Nil
|
||||
def work(results: Results[In], units: Seq[Result[Uniform]]) = f(results)
|
||||
}
|
||||
|
||||
def anyFailM[In <: HList]: Results[In] => Seq[Incomplete] = in =>
|
||||
{
|
||||
val incs = failuresM(in)
|
||||
if(incs.isEmpty) throw Incomplete(message = Some("Expected failure")) else incs
|
||||
}
|
||||
|
||||
def allM[In <: HList]: Results[In] => In = in =>
|
||||
{
|
||||
val incs = failuresM(in)
|
||||
if(incs.isEmpty) in.down(Result.tryValue) else throw Incomplete(causes = incs)
|
||||
}
|
||||
def all[D]: Seq[Result[D]] => Seq[D] = in =>
|
||||
{
|
||||
val incs = failures(in)
|
||||
if(incs.isEmpty) in.map(Result.tryValue.fn[D]) else throw Incomplete(causes = incs)
|
||||
}
|
||||
def failuresM[In <: HList]: Results[In] => Seq[Incomplete] = x => failures[Any](x.toList)
|
||||
def failures[A](results: Seq[Result[A]]): Seq[Incomplete] = results.collect { case Inc(i) => i }
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
/* sbt -- Simple Build Tool
|
||||
* Copyright 2010 Mark Harrah
|
||||
*/
|
||||
package sbt
|
||||
package std
|
||||
|
||||
import Types._
|
||||
import Task._
|
||||
import java.io.{BufferedInputStream, BufferedReader, File, InputStream}
|
||||
|
||||
|
||||
sealed trait MultiInTask[In <: HList]
|
||||
{
|
||||
def flatMap[T](f: In => Task[T]): Task[T]
|
||||
def flatMapR[T](f: Results[In] => Task[T]): Task[T]
|
||||
def mapH[T](f: In => T): Task[T]
|
||||
def mapR[T](f: Results[In] => T): Task[T]
|
||||
def flatFailure[T](f: Seq[Incomplete] => Task[T]): Task[T]
|
||||
def mapFailure[T](f: Seq[Incomplete] => T): Task[T]
|
||||
}
|
||||
sealed trait SingleInTask[S]
|
||||
{
|
||||
def flatMapR[T](f: Result[S] => Task[T]): Task[T]
|
||||
def flatMap[T](f: S => Task[T]): Task[T]
|
||||
def map[T](f: S => T): Task[T]
|
||||
def mapR[T](f: Result[S] => T): Task[T]
|
||||
def flatFailure[T](f: Incomplete => Task[T]): Task[T]
|
||||
def mapFailure[T](f: Incomplete => T): Task[T]
|
||||
def dependsOn(tasks: Task[_]*): Task[S]
|
||||
def andFinally(fin: => Unit): Task[S]
|
||||
|
||||
def || [T >: S](alt: Task[T]): Task[T]
|
||||
def && [T](alt: Task[T]): Task[T]
|
||||
}
|
||||
sealed trait TaskInfo[S]
|
||||
{
|
||||
def named(s: String): Task[S]
|
||||
def describedAs(s: String): Task[S]
|
||||
def implies: Task[S]
|
||||
}
|
||||
sealed trait ForkTask[S, CC[_]]
|
||||
{
|
||||
def fork[T](f: S => T): CC[Task[T]]
|
||||
}
|
||||
sealed trait JoinTask[S, CC[_]]
|
||||
{
|
||||
def join: Task[CC[S]]
|
||||
def reduce(f: (S,S) => S): Task[S]
|
||||
}
|
||||
import java.io._
|
||||
sealed trait BinaryPipe
|
||||
{
|
||||
def #| [T](f: BufferedInputStream => T): Task[T]
|
||||
//def #| [T](sid: String)(f: BufferedInputStream => T): Task[T]
|
||||
def #>(f: File): Task[Unit]
|
||||
//def #>(sid: String, f: File): Task[Unit]
|
||||
}
|
||||
sealed trait TextPipe
|
||||
{
|
||||
def #| [T](f: BufferedReader => T): Task[T]
|
||||
//def #| [T](sid: String)(f: BufferedReader => T): Task[T]
|
||||
}
|
||||
sealed trait TaskLines
|
||||
{
|
||||
def lines: Task[List[String]]
|
||||
def lines(sid: String): Task[List[String]]
|
||||
}
|
||||
sealed trait ProcessPipe {
|
||||
def #| (p: ProcessBuilder): Task[Int]
|
||||
//def #| (sid: String)(p: ProcessBuilder): Task[Int]
|
||||
}
|
||||
|
||||
trait TaskExtra
|
||||
{
|
||||
final implicit def actionToTask[A <% Action[T], T](a: A): Task[T] = Task(Info(), a)
|
||||
|
||||
final def task[T](f: => T): Task[T] = toTask(f _)
|
||||
final implicit def toTask[T](f: () => T): Task[T] = new Pure(f)
|
||||
|
||||
final implicit def pureTasks[S](in: Seq[S]): Seq[Task[S]] = in.map(s => task(s))
|
||||
final implicit def toTasks[S](in: Seq[() => S]): Seq[Task[S]] = in.map(toTask)
|
||||
final implicit def iterableTask[S](in: Seq[S]): ForkTask[S, Seq] = new ForkTask[S, Seq] {
|
||||
def fork[T](f: S => T): Seq[Task[T]] = in.map(x => task(x) map f)
|
||||
}
|
||||
final implicit def pureJoin[S](in: Seq[S]): JoinTask[S, Seq] = joinTasks(pureTasks(in))
|
||||
final implicit def joinTasks[S](in: Seq[Task[S]]): JoinTask[S, Seq] = new JoinTask[S, Seq] {
|
||||
def join: Task[Seq[S]] = new Join(in, (s: Seq[S]) => Right(s) )
|
||||
//def join[T](f: Iterable[S] => T): Task[Iterable[T]] = new MapAll( MList.fromTCList[Task](in), ml => f(ml.toList))
|
||||
//def joinR[T](f: Iterable[Result[S]] => T): Task[Iterable[Result[T]]] = new Mapped( MList.fromTCList[Task](in), ml => f(ml.toList))
|
||||
def reduce(f: (S,S) => S): Task[S] = TaskExtra.reduce(in.toIndexedSeq, f)
|
||||
}
|
||||
|
||||
final implicit def multInputTask[In <: HList](tasks: Tasks[In]): MultiInTask[In] = new MultiInTask[In] {
|
||||
def flatMap[T](f: In => Task[T]): Task[T] = new FlatMapAll(tasks, f)
|
||||
def flatMapR[T](f: Results[In] => Task[T]): Task[T] = new FlatMapped(tasks, f)
|
||||
def mapH[T](f: In => T): Task[T] = new MapAll(tasks, f)
|
||||
def mapR[T](f: Results[In] => T): Task[T] = new Mapped(tasks, f)
|
||||
def flatFailure[T](f: Seq[Incomplete] => Task[T]): Task[T] = new FlatMapFailure(tasks, f)
|
||||
def mapFailure[T](f: Seq[Incomplete] => T): Task[T] = new MapFailure(tasks, f)
|
||||
}
|
||||
|
||||
final implicit def singleInputTask[S](in: Task[S]): SingleInTask[S] = new SingleInTask[S] {
|
||||
type HL = S :+: HNil
|
||||
private val ml = in :^: KNil
|
||||
private def headM = (_: Results[HL]).combine.head
|
||||
private def headH = (_: HL).head
|
||||
private def headS = (_: Seq[Incomplete]).head
|
||||
|
||||
def flatMapR[T](f: Result[S] => Task[T]): Task[T] = new FlatMapped[T, HL](ml, f ∙ headM)
|
||||
def flatMap[T](f: S => Task[T]): Task[T] = new FlatMapAll(ml, f ∙ headH)
|
||||
def flatFailure[T](f: Incomplete => Task[T]): Task[T] = new FlatMapFailure(ml, f ∙ headS)
|
||||
|
||||
def map[T](f: S => T): Task[T] = new MapAll(ml, f ∙ headH)
|
||||
def mapR[T](f: Result[S] => T): Task[T] = new Mapped[T, HL](ml, f ∙ headM)
|
||||
def mapFailure[T](f: Incomplete => T): Task[T] = new MapFailure(ml, f ∙ headS)
|
||||
|
||||
def dependsOn(tasks: Task[_]*): Task[S] = new DependsOn(in, tasks)
|
||||
def andFinally(fin: => Unit): Task[S] = mapR(x => Result.tryValue[S]( { fin; x }))
|
||||
|
||||
def || [T >: S](alt: Task[T]): Task[T] = flatMapR { case Value(v) => task(v); case Inc(i) => alt }
|
||||
def && [T](alt: Task[T]): Task[T] = flatMap( _ => alt )
|
||||
}
|
||||
final implicit def toTaskInfo[S](in: Task[S]): TaskInfo[S] = new TaskInfo[S] {
|
||||
def named(s: String): Task[S] = in.copy(info = in.info.copy(name = Some(s)))
|
||||
def describedAs(s: String): Task[S] = in.copy(info = in.info.copy(description = Some(s)))
|
||||
def implies: Task[S] = in.copy(info = in.info.copy(implied = true))
|
||||
}
|
||||
|
||||
final implicit def pipeToProcess(t: Task[_])(implicit streams: Task[TaskStreams]): ProcessPipe = new ProcessPipe {
|
||||
def #| (p: ProcessBuilder): Task[Int] = pipe0(None, p)
|
||||
//def #| (sid: String)(p: ProcessBuilder): Task[Int] = pipe0(Some(sid), p)
|
||||
private def pipe0(sid: Option[String], p: ProcessBuilder): Task[Int] =
|
||||
for(s <- streams; in <- s.readBinary(t, sid, true)) yield {
|
||||
val pio = TaskExtra.processIO(s).withInput( out => { BasicIO.transferFully(in, out); out.close() } )
|
||||
(p run pio).exitValue
|
||||
}
|
||||
}
|
||||
|
||||
final implicit def binaryPipeTask(in: Task[_])(implicit streams: Task[TaskStreams]): BinaryPipe = new BinaryPipe {
|
||||
def #| [T](f: BufferedInputStream => T): Task[T] = pipe0(None, f)
|
||||
//def #| [T](sid: String)(f: BufferedInputStream => T): Task[T] = pipe0(Some(sid), f)
|
||||
|
||||
def #>(f: File): Task[Unit] = pipe0(None, toFile(f))
|
||||
//def #>(sid: String, f: File): Task[Unit] = pipe0(Some(sid), toFile(f))
|
||||
|
||||
private def pipe0 [T](sid: Option[String], f: BufferedInputStream => T): Task[T] =
|
||||
streams flatMap { s => s.readBinary(in, sid, true) map f }
|
||||
|
||||
private def toFile(f: File) = (in: InputStream) => IO.transfer(in, f)
|
||||
}
|
||||
final implicit def textPipeTask(in: Task[_])(implicit streams: Task[TaskStreams]): TextPipe = new TextPipe {
|
||||
def #| [T](f: BufferedReader => T): Task[T] = pipe0(None, f)
|
||||
//def #| [T](sid: String)(f: BufferedReader => T): Task[T] = pipe0(Some(sid), f)
|
||||
|
||||
private def pipe0 [T](sid: Option[String], f: BufferedReader => T): Task[T] =
|
||||
streams flatMap { s => s.readText(in, sid, true) map f }
|
||||
}
|
||||
final implicit def linesTask(in: Task[_])(implicit streams: Task[TaskStreams]): TaskLines = new TaskLines {
|
||||
def lines: Task[List[String]] = lines0(None)
|
||||
def lines(sid: String): Task[List[String]] = lines0(Some(sid))
|
||||
|
||||
private def lines0 [T](sid: Option[String]): Task[List[String]] =
|
||||
streams flatMap { s => s.readText(in, sid, true) map IO.readLines }
|
||||
}
|
||||
implicit def processToTask(p: ProcessBuilder)(implicit streams: Task[TaskStreams]): Task[Int] = streams map { s =>
|
||||
val pio = TaskExtra.processIO(s)
|
||||
(p run pio).exitValue
|
||||
}
|
||||
}
|
||||
object TaskExtra extends TaskExtra
|
||||
{
|
||||
def processIO(s: TaskStreams): ProcessIO =
|
||||
{
|
||||
def transfer(id: String) = (in: InputStream) => BasicIO.transferFully(in, s.binary(id))
|
||||
new ProcessIO(BasicIO.ignoreOut, transfer(s.outID), transfer(s.errorID))
|
||||
}
|
||||
def reduce[S](i: IndexedSeq[Task[S]], f: (S, S) => S): Task[S] =
|
||||
i match
|
||||
{
|
||||
case Seq() => error("Cannot reduce empty sequence")
|
||||
case Seq(x) => x
|
||||
case Seq(x, y) => reducePair(x, y, f)
|
||||
case z =>
|
||||
val (a, b) = i.splitAt(i.size / 2)
|
||||
reducePair( reduce(a, f), reduce(b, f), f )
|
||||
}
|
||||
def reducePair[S](a: Task[S], b: Task[S], f: (S, S) => S): Task[S] =
|
||||
(a :^: b :^: KNil) mapH { case x :+: y :+: HNil => f(x,y) }
|
||||
}
|
||||
Loading…
Reference in New Issue