-
-
Save heaths/9ca334d400763698e0d4c895d495190a to your computer and use it in GitHub Desktop.
create_enum!() example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::{error::Error, fmt, marker::PhantomData}; | |
macro_rules! create_enum { | |
($(#[$type_meta:meta])* $name:ident, $($(#[$variant_meta:meta])* $variant:ident), +) => ( | |
create_enum!($(#[$type_meta])* $name, $($(#[$variant_meta])* ($variant, stringify!($variant))), +); | |
); | |
($(#[$type_meta:meta])* $name:ident, $($(#[$variant_meta:meta])* ($variant:ident, $value:expr)), +) => ( | |
$(#[$type_meta])* | |
#[derive(Debug, PartialEq, Eq, Clone, Copy)] | |
pub enum $name { | |
$( | |
$(#[$variant_meta])* | |
$variant, | |
)* | |
} | |
impl ::std::fmt::Display for $name { | |
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { | |
match self { | |
$( | |
$name::$variant => write!(f, "{}", $value), | |
)* | |
} | |
} | |
} | |
impl ::std::str::FromStr for $name { | |
type Err = $crate::ParseEnumError<$name>; | |
fn from_str(s: &str) -> Result<Self, Self::Err> { | |
match s { | |
$( | |
$value => Ok($name::$variant), | |
)* | |
_ => Err($crate::ParseEnumError::<$name>(s.to_string(), ::std::marker::PhantomData)) | |
} | |
} | |
} | |
impl $crate::EnumValues for $name { | |
fn possible_values() -> &'static [&'static str] { | |
&[$($value),*] | |
} | |
} | |
); | |
} | |
pub trait EnumValues { | |
fn possible_values() -> &'static [&'static str]; | |
fn join_values(quote: Option<char>) -> String { | |
let values = Self::possible_values(); | |
let len = values.len(); | |
let q = |s: &str| -> String { | |
match quote { | |
Some(c) => format!("{c}{s}{c}"), | |
None => s.to_string(), | |
} | |
}; | |
match len { | |
0 => String::new(), | |
1 => q(values[0]), | |
2 => format!("{} or {}", q(values[0]), q(values[1])), | |
_ => { | |
let v: Vec<String> = values[..len - 1].iter().map(|s| q(s)).collect(); | |
format!("{}, or {}", v.join(", "), q(values[len - 1])) | |
} | |
} | |
} | |
} | |
#[derive(Debug)] | |
pub struct ParseEnumError<T>(String, PhantomData<T>) | |
where | |
T: fmt::Debug + EnumValues; | |
impl<T> fmt::Display for ParseEnumError<T> | |
where | |
T: fmt::Debug + EnumValues, | |
{ | |
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
write!( | |
f, | |
"unsupported value '{}'; supported values include {}", | |
self.0, | |
<T as EnumValues>::join_values(Some('\'')), | |
) | |
} | |
} | |
impl<T> Error for ParseEnumError<T> where T: fmt::Debug + EnumValues {} | |
create_enum!(Metasyntactic, Foo, Bar, Baz); | |
fn main() { | |
let m: Metasyntactic = "Foo".parse().unwrap(); | |
println!("{m}"); | |
let err = "Qux".parse::<Metasyntactic>().unwrap_err(); | |
println!("{err}"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Open in Rust Playground
Clone into the current directory using the GitHub CLI:
gh extension install --force heaths/gh-cargo-gist gh cargo-gist 9ca334d400763698e0d4c895d495190a cd 9ca334d400763698e0d4c895d495190a cargo run