All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalaz.stream.Cause.scala Maven / Gradle / Ivy

The newest version!
package scalaz.stream

import scalaz.\/
import scalaz.stream.Process.Halt
import Cause._

/**
 * Defines termination cause for the process.
 * Cause is always wrapped in `Halt` and controls process flow.
 */
sealed trait Cause {
  /**
   * Produces a cause that was caused by `cause`
   * @param cause
   * @return
   */
  def causedBy(cause: Cause): Cause = {
    (this, cause) match {
      case (End, End)                 => End
      case (End, Kill)                => Kill
      case (Kill, End | Kill)         => Kill
      case (End | Kill, err@Error(_)) => err
      case (err@Error(_), End | Kill) => err
      case (Error(rsn1), Error(rsn2)) if rsn1 == rsn2 => this
      case (Error(rsn1), Error(rsn2)) => Error(CausedBy(rsn1, rsn2))
    }
  }

  /**
   * Converts cause to `Kill` or an `Error`
   * @return
   */
  def kill: EarlyCause = fold[EarlyCause](Kill)(identity)

  def fold[A](onEnd: => A)(f:(EarlyCause => A)) = this match {
    case End => onEnd
    case early:EarlyCause => f(early)
  }

  /**
   * Converts this termination cause to `Process.Halt`
   */
  def asHalt: Halt = this match {
    case End => Halt(End)
    case Error(Terminated(cause)) => Halt(cause)
    case cause => Halt(cause)
  }

  /**
   * Converts this cause to `java.lang.Throwable`
   */
  def asThrowable: Throwable = this match {
    case End => Terminated(End)
    case Kill => Terminated(Kill)
    case Error(rsn) => rsn
  }
}

object Cause {

  /**
   * A marker that is indicating Cause was terminating the stream EarlyCause,
   * either due to error, or being killed
   */
  sealed trait EarlyCause extends Cause

  object EarlyCause {
    def fromTaskResult[A](r:Throwable\/A):EarlyCause\/A =
      r.bimap(Error.apply,identity)
  }


  /**
   * Process terminated normally due to End of input.
   * That means items from Emit have been exhausted.
   */
  case object End extends Cause



  /**
   * Signals force-full process termination.
   * Process can be killed when merged (pipe,tee,wye,njoin) and other merging stream or
   * resulting downstream requested termination of process.
   * This shall cause process to run all cleanup actions and then terminate normally
   */
  case object Kill extends EarlyCause

  /**
   * Signals, that evaluation of last await resulted in error.
   *
   * If error is not handled, this will cause the process to terminate with supplier error.
   *
   * @param rsn Error thrown by last await.
   *
   */
  case class Error(rsn: Throwable) extends EarlyCause {
    override def toString: String = {
      s"Error(${rsn.getClass.getName}: ${rsn.getMessage}})"
    }
  }


  /**
   * Wrapper for Exception that was caused by other Exception during the
   * Execution of the Process
   */
  case class CausedBy(e: Throwable, cause: Throwable) extends Exception(cause) {
    override def toString = s"$e caused by: $cause"
    override def getMessage: String = toString
    override def fillInStackTrace(): Throwable = this
  }

  /**
   * wrapper to signal cause for termination.
   * This is useful when cause needs to be propagated out of process domain (i.e. Task)
   */
  case class Terminated(cause:Cause) extends Exception {
    override def fillInStackTrace(): Throwable = cause match {
      case End | Kill => this
      case Error(rsn) => rsn
    }
    override def toString: String = s"Terminated($cause)"
    override def getMessage: String = cause.toString
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy