use crate::prelude::*;
#[cfg(feature = "std")]
type DefaultErr = String;
#[cfg(not(feature = "std"))]
type DefaultErr = ();
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, QuickCheck)]
pub enum Hazard<A: Clone, E: Clone = DefaultErr> {
Failure(E),
Success(A),
}
pub use Hazard::{Failure, Success};
monad! {
Hazard<A, E: Clone>:
fn consume(b) {
Success(b)
}
fn bind(self, f) {
match self {
Success(a) => f(a),
Failure(e) => Failure(e),
}
}
}
#[cfg(feature = "nightly")]
type Residual<E> = Hazard<core::convert::Infallible, E>;
#[cfg(feature = "nightly")]
impl<A: Clone, E: Clone> core::ops::Try for Hazard<A, E> {
type Output = A;
type Residual = Residual<E>;
#[inline]
fn from_output(a: A) -> Self {
consume(a)
}
#[inline]
fn branch(self) -> core::ops::ControlFlow<Residual<E>, A> {
match self {
Success(a) => core::ops::ControlFlow::Continue(a),
Failure(s) => core::ops::ControlFlow::Break(Failure(s)),
}
}
}
#[cfg(feature = "nightly")]
impl<A: Clone, E: Clone> core::ops::FromResidual<Residual<E>> for Hazard<A, E> {
#[inline]
#[track_caller]
fn from_residual(r: Residual<E>) -> Self {
match r {
Failure(s) => Failure(s),
Success(_) => unsafe { core::hint::unreachable_unchecked() },
}
}
}
#[cfg(all(test, feature = "nightly"))]
mod nightly_tests {
use super::*;
fn should_short_circuit() -> Hazard<()> {
Failure("Intentional short-circuit".to_owned())?;
Success(())
}
#[test]
fn hazard_question_mark() {
match should_short_circuit() {
Success(()) => panic!("Didn't exit early!"),
Failure(s) => assert_eq!(s, "Intentional short-circuit"),
}
}
}