macro_rules! raw_enum {
(
$(#[$meta:meta])*
pub enum $name:ident: $repr:ty {
$( $(#[$variant_meta:meta])* $variant:ident = $value:literal, )+
}
) => {
$(#[$meta])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum $name {
$( $(#[$variant_meta])* $variant, )+
#[serde(untagged)]
Unknown($repr),
}
impl $name {
pub const fn from_raw(raw: $repr) -> Self {
match raw {
$( $value => Self::$variant, )+
other => Self::Unknown(other),
}
}
pub const fn raw(self) -> $repr {
match self {
$( Self::$variant => $value, )+
Self::Unknown(raw) => raw,
}
}
}
};
}
#[cfg(test)]
mod tests {
raw_enum! {
pub enum Sample: u8 {
First = 0,
SecondOne = 5,
}
}
#[test]
fn keeps_unknown_values_and_serializes_names() {
assert_eq!(Sample::from_raw(5), Sample::SecondOne);
assert_eq!(Sample::from_raw(9), Sample::Unknown(9));
assert_eq!(Sample::Unknown(9).raw(), 9);
assert_eq!(
serde_json::to_string(&Sample::SecondOne).unwrap(),
r#""second_one""#
);
assert_eq!(serde_json::to_string(&Sample::Unknown(9)).unwrap(), "9");
}
}