icu_locale_core/preferences/extensions/unicode/macros/
struct_keyword.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5/// Macro used to generate a preference keyword as a struct.
6///
7/// # Examples
8///
9/// ```
10/// use icu::locale::{
11///     extensions::unicode::{Key, Value},
12///     preferences::extensions::unicode::struct_keyword,
13/// };
14///
15/// struct_keyword!(
16///     CurrencyType,
17///     "cu",
18///     String,
19///     |input: Value| { Ok(Self(input.to_string())) },
20///     |input: CurrencyType| {
21///         icu::locale::extensions::unicode::Value::try_from_str(
22///             input.0.as_str(),
23///         )
24///         .unwrap()
25///     }
26/// );
27/// ```
28#[macro_export]
29#[doc(hidden)]
30macro_rules! __struct_keyword {
31    ($(#[$doc:meta])* $([$derive_attrs:ty])? $name:ident, $ext_key:literal, $value:ty, $try_from:expr, $into:expr) => {
32        $(#[$doc])*
33        #[derive(Debug, Clone, Eq, PartialEq, Hash)]
34        $(#[derive($derive_attrs)])?
35        #[allow(clippy::exhaustive_structs)] // TODO
36        pub struct $name($value);
37
38        impl TryFrom<$crate::extensions::unicode::Value> for $name {
39            type Error = $crate::preferences::extensions::unicode::errors::PreferencesParseError;
40
41            fn try_from(
42                input: $crate::extensions::unicode::Value,
43            ) -> Result<Self, Self::Error> {
44                $try_from(input)
45            }
46        }
47
48        impl From<$name> for $crate::extensions::unicode::Value {
49            fn from(input: $name) -> $crate::extensions::unicode::Value {
50                $into(input)
51            }
52        }
53
54        impl $crate::preferences::PreferenceKey for $name {
55            fn unicode_extension_key() -> Option<$crate::extensions::unicode::Key> {
56                Some($crate::extensions::unicode::key!($ext_key))
57            }
58
59            fn try_from_key_value(
60                key: &$crate::extensions::unicode::Key,
61                value: &$crate::extensions::unicode::Value,
62            ) -> Result<Option<Self>, $crate::preferences::extensions::unicode::errors::PreferencesParseError> {
63                if Self::unicode_extension_key() == Some(*key) {
64                    let result = Self::try_from(value.clone())?;
65                    Ok(Some(result))
66                } else {
67                    Ok(None)
68                }
69            }
70
71            fn unicode_extension_value(
72                &self,
73            ) -> Option<$crate::extensions::unicode::Value> {
74                Some(self.clone().into())
75            }
76        }
77
78        impl core::ops::Deref for $name {
79            type Target = $value;
80
81            fn deref(&self) -> &Self::Target {
82                &self.0
83            }
84        }
85    };
86}
87pub use __struct_keyword as struct_keyword;
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use crate::{
93        extensions::unicode,
94        subtags::{subtag, Subtag},
95    };
96    use core::str::FromStr;
97
98    #[test]
99    fn struct_keywords_test() {
100        struct_keyword!(
101            DummyKeyword,
102            "dk",
103            Subtag,
104            |input: unicode::Value| {
105                if let Some(subtag) = input.into_single_subtag() {
106                    if subtag.len() == 3 {
107                        return Ok(DummyKeyword(subtag));
108                    }
109                }
110                Err(crate::preferences::extensions::unicode::errors::PreferencesParseError::InvalidKeywordValue)
111            },
112            |input: DummyKeyword| { unicode::Value::from_subtag(Some(input.0)) }
113        );
114
115        let v = unicode::Value::from_str("foo").unwrap();
116        let dk: DummyKeyword = v.clone().try_into().unwrap();
117        assert_eq!(dk, DummyKeyword(subtag!("foo")));
118        assert_eq!(unicode::Value::from(dk), v);
119
120        let v = unicode::Value::from_str("foobar").unwrap();
121        let dk: Result<DummyKeyword, _> = v.clone().try_into();
122        assert!(dk.is_err());
123    }
124}