sbt/tasks/standard/Cross.scala

98 lines
3.2 KiB
Scala

// TODO: join, reduce
package sbt
package std
import Types._
import Task._
object Cross
{
type AttributeSet = Set[AttributeKey[_]]
def hasCross(s: Seq[Task[_]]): Boolean = s.exists { _.work.isInstanceOf[CrossAction[_]] }
def extract[T](in: AttributeMap = AttributeMap.empty): Task[T] => Task[T] = (result: Task[T]) =>
result.work match
{
case CrossAction(subs) =>
subs.filter { x => compatible(in)(x._1) } match {
case Seq( (_,matchingTask) ) => matchingTask
case Seq() => if(subs.isEmpty) error("Cannot introduce cross configurations with flatMap") else error("No compatible cross configurations returned by " + result)
case _ => error("Multiple compatible cross configurations returned by " + result)
}
case _ => result
}
def compatible(base: AttributeMap)(nested: AttributeMap): Boolean =
nested.entries forall { case AttributeEntry(key, v) => base get(key) filter (_ == v) isDefined }
def expandUniform[S](uniform: Seq[Task[S]]): Cross[ Seq[Task[S]] ] =
// the cast is necessary to avoid duplicating the contents of expandExist
// and expandCrossed (expandExist cannot be defined in terms of expandUniform
// because there is no valid instatiation of S)
expandExist(uniform).asInstanceOf[ Cross[ Seq[Task[S]] ] ]
def expandExist(dependsOn: Seq[Task[_]]): Cross[ Seq[Task[_]] ] =
{
val klist = KList.fromList(dependsOn)
map( expandCrossed( klist ) ) { _.toList }
}
def keys(c: Cross[_]): AttributeSet =
(Set.empty[AttributeKey[_]] /: c ){ _ ++ _._1.keys }
def uniform[S,T](in: Seq[Task[S]])(f: (AttributeMap, Seq[Task[S]]) => Task[T]): Task[T] =
crossTask( expandUniform(in), f)
def exist[T](in: Seq[Task[_]])(f: (AttributeMap, Seq[Task[_]]) => Task[T]): Task[T] =
crossTask( expandExist(in), f)
def apply[T, HL <: HList](in: Tasks[HL])(f: (AttributeMap, Tasks[HL]) => Task[T]): Task[T] =
crossTask( expandCrossed(in), f)
def crossTask[S,T](subs: Cross[S], f: (AttributeMap, S) => Task[T]): Task[T] =
crossTask( subs map { case (m, t) => (m, f(m, t)) } )
def crossTask[T](subs: Cross[Task[T]]): Task[T] =
TaskExtra.actionToTask( CrossAction(subs) )
def expandCrossed[HL <: HList](in: Tasks[HL]): Cross[Tasks[HL]] =
in match {
case KCons(head, tail) =>
val crossTail = expandCrossed(tail)
head.work match {
case CrossAction(subs) => combine(subs, crossTail)(KCons.apply)
case _ => crossTail map { case (m, k) => (m, KCons(head, k) ) }
}
case x => (AttributeMap.empty, x) :: Nil
}
def combine[A,B,C](a: Cross[A], b: Cross[B])(f: (A,B) => C): Cross[C] =
{
val keysA = keys(a)
val keysB = keys(b)
val common = keysA & keysB
if( keysA.size > keysB.size )
merge(b,a, common)( (x,y) => f(y,x) )
else
merge(a,b, common)(f)
}
private[this] def merge[A,B,C](a: Cross[A], b: Cross[B], common: AttributeSet)(f: (A,B) => C): Seq[(AttributeMap, C)] =
{
def related(mapA: AttributeMap): Cross[B] =
b filter { case (mapB, _) =>
common forall ( c => mapA(c) == mapB(c) )
}
def check(aRb: Cross[B]) = if(aRb.isEmpty) error("Cross mismatch") else aRb
for {
(mapA, taskA) <- a
(mapB, taskB) <- check(related(mapA))
} yield
( mapA ++ mapB, f(taskA, taskB) )
}
def map[A,B](c: Cross[A])(f: A => B): Cross[B] =
for( (m, a) <- c) yield (m, f(a) )
}