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

org.specs2.execute.ResultLogicalCombinators.scala Maven / Gradle / Ivy

The newest version!
package org.specs2
package execute

import text.Message._
import ResultLogicalCombinators._
import control.Exceptions._

/**
 * This trait provides logical combinators to Results: and, or, not
 *
 * A Result expression can throw an exception which will be changed into a failure or an error before being used
 * with and/or/not
 */
private[specs2]
trait ResultLogicalCombinators extends Results {

  implicit def combineBoolean(b: =>Boolean): ResultLogicalCombinator = new ResultLogicalCombinator(b)
  implicit def combineResult(r: =>Result)  : ResultLogicalCombinator = new ResultLogicalCombinator(r)

  class ResultLogicalCombinator(res: =>Result) {
    private val result = ResultExecution.executeThrowable(res)
    private val r = result match {
      case Left(r1)  => r1
      case Right(r1) => r1
    }
    /**
     * @return the logical and combination of 2 results
     */
    def and(other: =>Result): Result = {
      lazy val o = ResultExecution.execute(other)
      def combine(result: Result, ifFail: Failure => Failure = identity, ifError: Error => Error = identity) = {
        result match {
          case s @ Success(_,_) =>   {
            o match {
              case Success(m, e)                  => if (r.message == m || r.message.isEmpty) Success(m, concat(s.exp, e), r.expectationsNb + o.expectationsNb)
              else                                     Success(r.message+" and "+m, concat(s.exp, e), r.expectationsNb + o.expectationsNb)
              case DecoratedResult(d, r1)         => DecoratedResult(d, r.and(r1))
              case Failure(_,_,_,_) | Error(_,_)  => o.addExpectationsNb(r.expectationsNb).mapExpected((e: String) => concat(r.expected, e))
              case _                              => r.addExpectationsNb(o.expectationsNb).mapExpected((e: String) => concat(e, o.expected))
            }
          }
          case Pending(_) | Skipped(_,_)     => {
            o match {
              case s @ Success(_,_)       => s
              case f @ Failure(_,_,_,_)   => f
              case e @ Error(_,_)         => e
              case DecoratedResult(d, r1) => DecoratedResult(d, r.and(r1))
              case _                      => o
            }
          }
          case d @ DecoratedResult(_,_)     => {
            o match {
              case DecoratedResult(d2, r2) => {
                val andResult = (d.result and r2)
                if (andResult.isSuccess) DecoratedResult(d.decorator, andResult)
                else                     DecoratedResult(d2, andResult)
              }
              case another                 => DecoratedResult(d.decorator, d.result and another)
            }
          }
          case f @ Failure(_,_,_,_)        => ifFail(f) // re-throw if necessary
          case e @ Error(_,_)              => ifError(e) // re-throw if necessary
        }
      }
      // if the original result threw an exception we must re-throw it
      result match {
        case Left(r1)  => combine(r1, (f:Failure) => throw new FailureException(f), (e:Error) => throw new ErrorException(e))
        case Right(r1) => combine(r1)
      }
    }
    /**
     * @return the logical or combination of 2 results
     */
    def or(other: =>Result): Result = r match {
      case f @ Failure(_,_,_,_) => {
        val o = other
        o match {
          case s @ Success(m, exp)    => if (r.message == m) r.addExpectationsNb(s.expectationsNb)
                                         else Success(r.message+" and "+m, exp, r.expectationsNb + s.expectationsNb)
          case Failure(m, e, st, d)   => Failure(r.message+" and "+m, e, f.stackTrace ::: st, d).addExpectationsNb(r.expectationsNb)
          case DecoratedResult(d, r1) => DecoratedResult(d, r.or(r1))
          case _                      => r.addExpectationsNb(o.expectationsNb).mapExpected((e: String) => concat(e, o.expected))
        }
      }
      case Pending(_) | Skipped(_,_)     => {
        val o = other
        o match {
          case s @ Success(_,_)       => s
          case f @ Failure(_,_,_,_)   => f
          case DecoratedResult(d, r1) => DecoratedResult(d, r.or(r1))
          case _                      => o
        }
      }
      case d @ DecoratedResult(_,_)     => {
        other match {
          case DecoratedResult(d2, r2) => {
            val orResult = (d.result or r2)
            if (orResult.isSuccess) DecoratedResult(d.decorator, orResult)
            else                    DecoratedResult(d2, orResult)
          }
          case o                   => DecoratedResult(d.decorator, d.result or o)
        }
      }
      case _ => r.addExpectationsNb(other.expectationsNb)
    }

    /**
     * @return Success if it is a failure and vice-versa
     */
    def not: Result = r match {
      case Success(m,e)     => Failure(m, e)
      case Failure(m,e,_,_) => Success(m)
      case other            => other
    }

    /** only consider this result if the condition is true */
    def when(b: Boolean, m: String= ""): Result= if (b) res else Success(m)
    /** only consider this result if the condition is false */
    def unless(b: Boolean, m: String= ""): Result = res.when(!b, m)
    /** when the condition is true the result it taken as is, when it's false, take its negation */
    def iff(b: Boolean): Result = if (b) res else res.not
  }
}

object ResultLogicalCombinators extends ResultLogicalCombinators




© 2015 - 2025 Weber Informatics LLC | Privacy Policy