pub(crate) const FAILURE_MESSAGE: &str = "failed to allocate";
pub trait Strategy {
type Result<T, E>;
const UNIT: Self;
fn create<T, E>(value: Result<T, E>) -> Self::Result<T, E>;
fn map<T, E, U, F>(value: Self::Result<T, E>, f: F) -> Self::Result<U, E>
where
F: FnOnce(T) -> U;
fn map_err<T, E, V, F>(value: Self::Result<T, E>, f: F) -> Self::Result<T, V>
where
F: FnOnce(E) -> V;
fn and_then<T, E, U, F>(value: Self::Result<T, E>, f: F) -> Self::Result<U, E>
where
F: FnOnce(T) -> Self::Result<U, E>;
fn ok<T, E>(value: T) -> Self::Result<T, E> {
Self::create(Ok(value))
}
fn err<T, E>(error: E) -> Self::Result<T, E> {
Self::create(Err(error))
}
fn inspect<T, E, F>(value: Self::Result<T, E>, f: F) -> Self::Result<T, E>
where
F: FnOnce(&T),
{
Self::and_then(value, |value| {
f(&value);
Self::ok(value)
})
}
}
#[macro_export]
macro_rules! strategy {
(
$(#[$attr:meta])*
$vis:vis struct $name:ident
$( ($($tuple_fields:tt)*) = $tuple_unit:expr; )?
$( {$($struct_fields:tt)*} = $struct_unit:expr; )?
$(;)?
type Result<$t:ident, $e:ident> = $output:ty;
Ok($ok_val:pat) => $ok:expr;
Err($err_val:pat) => $err:expr;
fn map($map_val:pat, $map_f:pat) => $map:expr;
fn map_err($map_err_val:pat, $map_err_f:pat) => $map_err:expr;
fn and_then($and_then_val:pat, $and_then_f:pat) => $and_then:expr;
) => {
$(#[$attr])*
$vis struct $name
$( ($($tuple_fields)*) )?
$( {$($struct_fields)*} const _: () = () )?
;
impl $crate::Strategy for $name {
type Result<$t, $e> = $output;
const UNIT: Self = $crate::__if_else!([
$($tuple_unit)?
] || [
$($struct_unit)?
] else [
Self
] else "must either be a tuple struct or regular struct, not both"
);
fn create<$t, $e>(
__strategy_impl_value: ::core::result::Result<$t, $e>
) -> <Self as $crate::Strategy>::Result<$t, $e> {
match __strategy_impl_value {
::core::result::Result::Ok($ok_val) => $ok,
::core::result::Result::Err($err_val) => $err,
}
}
#[allow(non_camel_case_types)]
fn map<$t, $e, __STRATEGY_IMPL_U, __STRATEGY_IMPL_F>(
$map_val: <Self as $crate::Strategy>::Result<$t, $e>,
$map_f: __STRATEGY_IMPL_F,
) -> <Self as $crate::Strategy>::Result<__STRATEGY_IMPL_U, $e>
where
__STRATEGY_IMPL_F: ::core::ops::FnOnce($t) -> __STRATEGY_IMPL_U,
{
$map
}
#[allow(non_camel_case_types)]
fn map_err<$t, $e, __STRATEGY_IMPL_V, __STRATEGY_IMPL_F>(
$map_err_val: <Self as $crate::Strategy>::Result<$t, $e>,
$map_err_f: __STRATEGY_IMPL_F,
) -> <Self as $crate::Strategy>::Result<$t, __STRATEGY_IMPL_V>
where
__STRATEGY_IMPL_F: core::ops::FnOnce($e) -> __STRATEGY_IMPL_V,
{
$map_err
}
#[allow(non_camel_case_types)]
fn and_then<$t, $e, __STRATEGY_IMPL_U, __STRATEGY_IMPL_F>(
$and_then_val: <Self as $crate::Strategy>::Result<$t, $e>,
$and_then_f: __STRATEGY_IMPL_F,
) -> <Self as $crate::Strategy>::Result<__STRATEGY_IMPL_U, $e>
where
__STRATEGY_IMPL_F: core::ops::FnOnce(T)
-> <Self as $crate::Strategy>::Result<__STRATEGY_IMPL_U, $e>,
{
$and_then
}
}
}
}
fn abort() -> ! {
unsafe {
core::arch::asm!("ud2");
core::hint::unreachable_unchecked()
}
}
strategy! {
pub struct Abort;
type Result<T, E> = T;
Ok(v) => v;
Err(_) => abort();
fn map(v, f) => f(v);
fn map_err(v, _) => v;
fn and_then(v, f) => f(v);
}
strategy! {
pub struct Panic;
type Result<T, E> = T;
Ok(v) => v;
Err(_) => panic!("{}", FAILURE_MESSAGE);
fn map(v, f) => f(v);
fn map_err(v, _) => v;
fn and_then(v, f) => f(v);
}
strategy! {
pub struct Optional;
type Result<T, E> = Option<T>;
Ok(v) => Some(v);
Err(_) => None;
fn map(v, f) => v.map(f);
fn map_err(v, _) => v;
fn and_then(v, f) => v.and_then(f);
}
strategy! {
pub struct Fallible;
type Result<T, E> = Result<T, E>;
Ok(v) => Ok(v);
Err(e) => Err(e);
fn map(v, f) => v.map(f);
fn map_err(v, f) => v.map_err(f);
fn and_then(v, f) => v.and_then(f);
}
#[macro_export]
#[doc(hidden)]
macro_rules! __if_else {
([] || [] else [$($t:tt)*] else $err:literal) => {$($t)*};
([] || [$($t:tt)*] else [$($_:tt)*] else $err:literal) => {$($t)*};
([$($t:tt)*] || [] else [$($_:tt)*] else $err:literal) => {$($t)*};
([$($_:tt)*] || [$($__:tt)*] else [$($___:tt)*] else $err:literal) => {
compile_error!($err);
};
([] else [$($t:tt)*]) => {$($t)*};
([$($t:tt)*] else [$($_:tt)*]) => {$($t)*};
}