• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)]
30 macro_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 }
87 pub use __struct_keyword as struct_keyword;
88 
89 #[cfg(test)]
90 mod tests {
91     use super::*;
92     use crate::{
93         extensions::unicode,
94         subtags::{subtag, Subtag},
95     };
96     use core::str::FromStr;
97 
98     #[test]
struct_keywords_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 }
125