riven/consts/
macros.rs

1#![macro_use]
2
3/// Macro for deriving `Serialize` and `Deserialize` for string enums with an
4/// `UNKNOWN(String)` variant.
5///
6/// Enum should have `#[derive(EnumString, EnumVariantNames, IntoStaticStr)]` included.
7///
8/// Also implements `AsRef<str>`, `Display`, and `From<&str>`.
9macro_rules! serde_strum_unknown {
10    ($name:ident) => {
11        impl AsRef<str> for $name {
12            fn as_ref(&self) -> &str {
13                match self {
14                    Self::UNKNOWN(string) => &*string,
15                    known => known.into(),
16                }
17            }
18        }
19        impl std::fmt::Display for $name {
20            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
21                self.as_ref().fmt(f)
22            }
23        }
24        impl serde::ser::Serialize for $name {
25            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26            where
27                S: serde::ser::Serializer,
28            {
29                serializer.serialize_str(self.as_ref())
30            }
31        }
32
33        impl From<&str> for $name {
34            fn from(item: &str) -> Self {
35                item.parse().unwrap()
36            }
37        }
38        impl<'de> serde::de::Deserialize<'de> for $name {
39            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
40            where
41                D: serde::de::Deserializer<'de>,
42            {
43                #[cfg(not(feature = "deny-unknown-enum-variants-strings"))]
44                {
45                    <&str>::deserialize(deserializer).map(Into::into)
46                }
47                #[cfg(feature = "deny-unknown-enum-variants-strings")]
48                {
49                    <&str>::deserialize(deserializer)
50                        .map(Into::into)
51                        .and_then(|item| match item {
52                            Self::UNKNOWN(unknown) => {
53                                if unknown.is_empty() {
54                                    // https://github.com/RiotGames/developer-relations/issues/898
55                                    Ok(Self::UNKNOWN(unknown))
56                                } else {
57                                    Err(serde::de::Error::unknown_variant(
58                                        &*unknown,
59                                        <Self as strum::VariantNames>::VARIANTS,
60                                    ))
61                                }
62                            }
63                            other => Ok(other),
64                        })
65                }
66            }
67        }
68        impl_edeserialize!($name);
69    };
70}
71
72macro_rules! arr {
73    (
74        $( #[$attr:meta] )*
75        $v:vis $id:ident $name:ident: [$ty:ty; _] = $value:expr
76    ) => {
77        $( #[$attr] )*
78        $v $id $name: [$ty; $value.len()] = $value;
79    }
80}
81
82/// Macro for newtype "enums" with integer values.
83///
84/// For serde, use the following:
85/// ```ignore
86/// #[derive(Serialize, Deserialize)]
87/// #[serde(from = "$repr", into = "$repr")]
88/// ```
89macro_rules! newtype_enum {
90    {
91        $( #[$attr:meta] )*
92        $v:vis newtype_enum $name:ident($repr:ty) {
93            $(
94                $( #[$var_attr:meta] )*
95                $var_name:ident = $var_val:expr,
96            )*
97        }
98    } => {
99        $( #[$attr] )*
100        #[derive(Copy, Clone)]
101        #[derive(Hash, PartialEq, Eq, PartialOrd, Ord)]
102        #[repr(transparent)]
103        $v struct $name($v $repr);
104        impl $name {
105            $(
106                $( #[$var_attr] )*
107                $v const $var_name: Self = Self($var_val);
108            )*
109        }
110
111        impl $name {
112            arr!{
113                #[doc = "Array containing all known variants."]
114                pub const ALL_KNOWN: [Self; _] = [
115                    $( Self::$var_name, )*
116                ]
117            }
118
119            #[doc = "If this is one of the known variants."]
120            $v const fn is_known(self) -> bool {
121                match self {
122                    $(
123                        Self::$var_name => true,
124                    )*
125                    _ => false,
126                }
127            }
128        }
129
130        impl std::convert::From<$name> for $repr {
131            fn from(value: $name ) -> Self {
132                value.0
133            }
134        }
135        impl serde::ser::Serialize for $name {
136            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137            where
138                S: serde::ser::Serializer,
139            {
140                <$repr>::serialize(&self.0, serializer)
141            }
142        }
143
144        impl std::convert::From<$repr> for $name {
145            fn from(value: $repr ) -> Self {
146                Self(value)
147            }
148        }
149        impl<'de> serde::de::Deserialize<'de> for $name {
150            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
151            where
152                D: serde::de::Deserializer<'de>
153            {
154                #[cfg(not(feature = "deny-unknown-enum-variants-integers"))]
155                {
156                    <$repr>::deserialize(deserializer).map(Into::into)
157                }
158                #[cfg(feature = "deny-unknown-enum-variants-integers")]
159                {
160                    <$repr>::deserialize(deserializer).map(Into::into)
161                        .and_then(|item: Self| {
162                            // Exception for id zero: https://github.com/RiotGames/developer-relations/issues/898
163                            if 0 != item.0 && !item.is_known() {
164                                Err(serde::de::Error::custom(format!(
165                                    "Unknown integer enum variant: {} (\"deny-unknown-enum-variants-integers\" feature is enabled).\nExpected one of the following: {:?}",
166                                    item, Self::ALL_KNOWN
167                                )))
168                            }
169                            else {
170                                Ok(item)
171                            }
172                        })
173                }
174            }
175        }
176        impl_edeserialize!($name);
177
178        impl std::fmt::Display for $name {
179            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180                self.0.fmt(f)
181            }
182        }
183        impl std::fmt::Debug for $name {
184            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185                write!(f, "{}({}{})", stringify!($name), self.0, if self.is_known() { "" } else { "?" })
186            }
187        }
188    }
189}
190
191macro_rules! impl_edeserialize {
192    ($($t:ty),* $(,)?) => {
193        $(
194            #[cfg(feature = "eserde")]
195            impl<'de> eserde::EDeserialize<'de> for $t {
196                fn deserialize_for_errors<D>(deserializer: D) -> Result<(), ()>
197                where
198                    D: serde::Deserializer<'de>
199                {
200                    <Self as serde::de::Deserialize<'de>>::deserialize(deserializer).map(|_| ()).map_err(|e| {
201                        eserde::reporter::ErrorReporter::report(e);
202                    })
203                }
204            }
205        )*
206    };
207}