use std::fmt;
use std::ops::{Try, ControlFlow, FromResidual};
use yansi::{Paint, Color};
use self::Outcome::*;
#[must_use]
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub enum Outcome<S, E, F> {
Success(S),
Failure(E),
Forward(F),
}
pub trait IntoOutcome<S, E, F> {
type Failure: Sized;
type Forward: Sized;
fn into_outcome(self, failure: Self::Failure) -> Outcome<S, E, F>;
fn or_forward(self, forward: Self::Forward) -> Outcome<S, E, F>;
}
impl<S, E, F> IntoOutcome<S, E, F> for Option<S> {
type Failure = E;
type Forward = F;
#[inline]
fn into_outcome(self, failure: E) -> Outcome<S, E, F> {
match self {
Some(val) => Success(val),
None => Failure(failure)
}
}
#[inline]
fn or_forward(self, forward: F) -> Outcome<S, E, F> {
match self {
Some(val) => Success(val),
None => Forward(forward)
}
}
}
impl<S, E, F> Outcome<S, E, F> {
#[inline]
pub fn unwrap(self) -> S {
match self {
Success(val) => val,
_ => panic!("Expected a successful outcome!")
}
}
#[inline]
pub fn expect(self, message: &str) -> S {
match self {
Success(val) => val,
_ => panic!("Outcome::expect() failed: {}", message)
}
}
#[inline]
pub fn is_success(&self) -> bool {
match *self {
Success(_) => true,
_ => false
}
}
#[inline]
pub fn is_failure(&self) -> bool {
match *self {
Failure(_) => true,
_ => false
}
}
#[inline]
pub fn is_forward(&self) -> bool {
match *self {
Forward(_) => true,
_ => false
}
}
#[inline]
pub fn succeeded(self) -> Option<S> {
match self {
Success(val) => Some(val),
_ => None
}
}
#[inline]
pub fn failed(self) -> Option<E> {
match self {
Failure(val) => Some(val),
_ => None
}
}
#[inline]
pub fn forwarded(self) -> Option<F> {
match self {
Forward(val) => Some(val),
_ => None
}
}
#[inline]
pub fn success_or<T>(self, value: T) -> Result<S, T> {
match self {
Success(val) => Ok(val),
_ => Err(value)
}
}
#[inline]
pub fn success_or_else<T, V: FnOnce() -> T>(self, f: V) -> Result<S, T> {
match self {
Success(val) => Ok(val),
_ => Err(f())
}
}
#[inline]
pub fn as_ref(&self) -> Outcome<&S, &E, &F> {
match *self {
Success(ref val) => Success(val),
Failure(ref val) => Failure(val),
Forward(ref val) => Forward(val),
}
}
#[inline]
pub fn map<T, M: FnOnce(S) -> T>(self, f: M) -> Outcome<T, E, F> {
match self {
Success(val) => Success(f(val)),
Failure(val) => Failure(val),
Forward(val) => Forward(val),
}
}
#[inline]
pub fn map_failure<T, M: FnOnce(E) -> T>(self, f: M) -> Outcome<S, T, F> {
match self {
Success(val) => Success(val),
Failure(val) => Failure(f(val)),
Forward(val) => Forward(val),
}
}
#[inline]
pub fn map_forward<T, M: FnOnce(F) -> T>(self, f: M) -> Outcome<S, E, T> {
match self {
Success(val) => Success(val),
Failure(val) => Failure(val),
Forward(val) => Forward(f(val)),
}
}
#[inline]
pub fn and_then<T, M: FnOnce(S) -> Outcome<T, E, F>>(self, f: M) -> Outcome<T, E, F> {
match self {
Success(val) => f(val),
Failure(val) => Failure(val),
Forward(val) => Forward(val),
}
}
#[inline]
pub fn failure_then<T, M: FnOnce(E) -> Outcome<S, T, F>>(self, f: M) -> Outcome<S, T, F> {
match self {
Success(val) => Success(val),
Failure(val) => f(val),
Forward(val) => Forward(val),
}
}
#[inline]
pub fn forward_then<T, M: FnOnce(F) -> Outcome<S, E, T>>(self, f: M) -> Outcome<S, E, T> {
match self {
Success(val) => Success(val),
Failure(val) => Failure(val),
Forward(val) => f(val),
}
}
#[inline]
pub fn as_mut(&mut self) -> Outcome<&mut S, &mut E, &mut F> {
match *self {
Success(ref mut val) => Success(val),
Failure(ref mut val) => Failure(val),
Forward(ref mut val) => Forward(val),
}
}
#[inline]
fn formatting(&self) -> (Color, &'static str) {
match *self {
Success(..) => (Color::Green, "Success"),
Failure(..) => (Color::Red, "Failure"),
Forward(..) => (Color::Yellow, "Forward"),
}
}
}
impl<Y, S, E: From<Y>, F> FromResidual<Result<std::convert::Infallible, Y>> for Outcome<S, E, F> {
fn from_residual(r: Result<std::convert::Infallible, Y>) -> Self {
match r {
Ok(never) => match never {},
Err(y) => Outcome::Failure(y.into()),
}
}
}
impl<S, X, E: From<X>, Y, F: From<Y>> FromResidual<Outcome<!, X, Y>> for Outcome<S, E, F> {
fn from_residual(r: Outcome<!, X, Y>) -> Self {
match r {
Outcome::Success(never) => match never {},
Outcome::Failure(x) => Outcome::Failure(x.into()),
Outcome::Forward(y) => Outcome::Forward(y.into()),
}
}
}
impl<A, B, C> Try for Outcome<A, B, C> {
type Output = A;
type Residual = Outcome<!, B, C>;
fn from_output(x: Self::Output) -> Self {
Outcome::Success(x)
}
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
match self {
Outcome::Success(a) => ControlFlow::Continue(a),
Outcome::Forward(b) => ControlFlow::Break(Outcome::Forward(b)),
Outcome::Failure(c) => ControlFlow::Break(Outcome::Failure(c))
}
}
}
impl<S, E, F> fmt::Debug for Outcome<S, E, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Outcome::{}", self.formatting().1)
}
}
impl<S, E, F> fmt::Display for Outcome<S, E, F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (color, string) = self.formatting();
write!(f, "{}", Paint::default(string).fg(color))
}
}
#[cfg(test)]
mod test {
use super::Outcome::{self, *};
macro_rules! fake_try {
($e:block) => {
(||{
std::ops::Try::from_output($e)
})()
}
}
#[test]
fn outcome_try_trait() {
let r: Outcome<u16, String, f64> = fake_try! {{ 3 }};
assert_eq!(r, Success(3));
let r: Outcome<u16, String, f64> = fake_try! {{ Success::<_, &'static str, f32>(3)? }};
assert_eq!(r, Success(3));
let r: Outcome<u16, String, f64> = fake_try! {{ Failure::<u64, _, f32>("oops")?; 7 }};
assert_eq!(r, Failure(String::from("oops")));
let r: Outcome<u16, String, f64> = fake_try! {{ Forward::<u64, &'static str, _>(1234.5_f32)?; 7 }};
assert_eq!(r, Forward(1234.5));
}
#[test]
fn can_use_question_mark_on_result_in_function_returning_outcome() {
fn demo() -> Outcome<i128, String, f32> {
Err("problem")?;
unreachable!()
}
assert_eq!(demo(), Failure(String::from("problem")));
}
}