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

tofu.concurrent.Exit.scala Maven / Gradle / Ivy

package tofu.concurrent

import cats.{Applicative, Eval, Traverse}
import tofu.control.ApplicativeZip
import tofu.syntax.monadic._
import scala.util.Try
import tofu.concurrent.Exit.Canceled
import tofu.concurrent.Exit.Completed
import tofu.concurrent.Exit.Error

sealed trait Exit[+E, +A] {
  def toTry(implicit ev: E <:< Throwable): Try[A] = this match {
    case Canceled     => util.Failure(new InterruptedException)
    case Error(e)     => util.Failure(e)
    case Completed(a) => util.Success(a)
  }

  def toEither: Either[Option[E], A] = this match {
    case Canceled     => Left(None)
    case Error(e)     => Left(Some(e))
    case Completed(a) => Right(a)
  }
}

object Exit {

  sealed trait Incomplete[+E] extends Exit[E, Nothing]

  case object Canceled                 extends Incomplete[Nothing]
  final case class Error[+E](e: E)     extends Incomplete[E]
  final case class Completed[+A](a: A) extends Exit[Nothing, A]

  def fromEither[E, A](e: Either[E, A]): Exit[E, A] = e match {
    case Left(err)  => Error(err)
    case Right(res) => Completed(res)
  }

  def fromTry[A](t: util.Try[A]): Exit[Throwable, A] = t match {
    case util.Failure(ex)  => Error(ex)
    case util.Success(res) => Completed(res)
  }

  private[this] object exitInstanceAny extends Traverse[Exit[Any, _]] with ApplicativeZip[Exit[Any, _]] {
    def traverse[G[_], A, B](fa: Exit[Any, A])(f: A => G[B])(implicit G: Applicative[G]): G[Exit[Any, B]] =
      fa match {
        case Canceled     => G.pure(Canceled)
        case Error(e)     => G.pure(Error(e))
        case Completed(a) => f(a).map(Completed(_))
      }
    def foldLeft[A, B](fa: Exit[Any, A], b: B)(f: (B, A) => B): B                                         =
      fa match {
        case Canceled | Error(_) => b
        case Completed(a)        => f(b, a)
      }
    def foldRight[A, B](fa: Exit[Any, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B]               =
      fa match {
        case Canceled | Error(_) => lb
        case Completed(a)        => f(a, lb)
      }
    override def map[A, B](fa: Exit[Any, A])(f: A => B): Exit[Any, B]                                     =
      fa match {
        case Canceled      => Canceled
        case e: Error[Any] => e
        case Completed(a)  => Completed(f(a))
      }

    def zipWith[A, B, C](fa: Exit[Any, A], fb: Exit[Any, B])(f: (A, B) => C): Exit[Any, C] =
      fa match {
        case Canceled        => Canceled
        case err: Error[Any] => err
        case Completed(a)    =>
          fb match {
            case Canceled        => Canceled
            case err: Error[Any] => err
            case Completed(b)    => Completed(f(a, b))
          }
      }
    def pure[A](x: A): Exit[Any, A]                                                        = Completed(x)
  }

  implicit def exitInstance[E]: Traverse[Exit[E, _]] with Applicative[Exit[E, _]] =
    exitInstanceAny.asInstanceOf[Traverse[Exit[E, _]] with Applicative[Exit[E, _]]]

  object CanceledException extends InterruptedException
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy