org.specs2.control.package.scala Maven / Gradle / Ivy
package org.specs2
import control.eff._, all._, syntax.all._
import scalaz._
import scalaz.effect.IO
import org.specs2.execute.{AsResult, Result}
import scalaz.concurrent.Task
import scalaz.syntax.bind._
import ErrorEffect.{ErrorOrOk, Error, fail, exception}
package object control {
/**
* Actions logging
*/
type Logger = String => Unit
lazy val noLogging = (s: String) => ()
lazy val consoleLogging = (s: String) => println(s)
/**
* Action type, using a logger as a reader and no writer
*/
type ActionStack = ErrorOrOk |: Console |: Warnings |: Eval |: NoEffect
implicit def EvalMember: Member.Aux[Eval, ActionStack, ErrorOrOk |: Console |: Warnings |: NoEffect] =
Member.successor
implicit def WarningsMember: Member.Aux[Warnings, ActionStack, ErrorOrOk |: Console |: Eval |: NoEffect] =
Member.successor
implicit def ConsoleMember: Member.Aux[Console, ActionStack, ErrorOrOk |: Warnings |: Eval |: NoEffect] =
Member.successor
implicit def ErrorMember: Member.Aux[ErrorOrOk, ActionStack, Console |: Warnings |: Eval |: NoEffect] =
Member.first
type Action[A] = Eff[ActionStack, A]
/**
* warn the user about something that is probably wrong on his side,
* this is not a specs2 bug, then fail to stop all further computations
*/
def warnAndFail[R <: Effects, A](message: String, failureMessage: String)(implicit m1: Warnings <= R, m2: ErrorOrOk <= R): Eff[R, A] =
warn(message)(m1) >>
fail(failureMessage)
def executeAction[A](action: Eff[ActionStack, A], printer: String => Unit = s => ()): (Error \/ A, List[String]) =
action.runError.runConsoleToPrinter(printer).runWarnings.runEval.run
def runAction[A](action: Eff[ActionStack, A], printer: String => Unit = s => ()): Error \/ A =
attemptExecuteAction(action, printer).fold(
t => -\/(-\/(t)),
other => other._1)
def attemptExecuteAction[A](action: Eff[ActionStack, A], printer: String => Unit = s => ()): Throwable \/ (Error \/ A, List[String]) =
action.runError.runConsoleToPrinter(printer).runWarnings.attemptEval.run
/**
* This implicit allows any IO[Result] to be used inside an example:
*
* "this should work" in {
* IO(success)
* }
*/
implicit def ioResultAsResult[T : AsResult]: AsResult[IO[T]] = new AsResult[IO[T]] {
def asResult(io: =>IO[T]) = AsResult(io.unsafePerformIO())
}
/**
* This implicit allows an Action[result] to be used inside an example.
*
* For example to read a database.
*/
implicit def actionAsResult[T : AsResult]: AsResult[Action[T]] = new AsResult[Action[T]] {
def asResult(action: =>Action[T]): Result =
runAction(action).fold(
err => err.fold(t => org.specs2.execute.Error(t), f => org.specs2.execute.Failure(f)),
ok => AsResult(ok)
)
}
/**
* An Action[T] can be converted to a Task[T]
*/
implicit class actionToTask[T](action: Action[T]) {
def toConsoleTask =
action.toTask(println)
def toTask(logger: String => Unit) =
Task.delay(executeAction(action)).
flatMap { case (result, warnings) =>
result.fold(
error => error.fold(
t => Task.fail(ActionException(warnings, None, Some(t))),
s => Task.fail(ActionException(warnings, Some(s), None))),
t => Task.delay(warnings.foreach(w => logger(w+"\n"))) >> Task.now(t))
}
}
implicit class actionOps[T](action: Action[T]) {
def when(condition: Boolean): Action[Unit] =
if (condition) action.as(()) else Actions.ok(())
def unless(condition: Boolean): Action[Unit] =
action.when(!condition)
def whenFailed(error: Error => Action[T]): Action[T] =
Actions.whenFailed(action, error)
def |||(other: Action[T]): Action[T] =
Actions.orElse(action, other)
def orElse(other: Action[T]): Action[T] =
Actions.orElse(action, other)
}
/**
* execute an action with no logging and return an option
*/
implicit class ioActionToOption[T](action: Action[T]) {
def runOption = runAction(action).toOption
}
/**
* A Task[T] (the result of running a Process[Task, T] for example) can be converted to
* an Action[T]
*/
implicit class taskToAction[T](task: Task[T]) {
def toAction: Action[T] =
task.attemptRun.fold(t => exception(t), a => ErrorEffect.ok(a))
def runOption: Option[T] =
task.get.run.toOption
}
object Actions {
def unit: Action[Unit] =
ok(())
def ok[A](a: A): Action[A] =
ErrorEffect.ok(a)
def safe[A](a: =>A): Action[A] =
ErrorEffect.ok(a)
def fail[A](message: String): Action[A] =
ErrorEffect.fail[ActionStack, A](message)
def exception[A](t: Throwable): Action[A] =
ErrorEffect.exception[ActionStack, A](t)
def checkThat[A](a: =>A, condition: Boolean, failureMessage: String): Action[A] =
safe(a).flatMap { value =>
if (condition) safe(value)
else fail(failureMessage)
}
def fromTask[A](task: Task[A]): Action[A] =
task.toAction
def fromError[A](error: ErrorEffect.Error): Action[A] =
ErrorEffect.error(error)
def orElse[A](action1: Action[A], action2: Action[A]): Action[A] =
ErrorEffect.orElse(action1, action2)
def whenFailed[A](action: Action[A], onError: Error => Action[A]): Action[A] =
ErrorEffect.whenFailed(action, onError)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy