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

com.dadrox.scuttle.Result.scala Maven / Gradle / Ivy

There is a newer version: 0.4
Show newest version
package com.dadrox.scuttle

object Result {
    implicit def failure2Fail[F](failure: F): Fail[F] = Fail(failure)

    object converters {
        implicit def optionToResult[S, F](it: Option[S]) = new {
            def asResult[F1 >: F](fail: Fail[F1]): Result[S, F1] = it match {
                case Some(value) => Success(value)
                case None        => fail
            }
        }
    }
}

// TODO:
//  - Some notion of "tracing" that shows how results are chained? Sorta like stacktraces

/** A response monad that carries detailed failure data.
 *  Sorta like a right-biased Either.
 */
sealed abstract class Result[+S, +F] { self =>
    def name(): String

    final def isSuccess(): Boolean = success.isDefined
    final def isFail(): Boolean = fail.isDefined

    final def success(): Option[S] = toOption

    final def fail(): Option[F] = this match {
        case Success(s) => None
        case Fail(f)    => Some(f)
    }

    final def toOption(): Option[S] = this match {
        case Success(s) => Some(s)
        case Fail(_)    => None
    }

    final def toSeq(): Seq[S] = this match {
        case Success(s) => Seq(s)
        case Fail(_)    => Seq.empty
    }

    final def exists(f: S => Boolean): Boolean = this match {
        case Success(s) => f(s)
        case Fail(_)    => false
    }

    final def forall(f: S => Boolean): Boolean = this match {
        case Success(s) => f(s)
        case Fail(_)    => false
    }

    // like map for the failure
    def rescue[S1 >: S, F1 >: F](rescueFail: PartialFunction[F, S1]): Result[S1, F] = this match {
        case Fail(f) if (rescueFail.isDefinedAt(f)) => Success(rescueFail(f))
        case _                                      => this
    }

    // like flatMap for the failure
    def rescueFlat[S1 >: S, F1 >: F](rescueFail: PartialFunction[F, Result[S1, F1]]): Result[S1, F1] = this match {
        case Fail(f) if (rescueFail.isDefinedAt(f)) => rescueFail(f)
        case _                                      => this
    }

    def filter[F1 >: F](f: S => Boolean)(implicit ev: Fail.Convert[S] => F1): Result[S, F1] = this match {
        case Success(s) => if (f(s)) this else Fail(ev(Fail.Convert(s)))
        case Fail(f)    => Fail(f)
    }

    final def map[S1](f: S => S1): Result[S1, F] = this match {
        case Success(s) => Success(f(s))
        case Fail(f)    => Fail(f)
    }

    final def flatMap[S1, F1 >: F](f: S => Result[S1, F1]): Result[S1, F1] = this match {
        case Success(s) => f(s)
        case Fail(f)    => Fail(f)
    }

    def flatten[S1 >: S, F1 >: F, C](implicit evidence: S1 <:< Result[C, F1]): Result[C, F1] = this match {
        case Success(s) => s
        case Fail(f)    => Fail(f)
    }

    final def foreach[U](f: S => U) { toOption.foreach(f) }

    final def getOrElse[S1 >: S](default: => S1): S1 = toOption.getOrElse(default)

    final def orElse[S1 >: S, F1 >: F](default: => Result[S1, F1]): Result[S1, F1] = this match {
        case Success(s) => this
        case _          => default
    }

    final def onSuccess(f: S => Unit): Result[S, F] = {
        foreach(f)
        this
    }

    final def onFail(f: F => Unit): Result[S, F] = {
        fail.foreach(f)
        this
    }

    final def withFilter[F1 >: F](p: S => Boolean)(implicit ev: Fail.Convert[S] => F1): WithFilter[F1] = new WithFilter(p)

    class WithFilter[F1 >: F](p: S => Boolean)(implicit ev: Fail.Convert[S] => F1) {
        def map[S1](f: S => S1): Result[S1, F1] = self.filter[F1](p)(ev).map(f)
        def flatMap[S1](f: S => Result[S1, F1]): Result[S1, F1] = self.filter[F1](p)(ev).flatMap(f)
        def foreach[U](f: S => U): Unit = self.filter[F1](p)(ev).foreach(f)
        def withFilter(q: S => Boolean): WithFilter[F1] = new WithFilter(x => p(x) && q(x))
    }
}

object Success {
    case class Convert[+S](a: S)
}
final case class Success[+S](value: S) extends Result[S, Nothing] {
    override val name = "Success"
}

object Fail {
    case class Convert[+F](f: F)
}
final case class Fail[+F](failure: F) extends Result[Nothing, F] {
    override val name = "Fail"
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy