#[macro_export]
macro_rules! impl_enum_from {
(<$($generic:ident),+> in $($rest:tt)+) => {
$crate::impl_enum_from!(@impl [$($generic),+] $($rest)+);
};
(<const $($rest:tt)+) => {
$crate::__impl_more_parse_generics!(@parse impl_enum_from [] [] const $($rest)+);
};
(<$generic:ident, $($rest:tt)+) => {
$crate::__impl_more_parse_generics!(@parse impl_enum_from [] [] $generic, $($rest)+);
};
(@impl [$($generic:tt)+] $from:ty => $($target:tt)+) => {
$crate::impl_enum_from!(@parse [<$($generic)+>] [$from] [] $($target)+);
};
($from:ty => $($target:tt)+) => {
$crate::impl_enum_from!(@parse [] [$from] [] $($target)+);
};
(@parse [$($generic:tt)*] [$from:ty] [$($enum:tt)+]
:: $variant:ident $(,)?) => {
impl $($generic)* ::core::convert::From<$from>
for $($enum)+
{
fn from(value: $from) -> Self {
Self::$variant(value)
}
}
};
(@parse [$($generic:tt)*] [$from:ty] [$($enum:tt)+]
:: $variant:ident { $field:ident $(,)? } $(,)?) => {
impl $($generic)* ::core::convert::From<$from>
for $($enum)+
{
fn from(value: $from) -> Self {
Self::$variant { $field: value }
}
}
};
(@parse [$($generic:tt)*] [$from:ty] [$($path:tt)*]
$next:tt $($rest:tt)+) => {
$crate::impl_enum_from!(@parse [$($generic)*] [$from]
[$($path)* $next] $($rest)+);
};
}
#[cfg(test)]
mod tests {
#[derive(Debug, PartialEq)]
enum Value<T> {
Some(T),
}
impl_enum_from!(u32 => crate::enum_from::tests::Value<u32>::Some);
#[test]
fn error_conversion_with_question_mark() {
enum Error {
Io(std::io::Error),
Parse(core::num::ParseIntError),
}
impl_enum_from!(std::io::Error => Error::Io);
impl_enum_from!(core::num::ParseIntError => Error::Parse,);
fn fail() -> Result<(), Error> {
Err(std::io::Error::from(std::io::ErrorKind::NotFound))?;
Ok(())
}
match fail() {
Err(Error::Io(error)) => assert_eq!(error.kind(), std::io::ErrorKind::NotFound),
_ => panic!("expected an IO error"),
}
let source = "invalid".parse::<u32>().unwrap_err();
match Error::from(source.clone()) {
Error::Parse(error) => assert_eq!(error, source),
_ => panic!("expected a parse error"),
}
}
#[test]
fn generic_enum_with_module_path() {
mod errors {
#[derive(Debug, PartialEq)]
pub enum Error<T, U> {
Code(u32),
Other((T, U)),
}
}
impl_enum_from!(<T, U> in u32 => errors::Error<T, U>::Code);
impl_enum_from!(<T, U> in (T, U) => errors::Error<T, U>::Other,);
assert_eq!(errors::Error::<u8, bool>::from(42), errors::Error::Code(42));
assert_eq!(
errors::Error::from((7_u8, true)),
errors::Error::Other((7, true))
);
}
#[test]
fn named_error_conversion_with_question_mark() {
enum Error {
Io { source: std::io::Error },
}
impl_enum_from!(std::io::Error => Error::Io { source });
fn fail() -> Result<(), Error> {
Err(std::io::Error::from(std::io::ErrorKind::PermissionDenied))?;
Ok(())
}
match fail() {
Err(Error::Io { source }) => {
assert_eq!(source.kind(), std::io::ErrorKind::PermissionDenied);
}
_ => panic!("expected an IO error"),
}
}
#[test]
fn named_generic_enum_with_module_path() {
mod errors {
#[derive(Debug, PartialEq)]
pub enum Error<T> {
Other { source: T },
}
}
impl_enum_from!(<T> in T => errors::Error<T>::Other { source, },);
assert_eq!(
errors::Error::from(42_u32),
errors::Error::Other { source: 42 }
);
}
#[test]
fn concrete_generic_arguments_with_crate_path() {
assert_eq!(Value::<u32>::from(42), Value::Some(42));
}
}