#![no_std]
#![deny(
missing_docs,
missing_copy_implementations,
missing_debug_implementations,
unsafe_code
)]
#[doc(hidden)]
pub extern crate core as _core;
use core::any::Any;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct InvalidType {
pub expected_variant: &'static str,
pub actual_variant: &'static str,
pub all_variants: &'static [&'static str],
#[doc(hidden)]
pub __non_exhaustive: (),
}
pub trait SumType {
fn variant(&self) -> &'static str;
fn variants(&self) -> &'static [&'static str];
fn downcast_ref<T: Any>(&self) -> Option<&T>;
fn downcast_mut<T: Any>(&mut self) -> Option<&mut T>;
fn variant_is<T: Any>(&self) -> bool;
}
#[doc(hidden)]
#[macro_export]
macro_rules! __sum_type_try_from {
($enum_name:ident, $( $name:ident => $variant_type:ty ),*) => {
$(
impl $crate::_core::convert::TryFrom<$enum_name> for $variant_type {
type Error = $crate::InvalidType;
fn try_from(other: $enum_name) -> Result<$variant_type, Self::Error> {
let variant = $crate::SumType::variant(&other);
let variants = $crate::SumType::variants(&other);
if let $enum_name::$name(value) = other {
Ok(value)
} else {
Err($crate::InvalidType {
expected_variant: stringify!($name),
actual_variant: variant,
all_variants: variants,
__non_exhaustive: (),
})
}
}
}
)*
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __sum_type_from {
($enum_name:ident, $( $name:ident => $variant_type:ty ),*) => {
$(
impl From<$variant_type> for $enum_name {
fn from(other: $variant_type) -> $enum_name {
$enum_name::$name(other)
}
}
)*
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __sum_type_trait {
($enum_name:ident, $( $name:ident => $variant_type:ty ),*) => {
impl $crate::SumType for $enum_name {
fn variants(&self) -> &'static [ &'static str] {
&[
$( stringify!($name) ),*
]
}
fn variant(&self) -> &'static str {
match *self {
$(
$enum_name::$name(_) => stringify!($name),
)*
}
}
fn downcast_ref<T: $crate::_core::any::Any>(&self) -> Option<&T> {
use $crate::_core::any::Any;
match *self {
$(
$enum_name::$name(ref value) => (value as &Any).downcast_ref::<T>(),
)*
}
}
fn downcast_mut<T: $crate::_core::any::Any>(&mut self) -> Option<&mut T> {
use $crate::_core::any::Any;
match *self {
$(
$enum_name::$name(ref mut value) => (value as &mut Any).downcast_mut::<T>(),
)*
}
}
fn variant_is<T: $crate::_core::any::Any>(&self) -> bool {
self.downcast_ref::<T>().is_some()
}
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __assert_multiple_variants {
($enum_name:ident, $name:ident => $variant_type:ty) => {
compile_error!(concat!(
"The `",
stringify!($enum_name),
"` type must have more than one variant"
));
};
($enum_name:ident, $( $name:ident => $variant_type:ty ),*) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __sum_type_impls {
($enum_name:ident, $( $name:ident => $variant_type:ty ),*) => (
$crate::__assert_multiple_variants!($enum_name, $( $name => $variant_type ),*);
$crate::__sum_type_from!($enum_name, $($name => $variant_type),*);
$crate::__sum_type_try_from!($enum_name, $($name => $variant_type),*);
$crate::__sum_type_trait!($enum_name, $($name => $variant_type),*);
)
}
#[macro_export]
macro_rules! sum_type {
(
$( #[$outer:meta] )*
pub enum $name:ident {
$(
$( #[$inner:meta] )*
$var_name:ident($var_ty:ty),
)*
}) => {
$( #[$outer] )*
pub enum $name {
$(
$( #[$inner] )*
$var_name($var_ty),
)*
}
$crate::__sum_type_impls!($name, $( $var_name => $var_ty),*);
};
(
$( #[$outer:meta] )*
enum $name:ident {
$(
$( #[$inner:meta] )*
$var_name:ident($var_ty:ty),
)*
}) => {
$( #[$outer] )*
enum $name {
$(
$( #[$inner] )*
$var_name($var_ty),
)*
}
$crate::__sum_type_impls!($name, $( $var_name => $var_ty),*);
};
(
$( #[$outer:meta] )*
pub enum $name:ident {
$(
$( #[$inner:meta] )*
$var_name:ident,
)*
}) => {
$crate::sum_type!($(#[$outer])* pub enum $name { $( $(#[$inner])* $var_name($var_name), )* });
};
(
$( #[$outer:meta] )*
enum $name:ident {
$(
$( #[$inner:meta] )*
$var_name:ident($var_ty:ty),
)*
}) => {
$crate::sum_type!($(#[$outer])* enum $name { $( $(#[$inner])* $var_name($var_name), )* });
};
}
#[macro_export]
macro_rules! defer {
($kind:ident as $variable:expr; $( $variant:ident )|* => |ref $item:ident| $exec:expr) => {
$crate::defer!(@foreach_variant $kind, $variable;
$(
$kind::$variant(ref $item) => $exec
),*
)
};
($kind:ident as $variable:expr; $( $variant:ident )|* => |ref mut $item:ident| $exec:expr) => {
$crate::defer!(@foreach_variant $kind, $variable;
$(
$kind::$variant(ref mut $item) => $exec
),*
)
};
(@foreach_variant $kind:ident, $variable:expr; $( $pattern:pat => $exec:expr ),*) => {
match $variable {
$(
$pattern => $exec,
)*
#[allow(unreachable_patterns)]
_ => unreachable!("Unexpected variant, {}, for {}",
<_ as $crate::SumType>::variant(&$variable),
stringify!($kind)),
}
}
}
#[cfg(feature = "generated_example")]
#[allow(missing_docs)]
pub mod generated_example {
sum_type! {
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum MySumType {
First(u32),
Second(&'static str),
Third(&'static [u8]),
}
}
}