#[macro_export]
macro_rules! alias_option {
($type_name:ident, $some_variant:ident, $none_variant:ident) => {
$crate::alias_option!($type_name, $some_variant, $none_variant, [Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash]);
};
($type_name:ident, $some_variant:ident, $none_variant:ident, traits: [$($trait:path),*]) => {
$crate::alias_option!($type_name, $some_variant, $none_variant, [$($trait),*]);
};
($type_name:ident, $some_variant:ident, $none_variant:ident, implement_try) => {
$crate::alias_option!($type_name, $some_variant, $none_variant, [Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash], implement_try);
};
($type_name:ident, $some_variant:ident, $none_variant:ident, traits: [$($trait:path),*], implement_try) => {
$crate::alias_option!($type_name, $some_variant, $none_variant, [$($trait),*], implement_try);
};
($type_name:ident, $some_variant:ident, $none_variant:ident, [$($trait:path),*]) => {
$crate::alias_option!($type_name, $some_variant, $none_variant, [$($trait),*], );
};
($type_name:ident, $some_variant:ident, $none_variant:ident, [$($trait:path),*], $($implement_try:ident)?) => {
paste::paste! {
#[derive($($trait),*)]
pub enum $type_name<T> {
$none_variant,
$some_variant(T),
}
impl<T> $type_name<T> {
pub fn [<is_ $none_variant:lower>](&self) -> bool {
matches!(self, $type_name::$none_variant)
}
pub fn [<is_ $some_variant:lower>](&self) -> bool {
matches!(self, $type_name::$some_variant(_))
}
pub fn [<is_ $some_variant:lower _and>]<F: FnOnce(&T) -> bool>(&self, f: F) -> bool {
match self {
$type_name::$some_variant(v) => f(v),
_ => false,
}
}
pub fn [<is_ $none_variant:lower _or>]<F: FnOnce(&T) -> bool>(&self, f: F) -> bool {
match self {
$type_name::$none_variant => true,
$type_name::$some_variant(v) => f(v),
}
}
pub fn [<as_ $some_variant:lower>](&self) -> Option<&T> {
match self {
$type_name::$some_variant(v) => Some(v),
_ => None,
}
}
pub fn [<as_ $some_variant:lower _mut>](&mut self) -> Option<&mut T> {
match self {
$type_name::$some_variant(v) => Some(v),
_ => None,
}
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> $type_name<U> {
match self {
$type_name::$some_variant(v) => $type_name::$some_variant(f(v)),
$type_name::$none_variant => $type_name::$none_variant,
}
}
pub fn unwrap(self) -> T {
match self {
$type_name::$some_variant(v) => v,
$type_name::$none_variant => {
panic!("called `unwrap()` on a `{}`", stringify!($none_variant))
}
}
}
pub fn unwrap_or(self, default: T) -> T {
match self {
$type_name::$some_variant(v) => v,
$type_name::$none_variant => default,
}
}
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
$type_name::$some_variant(v) => v,
$type_name::$none_variant => f(),
}
}
}
impl<T> From<Option<T>> for $type_name<T> {
fn from(opt: Option<T>) -> Self {
match opt {
Some(v) => $type_name::$some_variant(v),
None => $type_name::$none_variant,
}
}
}
impl<T> From<$type_name<T>> for Option<T> {
fn from(val: $type_name<T>) -> Self {
match val {
$type_name::$some_variant(v) => Some(v),
$type_name::$none_variant => None,
}
}
}
}
$(
let _ = stringify!($implement_try);
paste::paste! {
impl<T> std::ops::Try for $type_name<T> {
type Output = T;
type Residual = $type_name<std::convert::Infallible>;
fn from_output(output: Self::Output) -> Self {
$type_name::$some_variant(output)
}
fn branch(self) -> std::ops::ControlFlow<Self::Residual, Self::Output> {
match self {
$type_name::$some_variant(v) => std::ops::ControlFlow::Continue(v),
$type_name::$none_variant => std::ops::ControlFlow::Break($type_name::$none_variant),
}
}
}
impl<T> std::ops::FromResidual for $type_name<T> {
fn from_residual(_: $type_name<std::convert::Infallible>) -> Self {
$type_name::$none_variant
}
}
}
)?
}
}
#[cfg(test)]
mod tests {
#[test]
fn size_equivalence() {
use std::num::NonZeroU32;
alias_option!(Value, Found, Searching);
assert_eq!(
std::mem::size_of::<Value<i32>>(),
std::mem::size_of::<Option<i32>>()
);
assert_eq!(
std::mem::size_of::<Value<NonZeroU32>>(),
std::mem::size_of::<Option<NonZeroU32>>()
);
}
}