
play.api.libs.iteratee.Enumeratee.scala Maven / Gradle / Ivy
/*
* Copyright (C) 2009-2015 Typesafe Inc.
*/
package play.api.libs.iteratee
import play.api.libs.iteratee.Execution.Implicits.{ defaultExecutionContext => dec }
import play.api.libs.iteratee.internal.{ executeIteratee, executeFuture }
import scala.language.reflectiveCalls
import scala.util.control.NonFatal
import scala.concurrent.{ ExecutionContext, Future }
/**
* Combines the roles of an Iteratee[From] and a Enumerator[To]. This allows adapting of streams to that modify input
* produced by an Enumerator, or to be consumed by a Iteratee.
*/
trait Enumeratee[From, To] {
parent =>
/**
* Create a new Iteratee that feeds its input, potentially modifying it along the way, into the inner Iteratee, and
* produces that Iteratee as its result.
*/
def applyOn[A](inner: Iteratee[To, A]): Iteratee[From, Iteratee[To, A]]
/**
* Alias for `applyOn`
*/
def apply[A](inner: Iteratee[To, A]): Iteratee[From, Iteratee[To, A]] = applyOn[A](inner)
/**
* Transform the given iteratee into an iteratee that accepts the input type that this enumeratee maps.
*/
def transform[A](inner: Iteratee[To, A]): Iteratee[From, A] = apply(inner).joinI
/**
* Alias for `transform`
*/
def &>>[A](inner: Iteratee[To, A]): Iteratee[From, A] = transform(inner)
/**
* Alias for `apply`
*/
def &>[A](inner: Iteratee[To, A]): Iteratee[From, Iteratee[To, A]] = apply(inner)
/**
* Compose this Enumeratee with another Enumeratee
*/
def compose[To2](other: Enumeratee[To, To2]): Enumeratee[From, To2] = {
new Enumeratee[From, To2] {
def applyOn[A](iteratee: Iteratee[To2, A]): Iteratee[From, Iteratee[To2, A]] = {
parent.applyOn(other.applyOn(iteratee)).joinI
}
}
}
/**
* Compose this Enumeratee with another Enumeratee
*/
def ><>[To2](other: Enumeratee[To, To2]): Enumeratee[From, To2] = compose(other)
/**
* Compose this Enumeratee with another Enumeratee, concatenating any input left by both Enumeratees when they
* are done.
*/
def composeConcat[X](other: Enumeratee[To, To])(implicit p: To => scala.collection.TraversableLike[X, To], bf: scala.collection.generic.CanBuildFrom[To, X, To]): Enumeratee[From, To] = {
new Enumeratee[From, To] {
def applyOn[A](iteratee: Iteratee[To, A]): Iteratee[From, Iteratee[To, A]] = {
parent.applyOn(other.applyOn(iteratee).joinConcatI)
}
}
}
/**
* Alias for `composeConcat`
*/
def >+>[X](other: Enumeratee[To, To])(implicit p: To => scala.collection.TraversableLike[X, To], bf: scala.collection.generic.CanBuildFrom[To, X, To]): Enumeratee[From, To] = composeConcat[X](other)
}
/**
* @define paramEcSingle @param ec The context to execute the supplied function with. The context is prepared on the calling thread before being used.
* @define paramEcMultiple @param ec The context to execute the supplied functions with. The context is prepared on the calling thread before being used.
*/
object Enumeratee {
/**
* An Enumeratee that checks to ensure that the passed in Iteratee is not done before doing any work.
*/
trait CheckDone[From, To] extends Enumeratee[From, To] {
def continue[A](k: Input[To] => Iteratee[To, A]): Iteratee[From, Iteratee[To, A]]
def applyOn[A](it: Iteratee[To, A]): Iteratee[From, Iteratee[To, A]] =
it.pureFlatFold[From, Iteratee[To, A]] {
case Step.Cont(k) => continue(k)
case _ => Done(it, Input.Empty)
}(dec)
}
/**
* flatten a [[scala.concurrent.Future]] of [[play.api.libs.iteratee.Enumeratee]]] into an Enumeratee
*
* @param futureOfEnumeratee a future of enumeratee
*/
def flatten[From, To](futureOfEnumeratee: Future[Enumeratee[From, To]]) = new Enumeratee[From, To] {
def applyOn[A](it: Iteratee[To, A]): Iteratee[From, Iteratee[To, A]] =
Iteratee.flatten(futureOfEnumeratee.map(_.applyOn[A](it))(dec))
}
/**
* Create an Enumeratee that zips two Iteratees together.
*
* Each input gets passed to each Iteratee, and the result is a tuple of both of their results.
*
* If either Iteratee encounters an error, the result will be an error.
*
* The Enumeratee will continue consuming input until both inner Iteratees are done. If one inner Iteratee finishes
* before the other, the result of that Iteratee is held, and the one continues by itself, until it too is finished.
*/
def zip[E, A, B](inner1: Iteratee[E, A], inner2: Iteratee[E, B]): Iteratee[E, (A, B)] = zipWith(inner1, inner2)((_, _))(dec)
/**
* Create an Enumeratee that zips two Iteratees together, using the passed in zipper function to combine the results
* of the two.
*
* @param inner1 The first Iteratee to combine.
* @param inner2 The second Iteratee to combine.
* @param zipper Used to combine the results of each Iteratee.
* $paramEcSingle
*/
def zipWith[E, A, B, C](inner1: Iteratee[E, A], inner2: Iteratee[E, B])(zipper: (A, B) => C)(implicit ec: ExecutionContext): Iteratee[E, C] = {
val pec = ec.prepare()
import Execution.Implicits.{ defaultExecutionContext => ec } // Shadow ec to make this the only implicit EC in scope
def getNext(it1: Iteratee[E, A], it2: Iteratee[E, B]): Iteratee[E, C] = {
val eventuallyIter =
for (
(a1, it1_) <- getInside(it1);
(a2, it2_) <- getInside(it2)
) yield checkDone(a1, a2) match {
case Left((msg, in)) => Error(msg, in)
case Right(None) => Cont(step(it1_, it2_))
case Right(Some(Left(Left(a)))) => it2_.map(b => zipper(a, b))(pec)
case Right(Some(Left(Right(b)))) => it1_.map(a => zipper(a, b))(pec)
case Right(Some(Right(((a, b), e)))) => executeIteratee(Done(zipper(a, b), e))(pec)
}
Iteratee.flatten(eventuallyIter)
}
def step(it1: Iteratee[E, A], it2: Iteratee[E, B])(in: Input[E]) = {
Iteratee.flatten(
for (
it1_ <- it1.feed(in);
it2_ <- it2.feed(in)
) yield getNext(it1_, it2_))
}
def getInside[T](it: Iteratee[E, T]): Future[(Option[Either[(String, Input[E]), (T, Input[E])]], Iteratee[E, T])] = {
it.pureFold {
case Step.Done(a, e) => Some(Right((a, e)))
case Step.Cont(k) => None
case Step.Error(msg, e) => Some(Left((msg, e)))
}(dec).map(r => (r, it))(dec)
}
def checkDone(x: Option[Either[(String, Input[E]), (A, Input[E])]], y: Option[Either[(String, Input[E]), (B, Input[E])]]): Either[(String, Input[E]), Option[Either[Either[A, B], ((A, B), Input[E])]]] =
(x, y) match {
case (Some(Right((a, e1))), Some(Right((b, e2)))) => Right(Some(Right(((a, b), e1 /* FIXME: should calculate smalled here*/ ))))
case (Some(Left((msg, e))), _) => Left((msg, e))
case (_, Some(Left((msg, e)))) => Left((msg, e))
case (Some(Right((a, _))), None) => Right(Some(Left(Left(a))))
case (None, Some(Right((b, _)))) => Right(Some(Left(Right(b))))
case (None, None) => Right(None)
}
getNext(inner1, inner2)
}
/**
* A partially-applied function returned by the `mapInput` method.
*/
trait MapInput[From] {
/**
* @param f Used to transform each input element.
* $paramEcSingle
*/
def apply[To](f: Input[From] => Input[To])(implicit ec: ExecutionContext): Enumeratee[From, To]
}
/**
* Create an Enumeratee that transforms its input using the given function.
*
* This is like the `map` function, except that it allows the Enumeratee to, for example, send EOF to the inner
* iteratee before EOF is encountered.
*/
def mapInput[From] = new MapInput[From] {
def apply[To](f: Input[From] => Input[To])(implicit ec: ExecutionContext) = new CheckDone[From, To] {
val pec = ec.prepare()
def step[A](k: K[To, A]): K[From, Iteratee[To, A]] = {
case in @ (Input.El(_) | Input.Empty) => new CheckDone[From, To] {
def continue[A](k: K[To, A]) = Cont(step(k))
} &> Iteratee.flatten(Future(f(in))(pec).map(in => k(in))(dec))
case Input.EOF => Done(Cont(k), Input.EOF)
}
def continue[A](k: K[To, A]) = Cont(step(k))
}
}
/**
* A partially-applied function returned by the `mapConcatInput` method.
*/
trait MapConcatInput[From] {
/**
* @param f Used to transform each input element into a sequence of inputs.
* $paramEcSingle
*/
def apply[To](f: From => Seq[Input[To]])(implicit ec: ExecutionContext): Enumeratee[From, To]
}
/**
* Create an enumeratee that transforms its input into a sequence of inputs for the target iteratee.
*/
def mapConcatInput[From] = new MapConcatInput[From] {
def apply[To](f: From => Seq[Input[To]])(implicit ec: ExecutionContext) = mapFlatten[From](in => Enumerator.enumerateSeq2(f(in)))(ec)
}
/**
* A partially-applied function returned by the `mapConcat` method.
*/
trait MapConcat[From] {
/**
* @param f Used to transform each input element into a sequence of input elements.
* $paramEcSingle
*/
def apply[To](f: From => Seq[To])(implicit ec: ExecutionContext): Enumeratee[From, To]
}
/**
* Create an Enumeratee that transforms its input elements into a sequence of input elements for the target Iteratee.
*/
def mapConcat[From] = new MapConcat[From] {
def apply[To](f: From => Seq[To])(implicit ec: ExecutionContext) = mapFlatten[From](in => Enumerator.enumerateSeq1(f(in)))(ec)
}
/**
* A partially-applied function returned by the `mapFlatten` method.
*/
trait MapFlatten[From] {
/**
* @param f Used to transform each input element into an Enumerator.
* $paramEcSingle
*/
def apply[To](f: From => Enumerator[To])(implicit ec: ExecutionContext): Enumeratee[From, To]
}
/**
* Create an Enumeratee that transforms its input elements into an Enumerator that is fed into the target Iteratee.
*/
def mapFlatten[From] = new MapFlatten[From] {
def apply[To](f: From => Enumerator[To])(implicit ec: ExecutionContext) = new CheckDone[From, To] {
val pec = ec.prepare()
def step[A](k: K[To, A]): K[From, Iteratee[To, A]] = {
case Input.El(e) =>
new CheckDone[From, To] { def continue[A](k: K[To, A]) = Cont(step(k)) } &> Iteratee.flatten(Future(f(e))(pec).flatMap(_.apply(Cont(k)))(dec))
case Input.Empty =>
new CheckDone[From, To] { def continue[A](k: K[To, A]) = Cont(step(k)) } &> k(Input.Empty)
case Input.EOF => Done(Cont(k), Input.EOF)
}
def continue[A](k: K[To, A]) = Cont(step(k))
}
}
/**
* A partially-applied function returned by the `mapInputFlatten` method.
*/
trait MapInputFlatten[From] {
/**
* @param f Used to transform each input into an Enumerator.
* $paramEcSingle
*/
def apply[To](f: Input[From] => Enumerator[To])(implicit ec: ExecutionContext): Enumeratee[From, To]
}
/**
* Create an Enumeratee that transforms its input into an Enumerator that is fed into the target Iteratee.
*/
def mapInputFlatten[From] = new MapInputFlatten[From] {
def apply[To](f: Input[From] => Enumerator[To])(implicit ec: ExecutionContext) = new CheckDone[From, To] {
val pec = ec.prepare()
def step[A](k: K[To, A]): K[From, Iteratee[To, A]] = {
case in =>
new CheckDone[From, To] { def continue[A](k: K[To, A]) = Cont(step(k)) } &> Iteratee.flatten(Future(f(in))(pec).flatMap(_.apply(Cont(k)))(dec))
}
def continue[A](k: K[To, A]) = Cont(step(k))
}
}
/**
* A partially-applied function returned by the `mapInputM` method.
*/
trait MapInputM[From] {
/**
* @param f Used to transform each input.
* $paramEcSingle
*/
def apply[To](f: Input[From] => Future[Input[To]])(implicit ec: ExecutionContext): Enumeratee[From, To]
}
/**
* Like `mapInput`, but allows the map function to asynchronously return the mapped input.
*/
def mapInputM[From] = new MapInputM[From] {
def apply[To](f: Input[From] => Future[Input[To]])(implicit ec: ExecutionContext) = new CheckDone[From, To] {
val pec = ec.prepare()
def step[A](k: K[To, A]): K[From, Iteratee[To, A]] = {
case in @ (Input.El(_) | Input.Empty) =>
new CheckDone[From, To] { def continue[A](k: K[To, A]) = Cont(step(k)) } &> Iteratee.flatten(executeFuture(f(in))(pec).map(k(_))(dec))
case Input.EOF => Done(Cont(k), Input.EOF)
}
def continue[A](k: K[To, A]) = Cont(step(k))
}
}
/**
* A partially-applied function returned by the `mapM` method.
*/
trait MapM[E] {
/**
* @param f Used to transform each input element.
* $paramEcSingle
*/
def apply[NE](f: E => Future[NE])(implicit ec: ExecutionContext): Enumeratee[E, NE]
}
/**
* Like `map`, but allows the map function to asynchronously return the mapped element.
*/
def mapM[E] = new MapM[E] {
def apply[NE](f: E => Future[NE])(implicit ec: ExecutionContext): Enumeratee[E, NE] = mapInputM[E] {
case Input.Empty => Future.successful(Input.Empty)
case Input.EOF => Future.successful(Input.EOF)
case Input.El(e) => f(e).map(Input.El(_))(dec)
}(ec)
}
/**
* A partially-applied function returned by the `map` method.
*/
trait Map[E] {
/**
* @param f A function to transform input elements.
* $paramEcSingle
*/
def apply[NE](f: E => NE)(implicit ec: ExecutionContext): Enumeratee[E, NE]
}
/**
* Create an Enumeratee which transforms its input using a given function
*/
def map[E] = new Map[E] {
def apply[NE](f: E => NE)(implicit ec: ExecutionContext): Enumeratee[E, NE] = mapInput[E](in => in.map(f))(ec)
}
/**
* Create an Enumeratee that will take `count` input elements to pass to the target Iteratee, and then be done
*
* @param count The number of elements to take
*/
def take[E](count: Int): Enumeratee[E, E] = new CheckDone[E, E] {
def step[A](remaining: Int)(k: K[E, A]): K[E, Iteratee[E, A]] = {
case in @ Input.El(_) if remaining == 1 => Done(k(in), Input.Empty)
case in @ Input.El(_) if remaining > 1 =>
new CheckDone[E, E] { def continue[A](k: K[E, A]) = Cont(step(remaining - 1)(k)) } &> k(in)
case Input.Empty if remaining > 0 =>
new CheckDone[E, E] { def continue[A](k: K[E, A]) = Cont(step(remaining)(k)) } &> k(Input.Empty)
case Input.EOF => Done(Cont(k), Input.EOF)
case in => Done(Cont(k), in)
}
def continue[A](k: K[E, A]) = if (count <= 0) Done(Cont(k), Input.EOF) else Cont(step(count)(k))
}
/**
* A partially-applied function returned by the `scanLeft` method.
*/
trait ScanLeft[From] {
def apply[To](seed: To)(f: (To, From) => To): Enumeratee[From, To]
}
def scanLeft[From] = new ScanLeft[From] {
def apply[To](seed: To)(f: (To, From) => To): Enumeratee[From, To] = new CheckDone[From, To] {
def step[A](lastTo: To)(k: K[To, A]): K[From, Iteratee[To, A]] = {
case in @ Input.El(e) =>
val next = f(lastTo, e)
new CheckDone[From, To] { def continue[A](k: K[To, A]) = Cont(step(next)(k)) } &> k(Input.El(next))
case Input.Empty =>
new CheckDone[From, To] { def continue[A](k: K[To, A]) = Cont(step(lastTo)(k)) } &> k(Input.Empty)
case Input.EOF => Done(Cont(k), Input.EOF)
}
def continue[A](k: K[To, A]) = Cont(step(seed)(k))
}
}
/**
* A partially-applied function returned by the `grouped` method.
*/
trait Grouped[From] {
def apply[To](folder: Iteratee[From, To]): Enumeratee[From, To]
}
/**
* Create an Enumeratee that groups input using the given Iteratee.
*
* This will apply that Iteratee over and over, passing the result each time as the input for the target Iteratee,
* until EOF is reached. For example, let's say you had an Iteratee that took a stream of characters and parsed a
* single line:
*
* {{{
* def takeLine = for {
* line <- Enumeratee.takeWhile[Char](_ != '\n') &>> Iteratee.getChunks
* _ <- Enumeratee.take(1) &>> Iteratee.ignore[Char]
* } yield line.mkString
* }}}
*
* This could be used to build an Enumeratee that converts a stream of characters into a stream of lines:
*
* {{{
* def asLines = Enumeratee.grouped(takeLine)
* }}}
*/
def grouped[From] = new Grouped[From] {
def apply[To](folder: Iteratee[From, To]): Enumeratee[From, To] = new CheckDone[From, To] {
def step[A](f: Iteratee[From, To])(k: K[To, A]): K[From, Iteratee[To, A]] = {
case in @ (Input.El(_) | Input.Empty) =>
Iteratee.flatten(f.feed(in)).pureFlatFold {
case Step.Done(a, left) => new CheckDone[From, To] {
def continue[A](k: K[To, A]) =
(left match {
case Input.El(_) => step(folder)(k)(left)
case _ => Cont(step(folder)(k))
})
} &> k(Input.El(a))
case Step.Cont(kF) => Cont(step(Cont(kF))(k))
case Step.Error(msg, e) => Error(msg, in)
}(dec)
case Input.EOF => Iteratee.flatten(f.run.map[Iteratee[From, Iteratee[To, A]]]((c: To) => Done(k(Input.El(c)), Input.EOF))(dec))
}
def continue[A](k: K[To, A]) = Cont(step(folder)(k))
}
}
/**
* Create an Enumeratee that filters the inputs using the given predicate
*
* @param predicate A function to filter the input elements.
* $paramEcSingle
*/
def filter[E](predicate: E => Boolean)(implicit ec: ExecutionContext): Enumeratee[E, E] = new CheckDone[E, E] {
val pec = ec.prepare()
def step[A](k: K[E, A]): K[E, Iteratee[E, A]] = {
case in @ Input.El(e) => Iteratee.flatten(Future(predicate(e))(pec).map { b =>
if (b) (new CheckDone[E, E] { def continue[A](k: K[E, A]) = Cont(step(k)) } &> k(in)) else Cont(step(k))
}(dec))
case Input.Empty =>
new CheckDone[E, E] { def continue[A](k: K[E, A]) = Cont(step(k)) } &> k(Input.Empty)
case Input.EOF => Done(Cont(k), Input.EOF)
}
def continue[A](k: K[E, A]) = Cont(step(k))
}
/**
* Create an Enumeratee that filters the inputs using the negation of the given predicate
*
* @param predicate A function to filter the input elements.
* $paramEcSingle
*/
def filterNot[E](predicate: E => Boolean)(implicit ec: ExecutionContext): Enumeratee[E, E] = filter[E](e => !predicate(e))(ec)
/**
* A partially-applied function returned by the `collect` method.
*/
trait Collect[From] {
/**
* @param transformer A function to transform and filter the input elements with.
* $paramSingleEc
*/
def apply[To](transformer: PartialFunction[From, To])(implicit ec: ExecutionContext): Enumeratee[From, To]
}
/**
* Create an Enumeratee that both filters and transforms its input. The input is transformed by the given
* PartialFunction. If the PartialFunction isn't defined for an input element then that element is discarded.
*/
def collect[From] = new Collect[From] {
def apply[To](transformer: PartialFunction[From, To])(implicit ec: ExecutionContext): Enumeratee[From, To] = new CheckDone[From, To] {
val pec = ec.prepare()
def step[A](k: K[To, A]): K[From, Iteratee[To, A]] = {
case in @ Input.El(e) => Iteratee.flatten(Future {
if (transformer.isDefinedAt(e)) {
new CheckDone[From, To] { def continue[A](k: K[To, A]) = Cont(step(k)) } &> k(Input.El(transformer(e)))
} else {
Cont(step(k))
}
}(pec))
case Input.Empty =>
new CheckDone[From, To] { def continue[A](k: K[To, A]) = Cont(step(k)) } &> k(Input.Empty)
case Input.EOF => Done(Cont(k), Input.EOF)
}
def continue[A](k: K[To, A]) = Cont(step(k))
}
}
def drop[E](count: Int): Enumeratee[E, E] = new CheckDone[E, E] {
def step[A](remaining: Int)(k: K[E, A]): K[E, Iteratee[E, A]] = {
case in @ Input.El(_) if remaining == 1 => passAlong[E](Cont(k))
case in @ Input.El(_) if remaining > 1 => Cont(step(remaining - 1)(k))
case Input.Empty if remaining > 0 => Cont(step(remaining)(k))
case Input.EOF => Done(Cont(k), Input.EOF)
case in => passAlong[E] &> k(in)
}
def continue[A](k: K[E, A]) = Cont(step(count)(k))
}
/**
* Create an Enumeratee that drops input until a predicate is satisfied.
*
* @param f A predicate to test the input with.
* $paramEcSingle
*/
def dropWhile[E](p: E => Boolean)(implicit ec: ExecutionContext): Enumeratee[E, E] = {
val pec = ec.prepare()
new CheckDone[E, E] {
def step[A](k: K[E, A]): K[E, Iteratee[E, A]] = {
case in @ Input.El(e) => Iteratee.flatten(Future(p(e))(pec).map {
b => if (b) Cont(step(k)) else (passAlong[E] &> k(in))
}(dec))
case Input.Empty => Cont(step(k))
case Input.EOF => Done(Cont(k), Input.EOF)
}
def continue[A](k: K[E, A]) = Cont(step(k))
}
}
/**
* Create an Enumeratee that passes input through while a predicate is satisfied. Once the predicate
* fails, no more input is passed through.
*
* @param f A predicate to test the input with.
* $paramEcSingle
*/
def takeWhile[E](p: E => Boolean)(implicit ec: ExecutionContext): Enumeratee[E, E] = {
val pec = ec.prepare()
new CheckDone[E, E] {
def step[A](k: K[E, A]): K[E, Iteratee[E, A]] = {
case in @ Input.El(e) => Iteratee.flatten(Future(p(e))(pec).map {
b => if (b) (new CheckDone[E, E] { def continue[A](k: K[E, A]) = Cont(step(k)) } &> k(in)) else Done(Cont(k), in)
}(dec))
case Input.Empty =>
new CheckDone[E, E] { def continue[A](k: K[E, A]) = Cont(step(k)) } &> k(Input.Empty)
case Input.EOF => Done(Cont(k), Input.EOF)
}
def continue[A](k: K[E, A]) = Cont(step(k))
}
}
/**
* Create an Enumeratee that passes input through until a predicate is satisfied. Once the predicate
* is satisfied, no more input is passed through.
*
* @param f A predicate to test the input with.
* $paramEcSingle
*/
def breakE[E](p: E => Boolean)(implicit ec: ExecutionContext) = new Enumeratee[E, E] {
val pec = ec.prepare()
def applyOn[A](inner: Iteratee[E, A]): Iteratee[E, Iteratee[E, A]] = {
def step(inner: Iteratee[E, A])(in: Input[E]): Iteratee[E, Iteratee[E, A]] = in match {
case Input.El(e) => Iteratee.flatten(Future(p(e))(pec).map(b => if (b) Done(inner, in) else stepNoBreak(inner)(in))(dec))
case _ => stepNoBreak(inner)(in)
}
def stepNoBreak(inner: Iteratee[E, A])(in: Input[E]): Iteratee[E, Iteratee[E, A]] =
inner.pureFlatFold {
case Step.Cont(k) => {
val next = k(in)
next.pureFlatFold {
case Step.Cont(k) => Cont(step(next))
case _ => Done(inner, in)
}(dec)
}
case _ => Done(inner, in)
}(dec)
Cont(step(inner))
}
}
/**
* An enumeratee that passes all input through until EOF is reached, redeeming the final iteratee with EOF as the
* left over input.
*/
def passAlong[M] = new Enumeratee.CheckDone[M, M] {
def step[A](k: K[M, A]): K[M, Iteratee[M, A]] = {
case in @ (Input.El(_) | Input.Empty) => new Enumeratee.CheckDone[M, M] { def continue[A](k: K[M, A]) = Cont(step(k)) } &> k(in)
case Input.EOF => Done(Cont(k), Input.EOF)
}
def continue[A](k: K[M, A]) = Cont(step(k))
}
def heading[E](es: Enumerator[E]) = new Enumeratee[E, E] {
def applyOn[A](it: Iteratee[E, A]): Iteratee[E, Iteratee[E, A]] = passAlong[E] &> Iteratee.flatten(es(it))
}
def trailing[M](es: Enumerator[M]) = new Enumeratee.CheckDone[M, M] {
def step[A](k: K[M, A]): K[M, Iteratee[M, A]] = {
case in @ (Input.El(_) | Input.Empty) => new Enumeratee.CheckDone[M, M] { def continue[A](k: K[M, A]) = Cont(step(k)) } &> k(in)
case Input.EOF => Iteratee.flatten((es |>> Cont(k)).map[Iteratee[M, Iteratee[M, A]]](it => Done(it, Input.EOF))(dec))
}
def continue[A](k: K[M, A]) = Cont(step(k))
}
/**
* Create an Enumeratee that performs an action when its Iteratee is done.
*
* @param action The action to perform.
* $paramEcSingle
*/
def onIterateeDone[E](action: () => Unit)(implicit ec: ExecutionContext): Enumeratee[E, E] = new Enumeratee[E, E] {
val pec = ec.prepare()
def applyOn[A](iteratee: Iteratee[E, A]): Iteratee[E, Iteratee[E, A]] = passAlong[E](iteratee).map(_.map { a => action(); a }(pec))(dec)
}
/**
* Create an Enumeratee that performs an action on EOF.
*
* @param action The action to perform.
* $paramEcSingle
*/
def onEOF[E](action: () => Unit)(implicit ec: ExecutionContext): Enumeratee[E, E] = new CheckDone[E, E] {
val pec = ec.prepare()
def step[A](k: K[E, A]): K[E, Iteratee[E, A]] = {
case Input.EOF =>
Iteratee.flatten(Future(action())(pec).map(_ => Done[E, Iteratee[E, A]](Cont(k), Input.EOF))(dec))
case in =>
new CheckDone[E, E] { def continue[A](k: K[E, A]) = Cont(step(k)) } &> k(in)
}
def continue[A](k: K[E, A]) = Cont(step(k))
}
/**
* Create an Enumeratee that recovers an iteratee in Error state.
*
* This will ignore the input that caused the iteratee's error state
* and use the previous state of the iteratee to handle the next input.
*
* {{{
* Enumerator(0, 2, 4) &> Enumeratee.recover { (error, input) =>
* Logger.error(f"oops failure occurred with input: $input", error)
* } &> Enumeratee.map { i =>
* 8 / i
* } |>>> Iteratee.getChunks // => List(4, 2)
* }}}
*
* @param f Called when an error occurs with the cause of the error and the input associated with the error.
* $paramEcSingle
*/
def recover[E](f: (Throwable, Input[E]) => Unit = (_: Throwable, _: Input[E]) => ())(implicit ec: ExecutionContext): Enumeratee[E, E] = {
val pec = ec.prepare()
new Enumeratee[E, E] {
def applyOn[A](it: Iteratee[E, A]): Iteratee[E, Iteratee[E, A]] = {
def step(it: Iteratee[E, A])(input: Input[E]): Iteratee[E, Iteratee[E, A]] = input match {
case in @ (Input.El(_) | Input.Empty) =>
val next: Future[Iteratee[E, Iteratee[E, A]]] = it.pureFlatFold[E, Iteratee[E, A]] {
case Step.Cont(k) =>
val n = k(in)
n.pureFlatFold[E, Iteratee[E, A]] {
case Step.Cont(k) => Cont(step(n))
case _ => Done(n, Input.Empty)
}(dec)
case other => Done(other.it, in)
}(dec).unflatten.map({ s =>
s.it
})(dec).recover({
case NonFatal(e) =>
f(e, in)
Cont(step(it))
})(pec)
Iteratee.flatten(next)
case Input.EOF =>
Done(it, Input.Empty)
}
Cont(step(it))
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy