Skip to content

Instantly share code, notes, and snippets.

@heaths

heaths/main.rs Secret

Last active November 1, 2024 19:28
Show Gist options
  • Save heaths/9ca334d400763698e0d4c895d495190a to your computer and use it in GitHub Desktop.
Save heaths/9ca334d400763698e0d4c895d495190a to your computer and use it in GitHub Desktop.
create_enum!() example
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}");
}
@heaths
Copy link
Author

heaths commented Nov 1, 2024

  • 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment