macro_rules! geo_enum {
(@common
$(#[$meta:meta])*
$vis:vis enum $name:ident / $name_ref:ident {
$( $(#[$vmeta:meta])* $var:ident => $code:literal, $label:literal ),* $(,)?
}
) => {
$(#[$meta])*
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
$vis enum $name {
$( $(#[$vmeta])* $var, )*
Unknown($crate::std::boxed::Box<str>),
}
#[doc = concat!("Borrowing, [`Copy`] counterpart of [`", stringify!($name), "`].")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
$vis enum $name_ref<'a> {
$( $(#[$vmeta])* $var, )*
Unknown(&'a str),
}
impl $name {
pub const ALL: &'static [Self] = &[ $( Self::$var ),* ];
#[must_use]
pub fn code(&self) -> &str {
match self {
$( Self::$var => $code, )*
Self::Unknown(s) => s,
}
}
#[must_use]
pub fn name(&self) -> Option<&'static str> {
match self {
$( Self::$var => Some($label), )*
Self::Unknown(_) => None,
}
}
#[must_use]
pub fn from_code(code: &str) -> Self {
match code {
$( $code => Self::$var, )*
other => Self::Unknown(other.into()),
}
}
#[must_use]
pub fn is_known(&self) -> bool {
!matches!(self, Self::Unknown(_))
}
#[must_use]
pub fn as_view(&self) -> $name_ref<'_> {
match self {
$( Self::$var => $name_ref::$var, )*
Self::Unknown(s) => $name_ref::Unknown(s),
}
}
}
impl<'a> $name_ref<'a> {
#[must_use]
pub fn code(self) -> &'a str {
match self {
$( Self::$var => $code, )*
Self::Unknown(s) => s,
}
}
#[must_use]
pub fn name(self) -> Option<&'static str> {
match self {
$( Self::$var => Some($label), )*
Self::Unknown(_) => None,
}
}
#[must_use]
pub fn from_code(code: &'a str) -> Self {
match code {
$( $code => Self::$var, )*
other => Self::Unknown(other),
}
}
#[must_use]
pub fn is_known(self) -> bool {
!matches!(self, Self::Unknown(_))
}
#[must_use]
pub fn to_owned(self) -> $name {
match self {
$( Self::$var => $name::$var, )*
Self::Unknown(s) => $name::Unknown(s.into()),
}
}
}
impl ::core::fmt::Display for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_str(self.code())
}
}
impl ::core::fmt::Display for $name_ref<'_> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_str(self.code())
}
}
impl ::core::str::FromStr for $name {
type Err = ::core::convert::Infallible;
fn from_str(s: &str) -> ::core::result::Result<Self, Self::Err> {
Ok(Self::from_code(s))
}
}
impl From<&str> for $name {
fn from(s: &str) -> Self {
Self::from_code(s)
}
}
impl From<$crate::std::string::String> for $name {
fn from(s: $crate::std::string::String) -> Self {
match s.as_str() {
$( $code => Self::$var, )*
_ => Self::Unknown(s.into_boxed_str()),
}
}
}
impl ::serde::Serialize for $name {
fn serialize<S: ::serde::Serializer>(
&self,
serializer: S,
) -> ::core::result::Result<S::Ok, S::Error> {
serializer.serialize_str(self.code())
}
}
impl ::serde::Serialize for $name_ref<'_> {
fn serialize<S: ::serde::Serializer>(
&self,
serializer: S,
) -> ::core::result::Result<S::Ok, S::Error> {
serializer.serialize_str(self.code())
}
}
impl<'de> ::serde::Deserialize<'de> for $name {
fn deserialize<D: ::serde::Deserializer<'de>>(
deserializer: D,
) -> ::core::result::Result<Self, D::Error> {
let s = <$crate::std::borrow::Cow<'de, str>>::deserialize(deserializer)?;
Ok(Self::from_code(&s))
}
}
};
(
meta $meta_struct:ident { $( $field:ident : $fty:ty ),+ $(,)? }
$(#[$meta:meta])*
$vis:vis enum $name:ident / $name_ref:ident {
$( $(#[$vmeta:meta])* $var:ident => $code:literal, $label:literal, { $($init:tt)* } ),* $(,)?
}
) => {
geo_enum! { @common
$(#[$meta])*
$vis enum $name / $name_ref {
$( $(#[$vmeta])* $var => $code, $label ),*
}
}
#[derive(Clone, Copy)]
struct $meta_struct { $( $field : $fty ),+ }
impl $name {
fn meta(&self) -> Option<$meta_struct> {
Some(match self {
$( Self::$var => $meta_struct { $($init)* }, )*
Self::Unknown(_) => return None,
})
}
$(
#[doc = concat!("The `", stringify!($field), "` metadata for this value, or `None` if unknown.")]
#[must_use]
pub fn $field(&self) -> $fty {
self.meta().and_then(|m| m.$field)
}
)+
}
impl $name_ref<'_> {
fn meta(self) -> Option<$meta_struct> {
Some(match self {
$( Self::$var => $meta_struct { $($init)* }, )*
Self::Unknown(_) => return None,
})
}
$(
#[doc = concat!("The `", stringify!($field), "` metadata for this value, or `None` if unknown.")]
#[must_use]
pub fn $field(self) -> $fty {
self.meta().and_then(|m| m.$field)
}
)+
}
};
(
$(#[$meta:meta])*
$vis:vis enum $name:ident / $name_ref:ident {
$( $(#[$vmeta:meta])* $var:ident => $code:literal, $label:literal ),* $(,)?
}
) => {
geo_enum! { @common
$(#[$meta])*
$vis enum $name / $name_ref {
$( $(#[$vmeta])* $var => $code, $label ),*
}
}
};
}
pub(crate) use geo_enum;