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

parsley.Result.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020 Parsley Contributors 
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
package parsley

import scala.annotation.nowarn
import scala.util.{Failure => TFailure, Success => TSuccess, Try}

/** This trait represents the result of a parser.
  *
  * Either a `Success[A]` or a `Failure`.
  *
  * @tparam A the type of expected success result.
  */
sealed abstract class Result[+Err, +A] {
    /** Returns the result of applying `ferr` to this result's error if this is a `Failure` or `fa` to the result stored in the `Success` otherwise.
      *
      * @param ferr the function to apply if this is a `Failure`.
      * @param fa the function to apply if this is a `Success`.
      * @return the results of applying the function
      * @since 1.7.0
      */
    def fold[B](ferr: Err => B, fa: A => B): B = this match {
        case Success(x)   => fa(x)
        case Failure(msg) => ferr(msg)
    }

    /** Executes the procedure `f` if this is a `Success`. Otherwise, do nothing.
      *
      * This is equivalent to:
      * {{{
      * result match {
      *   case Success(x) => f(x)
      *   case _          => ()
      * }
      * }}}
      *
      * @param f The side-effecting function to execute.
      * @since 1.7.0
      */
    def foreach[U](f: A => U): Unit = this match {
        case Success(x) => f(x): @nowarn
        case _          => ()
    }

    /** Returns the successful value within the result.
      *
      * This is equivalent to:
      * {{{
      * result match {
      *   case Success(x) => x
      *   case _          => throw new Exception
      * }
      * }}}
      *
      * @note the result must not be a failure.
      * @throws java.util.NoSuchElementException if the result is a failure.
      * @since 1.7.0
      */
    def get: A

    /** Returns the value from this `Success` or the result of evaluating `default` if this is a `Failure`.
      *
      * @since 1.7.0
      */
    def getOrElse[B >: A](default: =>B): B = orElse(Success(default)).get

    /** Returns this result if it is a `Success`, otherwise return the result of evaluating `alternative`.
      *
      * @since 1.7.0
      */
    def orElse[B >: A, Errʹ >: Err](alternative: =>Result[Errʹ, B]): Result[Errʹ, B] = this match {
        case Success(_) => this
        case _          => alternative
    }

    /** Returns `true` if this result is a `Success` and its value is equal to `elem` (as determined by `==`),
      * returns `false` otherwise.
      *
      * @param elem    the element to test.
      * @return `true` if this is a `Success` value equal to `elem`.
      * @since 1.7.0
      */
    def contains[B >: A](elem: B): Boolean = exists(_ == elem)

    /** Returns `true` if this result is a `Failure` or returns the result of the application of
      * the given predicate to the `Success` value.
      *
      * @since 1.7.0
      */
    def forall(f: A => Boolean): Boolean = this match {
        case Success(x) => f(x)
        case _          => true
    }

    /** Returns `false` if `Failure` or returns the result of the application of
      * the given predicate to the `Success` value.
      *
      * @since 1.7.0
      */
    def exists(p: A => Boolean): Boolean = this match {
        case Success(x) => p(x)
        case _          => false
    }

    /** Returns the result of applying `f` to this result if it is a success. Returns
      * a failure if this result is a failure. Differs from `map` as `f` returns a result
      * instead of just a value.
      *
      * @since 1.7.0
      */
    def flatMap[B, Errʹ >: Err](f: A => Result[Errʹ, B]): Result[Errʹ, B] = this match {
        case Success(x) => f(x)
        case _          => this.asInstanceOf[Result[Err, B]]
    }

    /** Returns the nested result if this result is a success, otherwise return this failure.
      *
      * Equivalent to `flatMap(identity[Result[Errʹ, B]])`.
      *
      * @since 1.7.0
      */
    def flatten[B, Errʹ >: Err](implicit ev: A <:< Result[Errʹ, B]): Result[Errʹ, B] = flatMap(ev)

    /** Returns a `Success` containing the result of applying `f` to this result's value if
      * this is a success. Otherwise, returns a failure.
      *
      * @since 1.7.0
      */
    def map[B](f: A => B): Result[Err, B] = this match {
        case Success(x) => Success(f(x))
        case _          => this.asInstanceOf[Result[Err, B]]
    }

    /** Returns `Success` with the existing value of `Success` if this is a `Success`
      * and the given predicate `p` holds for the right value,
      * or `Failure(msg)` if this is a `Success` and the given predicate `p` does not hold for the right value,
      * or `Failure` with the existing value of `Failure` if this is a `Failure`.
      *
      * @since 1.7.0
      */
    def filterOrElse[Errʹ >: Err](p: A => Boolean, msg: =>Errʹ): Result[Errʹ, A] = this match {
        case Success(x) if !p(x) => Failure(msg)
        case _                   => this
    }

    /** Returns a `Seq` containing the `Success` value if it exists or an empty `Seq` if this is a `Failure`.
      *
      * @since 1.7.0
      */
    def toSeq: Seq[A] = this match {
        case Success(x) => Seq(x)
        case _          => Seq.empty
    }

    /** Returns a `Some` containing the `Success` value if it exists or a `None` if this is a `Failure`.
      *
      * @since 1.7.0
      */
    def toOption: Option[A] = this match {
        case Success(x) => Some(x)
        case _          => None
    }

    /** Converts the `Result` into a `Try` where `Failure` maps to a plain `Exception`.
      *
      * @since 1.7.0
      */
    def toTry: Try[A] = this match {
        case Success(x)   => TSuccess(x)
        case Failure(msg) => TFailure(new Exception(s"ParseError: $msg"))
    }

    /** Converts the `Result` into a `Either` where `Failure` maps to a `Left[Err]`.
      *
      * @since 1.7.0
      */
    def toEither: Either[Err, A] = this match {
        case Success(x)   => Right(x)
        case Failure(msg) => Left(msg)
    }

    /** Returns `true` if this is a `Success`, `false` otherwise.
      *
      * @since 1.7.0
      */
    def isSuccess: Boolean

    /** Returns `true` if this is a `Failure`, `false` otherwise.
      *
      * @since 1.7.0
      */
    def isFailure: Boolean
}

/** This class is used for when a parser succeeds, and contains its result.
  *
  * @param x the result value of the successful parse.
  * @tparam A the type of expected success result.
  */
final case class Success[A](x: A) extends Result[Nothing, A] {
    /** @inheritdoc */
    override def isSuccess: Boolean = true
    /** @inheritdoc */
    override def isFailure: Boolean = false
    /** @inheritdoc */
    override def get: A = x
}

/** This class is used for a parser failure, and contains the error message.
  *
  * @tparam Err the type of the error message generated by the failing parse.
  * @param msg the error message reported by the parser.
  */
final case class Failure[Err](msg: Err) extends Result[Err, Nothing] {
    /** @inheritdoc */
    override def isSuccess: Boolean = false
    /** @inheritdoc */
    override def isFailure: Boolean = true
    /** @inheritdoc */
    override def get: Nothing = throw new NoSuchElementException("get called on Failure") // scalastyle:ignore throw
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy