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