#[macro_export]
macro_rules! alias_result {
($type_name:ident, $ok_variant:ident, $err_variant:ident) => {
$crate::alias_result!($type_name, $ok_variant, $err_variant, [Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash]);
};
($type_name:ident, $ok_variant:ident, $err_variant:ident, traits: [$($trait:path),*]) => {
$crate::alias_result!($type_name, $ok_variant, $err_variant, [$($trait),*]);
};
($type_name:ident, $ok_variant:ident, $err_variant:ident, implement_try) => {
$crate::alias_result!($type_name, $ok_variant, $err_variant, [Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash], implement_try);
};
($type_name:ident, $ok_variant:ident, $err_variant:ident, traits: [$($trait:path),*], implement_try) => {
$crate::alias_result!($type_name, $ok_variant, $err_variant, [$($trait),*], implement_try);
};
($type_name:ident, $ok_variant:ident, $err_variant:ident, [$($trait:path),*]) => {
$crate::alias_result!($type_name, $ok_variant, $err_variant, [$($trait),*], );
};
($type_name:ident, $ok_variant:ident, $err_variant:ident, [$($trait:path),*], $($implement_try:ident)?) => {
paste::paste! {
#[derive($($trait),*)]
pub enum $type_name<T, E> {
$ok_variant(T),
$err_variant(E),
}
impl<T, E> $type_name<T, E> {
pub fn [<is_ $ok_variant:lower>](&self) -> bool {
matches!(self, $type_name::$ok_variant(_))
}
pub fn [<is_ $err_variant:lower>](&self) -> bool {
matches!(self, $type_name::$err_variant(_))
}
pub fn [<as_ $ok_variant:lower>](&self) -> Option<&T> {
match self {
$type_name::$ok_variant(v) => Some(v),
_ => None,
}
}
pub fn [<as_ $ok_variant:lower _mut>](&mut self) -> Option<&mut T> {
match self {
$type_name::$ok_variant(v) => Some(v),
_ => None,
}
}
pub fn [<as_ $err_variant:lower>](&self) -> Option<&E> {
match self {
$type_name::$err_variant(e) => Some(e),
_ => None,
}
}
pub fn [<as_ $err_variant:lower _mut>](&mut self) -> Option<&mut E> {
match self {
$type_name::$err_variant(e) => Some(e),
_ => None,
}
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> $type_name<U, E> {
match self {
$type_name::$ok_variant(v) => $type_name::$ok_variant(f(v)),
$type_name::$err_variant(e) => $type_name::$err_variant(e),
}
}
pub fn map_err<F, O: FnOnce(E) -> F>(self, op: O) -> $type_name<T, F> {
match self {
$type_name::$ok_variant(v) => $type_name::$ok_variant(v),
$type_name::$err_variant(e) => $type_name::$err_variant(op(e)),
}
}
pub fn unwrap(self) -> T {
match self {
$type_name::$ok_variant(v) => v,
$type_name::$err_variant(_) => {
panic!("called `unwrap()` on an `{}`", stringify!($err_variant))
}
}
}
pub fn unwrap_or(self, default: T) -> T {
match self {
$type_name::$ok_variant(v) => v,
$type_name::$err_variant(_) => default,
}
}
pub fn unwrap_or_else<F: FnOnce(E) -> T>(self, op: F) -> T {
match self {
$type_name::$ok_variant(v) => v,
$type_name::$err_variant(e) => op(e),
}
}
}
impl<T, E> From<Result<T, E>> for $type_name<T, E> {
fn from(result: Result<T, E>) -> Self {
match result {
Ok(v) => $type_name::$ok_variant(v),
Err(e) => $type_name::$err_variant(e),
}
}
}
impl<T, E> From<$type_name<T, E>> for Result<T, E> {
fn from(val: $type_name<T, E>) -> Self {
match val {
$type_name::$ok_variant(v) => Ok(v),
$type_name::$err_variant(e) => Err(e),
}
}
}
}
$(
let _ = stringify!($implement_try);
paste::paste! {
impl<T, E> std::ops::Try for $type_name<T, E> {
type Output = T;
type Residual = $type_name<std::convert::Infallible, E>;
fn from_output(output: Self::Output) -> Self {
$type_name::$ok_variant(output)
}
fn branch(self) -> std::ops::ControlFlow<Self::Residual, Self::Output> {
match self {
$type_name::$ok_variant(v) => std::ops::ControlFlow::Continue(v),
$type_name::$err_variant(e) => std::ops::ControlFlow::Break($type_name::$err_variant(e)),
}
}
}
impl<T, E> std::ops::FromResidual for $type_name<T, E> {
fn from_residual(residual: $type_name<std::convert::Infallible, E>) -> Self {
match residual {
$type_name::$err_variant(e) => $type_name::$err_variant(e),
_ => unreachable!(),
}
}
}
}
)?
};
}
#[cfg(test)]
mod tests {
#[test]
fn size_equivalence() {
use std::num::{NonZeroU32, NonZeroU64};
alias_result!(Response, Success, Failure);
assert_eq!(
std::mem::size_of::<Response<NonZeroU32, String>>(),
std::mem::size_of::<Result<NonZeroU32, String>>()
);
assert_eq!(
std::mem::size_of::<Response<NonZeroU32, NonZeroU64>>(),
std::mem::size_of::<Result<NonZeroU32, NonZeroU64>>()
);
assert_eq!(
std::mem::size_of::<Response<NonZeroU32, i32>>(),
std::mem::size_of::<Result<NonZeroU32, i32>>()
);
}
}