icu_locale_core/preferences/extensions/unicode/macros/
enum_keyword.rs1#[macro_export]
7#[doc(hidden)]
8macro_rules! __enum_keyword_inner {
9 ($key:expr, $name:ident, $variant:ident, $value:ident) => {{
10 if $value.get_subtag(1).is_some() {
11 return Err(Self::Error::InvalidKeywordValue);
12 }
13 $name::$variant
14 }};
15 ($key:expr, $name:ident, $variant:ident, $value:ident, $v2:ident, $($subk:expr => $subv:ident),*) => {{
16 const _: () = assert!(!matches!($crate::subtags::subtag!($key), TRUE), "true value not allowed with second level");
17 if $value.get_subtag(2).is_some() {
18 return Err(Self::Error::InvalidKeywordValue);
19 }
20 $name::$variant(match $value.get_subtag(1) {
21 None => None,
22 $(
23 Some(s) if s == &$crate::subtags::subtag!($subk) => Some($v2::$subv),
24 )*
25 _ => return Err(Self::Error::InvalidKeywordValue),
26 })
27 }};
28}
29
30#[macro_export]
57#[doc(hidden)]
58macro_rules! __enum_keyword {
59 (
60 $(#[$doc:meta])*
61 $([$derive_attrs:ty])?
62 $name:ident {
63 $(
64 $(#[$variant_doc:meta])*
65 $([$variant_attr:ty])?
66 $variant:ident $($v2:ident)?
67 ),*
68 }
69 ) => {
70 #[non_exhaustive]
71 #[derive(Debug, Clone, Eq, PartialEq, Copy, Hash)]
72 $(#[derive($derive_attrs)])?
73 $(#[$doc])*
74 #[cfg_attr(feature = "databake", derive(databake::Bake))]
75 #[cfg_attr(feature = "databake", databake(path = icu_locale_core::preferences::extensions::unicode::keywords))]
76 pub enum $name {
77 $(
78 $(#[$variant_doc])*
79 $(#[$variant_attr])?
80 $variant $((Option<$v2>))?
81 ),*
82 }
83 };
84 ($(#[$doc:meta])*
85 $([$derive_attrs:ty])?
86 $name:ident {
87 $(
88 $(#[$variant_doc:meta])*
89 $([$variant_attr:ty])?
90 ($key:literal => $variant:ident $(($v2:ident) {
91 $(
92 ($subk:literal => $subv:ident)
93 ),*
94 })?)
95 ),* $(,)?
96 },
97 $ext_key:literal
98 $(, $input:ident, $aliases:stmt)?
99 ) => {
100 $crate::__enum_keyword!(
101 $(#[$doc])*
102 $([$derive_attrs])?
103 $name {
104 $(
105 $(#[$variant_doc])*
106 $([$variant_attr])?
107 $variant $($v2)?
108 ),*
109 }
110 );
111
112 impl $crate::preferences::PreferenceKey for $name {
113 fn unicode_extension_key() -> Option<$crate::extensions::unicode::Key> {
114 Some(Self::UNICODE_EXTENSION_KEY)
115 }
116
117 fn try_from_key_value(
118 key: &$crate::extensions::unicode::Key,
119 value: &$crate::extensions::unicode::Value,
120 ) -> Result<Option<Self>, $crate::preferences::extensions::unicode::errors::PreferencesParseError> {
121 if Self::UNICODE_EXTENSION_KEY == *key {
122 Self::try_from(value).map(Some)
123 } else {
124 Ok(None)
125 }
126 }
127
128 fn unicode_extension_value(&self) -> Option<$crate::extensions::unicode::Value> {
129 Some((*self).into())
130 }
131 }
132
133 impl $name {
134 pub(crate) const UNICODE_EXTENSION_KEY: $crate::extensions::unicode::Key = $crate::extensions::unicode::key!($ext_key);
135 }
136
137 impl TryFrom<&$crate::extensions::unicode::Value> for $name {
138 type Error = $crate::preferences::extensions::unicode::errors::PreferencesParseError;
139
140 fn try_from(value: &$crate::extensions::unicode::Value) -> Result<Self, Self::Error> {
141 const TRUE: $crate::subtags::Subtag = $crate::subtags::subtag!("true");
142
143 #[allow(unused_imports)]
144 use $crate::extensions::unicode::value;
145 $(
146 let $input = value;
147 $aliases
148 )?
149 Ok(match value.get_subtag(0).copied().unwrap_or(TRUE) {
150 $(
151 s if s == $crate::subtags::subtag!($key) => $crate::__enum_keyword_inner!($key, $name, $variant, value$(, $v2, $($subk => $subv),*)?),
152 )*
153 _ => return Err(Self::Error::InvalidKeywordValue),
154 })
155 }
156 }
157
158 impl From<$name> for $crate::extensions::unicode::Value {
159 fn from(input: $name) -> $crate::extensions::unicode::Value {
160 let f;
161 #[allow(unused_mut)]
162 let mut s = None;
163 match input {
164 $(
165 #[allow(non_snake_case)]
170 $name::$variant $(($v2))? => {
171 f = $crate::subtags::subtag!($key);
172
173 $(
174 if let Some(v2) = $v2 {
175 match v2 {
176 $(
177 $v2::$subv => s = Some($crate::subtags::subtag!($subk)),
178 )*
179 }
180 }
181 )?
182 },
183 )*
184 }
185 if let Some(s) = s {
186 $crate::extensions::unicode::Value::from_two_subtags(f, s)
187 } else {
188 $crate::extensions::unicode::Value::from_subtag(Some(f))
189 }
190 }
191 }
192
193 impl $name {
194 pub const fn as_str(&self) -> &'static str {
196 match self {
197 $(
198 #[allow(non_snake_case)]
203 Self::$variant $(($v2))? => {
204 $(
205 if let Some(v2) = $v2 {
206 return match v2 {
207 $(
208 $v2::$subv => concat!($key, '-', $subk),
209 )*
210 };
211 }
212 )?
213 return $key;
214 },
215 )*
216 }
217 }
218 }
219 };
220}
221pub use __enum_keyword as enum_keyword;
222
223fn _nested_true() {}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use crate::extensions::unicode;
242 use core::str::FromStr;
243
244 #[test]
245 fn enum_keywords_test() {
246 enum_keyword!(DummyKeyword {
247 ("standard" => Standard),
248 ("rare" => Rare),
249 }, "dk");
250
251 let v = unicode::Value::from_str("standard").unwrap();
252 let dk = DummyKeyword::try_from(&v).unwrap();
253 assert_eq!(dk, DummyKeyword::Standard);
254 assert_eq!(unicode::Value::from(dk), v);
255
256 let v = unicode::Value::from_str("rare").unwrap();
257 let dk = DummyKeyword::try_from(&v).unwrap();
258 assert_eq!(dk, DummyKeyword::Rare);
259 assert_eq!(unicode::Value::from(dk), v);
260
261 let v = unicode::Value::from_str("foo").unwrap();
262 let dk = DummyKeyword::try_from(&v);
263 dk.unwrap_err();
264
265 assert_eq!(DummyKeyword::Standard.as_str(), "standard");
266 }
267
268 #[test]
269 fn enum_keywords_test_alias() {
270 enum_keyword!(DummyKeyword {
271 ("standard" => Standard),
272 ("rare" => Rare),
273 }, "dk", s, if *s == value!("std") { return Ok(Self::Standard) });
274
275 let v = unicode::Value::from_str("standard").unwrap();
276 let dk = DummyKeyword::try_from(&v).unwrap();
277 assert_eq!(dk, DummyKeyword::Standard);
278 assert_eq!(unicode::Value::from(dk), v);
279
280 let v_alias = unicode::Value::from_str("std").unwrap();
281 let dk = DummyKeyword::try_from(&v_alias).unwrap();
282 assert_eq!(dk, DummyKeyword::Standard);
283 assert_eq!(unicode::Value::from(dk), v);
284
285 let v = unicode::Value::from_str("rare").unwrap();
286 let dk = DummyKeyword::try_from(&v).unwrap();
287 assert_eq!(dk, DummyKeyword::Rare);
288 assert_eq!(unicode::Value::from(dk), v);
289
290 let v = unicode::Value::from_str("foo").unwrap();
291 let dk = DummyKeyword::try_from(&v);
292 dk.unwrap_err();
293
294 assert_eq!(DummyKeyword::Standard.as_str(), "standard");
295 }
296
297 #[test]
298 fn enum_keywords_nested_test() {
299 enum_keyword!(DummySubKeyword { Standard, Rare });
300
301 enum_keyword!(DummyKeyword {
302 ("default" => Default),
303 ("sub" => Sub(DummySubKeyword) {
304 ("standard" => Standard),
305 ("rare" => Rare)
306 })
307 }, "dk");
308
309 let v = unicode::Value::from_str("default").unwrap();
310 let dk = DummyKeyword::try_from(&v).unwrap();
311 assert_eq!(dk, DummyKeyword::Default);
312 assert_eq!(unicode::Value::from(dk), v);
313
314 let v = unicode::Value::from_str("sub").unwrap();
315 let dk = DummyKeyword::try_from(&v).unwrap();
316 assert_eq!(dk, DummyKeyword::Sub(None));
317 assert_eq!(unicode::Value::from(dk), v);
318
319 let v = unicode::Value::from_str("foo").unwrap();
320 let dk = DummyKeyword::try_from(&v);
321 dk.unwrap_err();
322
323 let v = unicode::Value::from_str("sub-standard").unwrap();
324 let dk = DummyKeyword::try_from(&v).unwrap();
325 assert_eq!(dk, DummyKeyword::Sub(Some(DummySubKeyword::Standard)));
326 assert_eq!(unicode::Value::from(dk), v);
327
328 let v = unicode::Value::from_str("sub-rare").unwrap();
329 let dk = DummyKeyword::try_from(&v).unwrap();
330 assert_eq!(dk, DummyKeyword::Sub(Some(DummySubKeyword::Rare)));
331 assert_eq!(unicode::Value::from(dk), v);
332
333 let v = unicode::Value::from_str("sub-foo").unwrap();
334 let dk = DummyKeyword::try_from(&v);
335 dk.unwrap_err();
336
337 assert_eq!(
338 DummyKeyword::Sub(Some(DummySubKeyword::Rare)).as_str(),
339 "sub-rare"
340 );
341 }
342}