mirror of https://github.com/sbt/sbt.git
cross-configurations
This commit is contained in:
parent
4bb7c44730
commit
62691e6681
|
|
@ -27,7 +27,7 @@ trait SingleProject extends Tasked
|
|||
val dummies = new Transform.Dummies(In, State, Streams)
|
||||
def name(t: Task[_]): String = context.staticName(t) getOrElse std.Streams.name(t)
|
||||
val injected = new Transform.Injected( input, state, std.Streams(t => streamBase / name(t)) )
|
||||
context.forName(input.name) map { t => (t map(_ => state), Transform(dummies, injected, context) ) }
|
||||
context.forName(input.name) map { t => (t.merge.map(_ => state), Transform(dummies, injected, context) ) }
|
||||
}
|
||||
|
||||
def help: Seq[Help] = Nil
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@
|
|||
*/
|
||||
package sbt
|
||||
|
||||
import Types._
|
||||
import Task._
|
||||
import Types._
|
||||
import Task._
|
||||
|
||||
// Action, Task, and Info are intentionally invariant in its type parameter.
|
||||
// Action, Task, and Info are intentionally invariant in their type parameter.
|
||||
// Various natural transformations used, such as PMap, require invariant type constructors for correctness
|
||||
|
||||
sealed trait Action[T]
|
||||
|
|
@ -15,9 +15,12 @@ final case class Mapped[T, In <: HList](in: Tasks[In], f: Results[In] => T) exte
|
|||
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[Result[U]] => Either[Task[T], T]) extends Action[T]
|
||||
final case class CrossAction[T](subs: Cross[Task[T]] ) extends Action[T]
|
||||
|
||||
|
||||
object Task
|
||||
{
|
||||
type Cross[T] = Seq[(AttributeMap, T)]
|
||||
type Tasks[HL <: HList] = KList[Task, HL]
|
||||
type Results[HL <: HList] = KList[Result, HL]
|
||||
}
|
||||
|
|
@ -41,8 +44,8 @@ final case class Info[T](attributes: AttributeMap = AttributeMap.empty, original
|
|||
}
|
||||
object Info
|
||||
{
|
||||
val Name = AttributeKey.make[String]
|
||||
val Description = AttributeKey.make[String]
|
||||
val Implied = AttributeKey.make[Boolean]
|
||||
val Cross = AttributeKey.make[AttributeMap]
|
||||
val Name = AttributeKey[String]("name")
|
||||
val Description = AttributeKey[String]("description")
|
||||
val Implied = AttributeKey[Boolean]("implied")
|
||||
val Cross = AttributeKey[AttributeMap]("cross-configuration")
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// 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) )
|
||||
}
|
||||
|
|
@ -140,6 +140,7 @@ object Convert
|
|||
case FlatMapped(in, f) => toNode(in)( left ∙ f )
|
||||
case DependsOn(in, deps) => toNode(KList.fromList(deps))( _ => Left(in) )
|
||||
case Join(in, f) => uniform(in)(f)
|
||||
case CrossAction(subs) => error("Cannot run cross task: " + subs.mkString("\n\t","\n\t","\n"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@
|
|||
package sbt
|
||||
package std
|
||||
|
||||
import Types._
|
||||
import Task._
|
||||
import java.io.{BufferedInputStream, BufferedReader, File, InputStream}
|
||||
|
||||
import Types._
|
||||
import Task._
|
||||
import java.io.{BufferedInputStream, BufferedReader, File, InputStream}
|
||||
import Cross.{combine, crossTask, exist, expandExist, extract, hasCross, uniform}
|
||||
|
||||
sealed trait MultiInTask[In <: HList]
|
||||
{
|
||||
|
|
@ -32,6 +32,10 @@ sealed trait SingleInTask[S]
|
|||
def || [T >: S](alt: Task[T]): Task[T]
|
||||
def && [T](alt: Task[T]): Task[T]
|
||||
}
|
||||
sealed trait CrossMerge[T]
|
||||
{
|
||||
def merge: Task[Cross[T]]
|
||||
}
|
||||
sealed trait TaskInfo[S]
|
||||
{
|
||||
def named(s: String): Task[S]
|
||||
|
|
@ -41,13 +45,14 @@ sealed trait TaskInfo[S]
|
|||
sealed trait ForkTask[S, CC[_]]
|
||||
{
|
||||
def fork[T](f: S => T): CC[Task[T]]
|
||||
def tasks: Seq[Task[S]]
|
||||
}
|
||||
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 binary[T](f: BufferedInputStream => T): Task[T]
|
||||
|
|
@ -65,62 +70,97 @@ sealed trait TaskLines
|
|||
def lines: Task[List[String]]
|
||||
def lines(sid: String): Task[List[String]]
|
||||
}
|
||||
sealed trait ProcessPipe {
|
||||
sealed trait ProcessPipe
|
||||
{
|
||||
def #| (p: ProcessBuilder): Task[Int]
|
||||
def pipe(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 cross[T](key: AttributeKey[T])(values: T*): Task[T] =
|
||||
CrossAction( for(v <- values) yield ( AttributeMap.empty put (key, v), task(v) ) )
|
||||
|
||||
final implicit def t2ToMulti[A,B](t: (Task[A],Task[B])) = multInputTask(t._1 :^: t._2 :^: KNil)
|
||||
final implicit def f2ToHfun[A,B,R](f: (A,B) => R): (A :+: B :+: HNil => R) = { case a :+: b :+: HNil => f(a,b) }
|
||||
|
||||
final implicit def t3ToMulti[A,B,C](t: (Task[A],Task[B],Task[C])) = multInputTask(t._1 :^: t._2 :^: t._3 :^: KNil)
|
||||
final implicit def f3ToHfun[A,B,C,R](f: (A,B,C) => R): (A :+: B :+: C :+: HNil => R) = { case a :+: b :+: c :+: HNil => f(a,b,c) }
|
||||
|
||||
final implicit def actionToTask[T](a: Action[T]): 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 upcastTask[A >: B, B](t: Task[B]): Task[A] = t map { x => x : B }
|
||||
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)
|
||||
def fork[T](f: S => T): Seq[Task[T]] = in.map(x => task(f(x)))
|
||||
def tasks: Seq[Task[S]] = fork(identity)
|
||||
}
|
||||
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] {
|
||||
|
||||
final implicit def joinAnyTasks(in: Seq[Task[_]]): JoinTask[Any, Seq] = joinTasks[Any](in map (x => x: Task[Any]))
|
||||
final implicit def joinTasks[S](in: Seq[Task[S]]): JoinTask[S, Seq] =
|
||||
if(hasCross(in)) multJoin(in) else basicJoin(in)
|
||||
|
||||
final def multJoin[S](in: Seq[Task[S]]): JoinTask[S, Seq] = new JoinTask[S, Seq] {
|
||||
def join: Task[Seq[S]] = uniform(in)( (_, s) => basicJoin(s).join )
|
||||
def reduce(f: (S,S) => S): Task[S] = basicJoin(in) reduce f
|
||||
}
|
||||
final def basicJoin[S](in: Seq[Task[S]]): JoinTask[S, Seq] = new JoinTask[S, Seq] {
|
||||
def join: Task[Seq[S]] = new Join(in, (s: Seq[Result[S]]) => Right(TaskExtra.all(s)) )
|
||||
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] {
|
||||
import TaskExtra.{allM, anyFailM}
|
||||
|
||||
def flatMapR[T](f: Results[In] => Task[T]): Task[T] = new FlatMapped(tasks, f)
|
||||
def flatMap[T](f: In => Task[T]): Task[T] = flatMapR(f compose allM)
|
||||
def flatFailure[T](f: Seq[Incomplete] => Task[T]): Task[T] = flatMapR(f compose anyFailM)
|
||||
|
||||
def mapR[T](f: Results[In] => T): Task[T] = new Mapped(tasks, f)
|
||||
def map[T](f: In => T): Task[T] = mapR(f compose allM)
|
||||
def mapFailure[T](f: Seq[Incomplete] => T): Task[T] = mapR(f compose anyFailM)
|
||||
final implicit def crossMerge[T](in: Task[T]): CrossMerge[T] = new CrossMerge[T] {
|
||||
def merge: Task[Cross[T]] = in.work match {
|
||||
case CrossAction(subs) =>
|
||||
val (maps, tasks) = subs.unzip
|
||||
tasks.join.map { maps zip _ }
|
||||
case _ => in map( x => (AttributeMap.empty, x) :: Nil)
|
||||
}
|
||||
}
|
||||
|
||||
final implicit def singleInputTask[S](in: Task[S]): SingleInTask[S] = new SingleInTask[S] {
|
||||
import TaskExtra.{successM, failM}
|
||||
final implicit def multInputTask[In <: HList](tasks: Tasks[In]): MultiInTask[In] =
|
||||
if(hasCross(tasks.toList)) multCross(tasks) else multBasic(tasks)
|
||||
|
||||
final def multCross[In <: HList](tasks: Tasks[In]): MultiInTask[In] = new MultiBase[In] {
|
||||
def flatMapR[T](f: Results[In] => Task[T]): Task[T] = Cross(tasks)( (m, ts) => new FlatMapped[T,In](ts, extract[T](m) compose f) )
|
||||
def mapR[T](f: Results[In] => T): Task[T] = Cross(tasks)( (_, ts) => multBasic(ts) mapR f)
|
||||
}
|
||||
|
||||
final def multBasic[In <: HList](tasks: Tasks[In]): MultiInTask[In] = new MultiBase[In] {
|
||||
def flatMapR[T](f: Results[In] => Task[T]): Task[T] = new FlatMapped(tasks, extract() ∙ f)
|
||||
def mapR[T](f: Results[In] => T): Task[T] = new Mapped(tasks, f)
|
||||
}
|
||||
|
||||
final implicit def singleInputTask[S](in: Task[S]): SingleInTask[S] =
|
||||
in.work match {
|
||||
case CrossAction(subs) => singleCross(in, subs)
|
||||
case x => singleBasic(in)
|
||||
}
|
||||
|
||||
final def singleCross[S](in: Task[S], subs: Cross[Task[S]]): SingleInTask[S] =
|
||||
new SingleBase[S] {
|
||||
def impl[T](f: (AttributeMap, Task[S]) => Task[T]): Task[T] = CrossAction( subs map { case (m, t) => (m, f(m, t)) } )
|
||||
def flatMapR[T](f: Result[S] => Task[T]): Task[T] = impl( (m, t) => singleBasic(t) flatMapR( Cross.extract(m) ∙ f) )
|
||||
def mapR[T](f: Result[S] => T): Task[T] = impl( (m,t) => t mapR f)
|
||||
def dependsOn(tasks: Task[_]*): Task[S] = crossTask( combine( subs, expandExist(tasks) ){ (t,deps) => new DependsOn(t, deps) } )
|
||||
}
|
||||
|
||||
final def singleBasic[S](in: Task[S]): SingleInTask[S] = new SingleBase[S] {
|
||||
type HL = S :+: HNil
|
||||
private val ml = in :^: KNil
|
||||
private def headM = (_: Results[HL]).combine.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] = flatMapR(f compose successM)
|
||||
def flatFailure[T](f: Incomplete => Task[T]): Task[T] = flatMapR(f compose failM)
|
||||
|
||||
def flatMapR[T](f: Result[S] => Task[T]): Task[T] = new FlatMapped[T, HL](ml, Cross.extract() ∙ f ∙ headM)
|
||||
def mapR[T](f: Result[S] => T): Task[T] = new Mapped[T, HL](ml, f ∙ headM)
|
||||
def map[T](f: S => T): Task[T] = mapR(f compose successM)
|
||||
def mapFailure[T](f: Incomplete => T): Task[T] = mapR(f compose failM)
|
||||
|
||||
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 )
|
||||
def dependsOn(tasks: Task[_]*): Task[S] =
|
||||
if(hasCross(tasks))
|
||||
singleCross(in, (AttributeMap.empty, in) :: Nil).dependsOn(tasks :_*)
|
||||
else
|
||||
new DependsOn(in, tasks)
|
||||
}
|
||||
|
||||
final implicit def toTaskInfo[S](in: Task[S]): TaskInfo[S] = new TaskInfo[S] {
|
||||
def named(s: String): Task[S] = in.copy(info = in.info.setName(s))
|
||||
def describedAs(s: String): Task[S] = in.copy(info = in.info.setDescription(s))
|
||||
|
|
@ -167,6 +207,31 @@ trait TaskExtra
|
|||
val pio = TaskExtra.processIO(s)
|
||||
(p run pio).exitValue
|
||||
}
|
||||
|
||||
private[this] abstract class SingleBase[S] extends SingleInTask[S]
|
||||
{
|
||||
import TaskExtra.{successM, failM}
|
||||
|
||||
def flatMap[T](f: S => Task[T]): Task[T] = flatMapR(f compose successM)
|
||||
def flatFailure[T](f: Incomplete => Task[T]): Task[T] = flatMapR(f compose failM)
|
||||
|
||||
def map[T](f: S => T): Task[T] = mapR(f compose successM)
|
||||
def mapFailure[T](f: Incomplete => T): Task[T] = mapR(f compose failM)
|
||||
|
||||
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 )
|
||||
}
|
||||
private[this] abstract class MultiBase[In <: HList] extends MultiInTask[In]
|
||||
{
|
||||
import TaskExtra.{allM, anyFailM}
|
||||
|
||||
def flatMap[T](f: In => Task[T]): Task[T] = flatMapR(f compose allM)
|
||||
def flatFailure[T](f: Seq[Incomplete] => Task[T]): Task[T] = flatMapR(f compose anyFailM)
|
||||
|
||||
def map[T](f: In => T): Task[T] = mapR(f compose allM)
|
||||
def mapFailure[T](f: Seq[Incomplete] => T): Task[T] = mapR(f compose anyFailM)
|
||||
}
|
||||
}
|
||||
object TaskExtra extends TaskExtra
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ import Types._
|
|||
// T must be invariant to work properly.
|
||||
// Because it is sealed and the only instances go through make,
|
||||
// a single AttributeKey instance cannot conform to AttributeKey[T] for different Ts
|
||||
sealed trait AttributeKey[T]
|
||||
sealed trait AttributeKey[T] {
|
||||
def label: String
|
||||
}
|
||||
object AttributeKey
|
||||
{
|
||||
def make[T]: AttributeKey[T] = new AttributeKey[T] {}
|
||||
def apply[T](name: String): AttributeKey[T] = new AttributeKey[T] { def label = name }
|
||||
}
|
||||
|
||||
trait AttributeMap
|
||||
|
|
@ -20,15 +22,35 @@ trait AttributeMap
|
|||
def get[T](k: AttributeKey[T]): Option[T]
|
||||
def contains[T](k: AttributeKey[T]): Boolean
|
||||
def put[T](k: AttributeKey[T], value: T): AttributeMap
|
||||
def keys: Iterable[AttributeKey[_]]
|
||||
def ++(o: AttributeMap): AttributeMap
|
||||
def entries: Iterable[AttributeEntry[_]]
|
||||
def isEmpty: Boolean
|
||||
}
|
||||
object AttributeMap
|
||||
{
|
||||
def empty: AttributeMap = new BasicAttributeMap(Map.empty)
|
||||
val empty: AttributeMap = new BasicAttributeMap(Map.empty)
|
||||
}
|
||||
private class BasicAttributeMap(backing: Map[AttributeKey[_], Any]) extends AttributeMap
|
||||
private class BasicAttributeMap(private val backing: Map[AttributeKey[_], Any]) extends AttributeMap
|
||||
{
|
||||
def isEmpty: Boolean = backing.isEmpty
|
||||
def apply[T](k: AttributeKey[T]) = backing(k).asInstanceOf[T]
|
||||
def get[T](k: AttributeKey[T]) = backing.get(k).asInstanceOf[Option[T]]
|
||||
def contains[T](k: AttributeKey[T]) = backing.contains(k)
|
||||
def put[T](k: AttributeKey[T], value: T): AttributeMap = new BasicAttributeMap( backing.updated(k, value) )
|
||||
def keys: Iterable[AttributeKey[_]] = backing.keys
|
||||
def ++(o: AttributeMap): AttributeMap =
|
||||
o match {
|
||||
case bam: BasicAttributeMap => new BasicAttributeMap(backing ++ bam.backing)
|
||||
case _ => o ++ this
|
||||
}
|
||||
def entries: Iterable[AttributeEntry[_]] =
|
||||
for( (k: AttributeKey[kt], v) <- backing) yield AttributeEntry(k, v.asInstanceOf[kt])
|
||||
override def toString = entries.mkString("(", ", ", ")")
|
||||
}
|
||||
|
||||
// type inference required less generality
|
||||
final case class AttributeEntry[T](a: AttributeKey[T], b: T)
|
||||
{
|
||||
override def toString = a.label + ": " + b
|
||||
}
|
||||
|
|
@ -454,14 +454,21 @@ object IO
|
|||
|
||||
// Not optimized for large files
|
||||
def readLines(in: BufferedReader): List[String] =
|
||||
foldLines[List[String]](in, Nil)( (accum, line) => line :: accum )
|
||||
|
||||
def foreachLine(in: BufferedReader)(f: String => Unit): Unit =
|
||||
foldLines(in, ())( (_, line) => f(line) )
|
||||
|
||||
def foldLines[T](in: BufferedReader, init: T)(f: (T, String) => T): T =
|
||||
{
|
||||
def readLine(accum: List[String]): List[String] =
|
||||
def readLine(accum: T): T =
|
||||
{
|
||||
val line = in.readLine()
|
||||
if(line eq null) accum.reverse else readLine(line :: accum)
|
||||
if(line eq null) accum else readLine(f(accum, line))
|
||||
}
|
||||
readLine(Nil)
|
||||
readLine(init)
|
||||
}
|
||||
|
||||
def writeLines(file: File, lines: Seq[String], charset: Charset = defaultCharset, append: Boolean = false): Unit =
|
||||
writer(file, lines.headOption.getOrElse(""), charset, append) { w =>
|
||||
lines.foreach { line => w.write(line); w.newLine() }
|
||||
|
|
|
|||
Loading…
Reference in New Issue