icu_locale_core/preferences/extensions/unicode/keywords/
currency.rs1use crate::extensions::unicode::{Key, key};
6use crate::preferences::extensions::unicode::errors::PreferencesParseError;
7use crate::preferences::extensions::unicode::struct_keyword;
8use crate::{extensions::unicode::Value, subtags::Subtag};
9use tinystr::TinyAsciiStr;
10
11impl_tinystr_subtag!(
12 CurrencyType,
16 preferences::extensions::unicode::keywords,
17 currency,
18 preferences_extensions_unicode_keywords_currency,
19 3..=3,
20 s,
21 s.is_ascii_alphabetic(),
22 s.to_ascii_lowercase(),
23 s.is_ascii_alphabetic() && s.is_ascii_lowercase(),
24 InvalidExtension,
25 ["usd"],
26 ["dollar"],
27);
28
29impl CurrencyType {
30 #[inline]
42 pub const fn iso_code(self) -> TinyAsciiStr<3> {
43 self.0.to_ascii_uppercase()
44 }
45}
46
47impl TryFrom<Value> for CurrencyType {
48 type Error = PreferencesParseError;
49 fn try_from(input: Value) -> Result<Self, Self::Error> {
50 Self::try_from(&input)
51 }
52}
53
54impl TryFrom<&Value> for CurrencyType {
55 type Error = PreferencesParseError;
56 fn try_from(input: &Value) -> Result<Self, Self::Error> {
57 if let Some(subtag) = input.as_single_subtag() {
58 let ts = subtag.as_tinystr();
59 if ts.len() == 3 && ts.is_ascii_alphabetic() {
60 return Ok(Self(ts.resize()));
61 }
62 }
63 Err(PreferencesParseError::InvalidKeywordValue)
64 }
65}
66
67impl From<CurrencyType> for Value {
68 fn from(input: CurrencyType) -> Value {
69 (&input).into()
70 }
71}
72impl From<&CurrencyType> for Value {
73 fn from(input: &CurrencyType) -> Value {
74 Value::from_subtag(Some(Subtag::from_tinystr_unvalidated(input.0.resize())))
75 }
76}
77impl crate::preferences::PreferenceKey for CurrencyType {
78 fn unicode_extension_key() -> Option<Key> {
79 Some(Self::UNICODE_EXTENSION_KEY)
80 }
81 fn try_from_key_value(key: &Key, value: &Value) -> Result<Option<Self>, PreferencesParseError> {
82 if Self::UNICODE_EXTENSION_KEY == *key {
83 let result = Self::try_from(value.clone())?;
84 Ok(Some(result))
85 } else {
86 Ok(None)
87 }
88 }
89 fn unicode_extension_value(&self) -> Option<Value> {
90 Some(self.into())
91 }
92}
93impl CurrencyType {
94 pub(crate) const UNICODE_EXTENSION_KEY: Key = key!("cu");
95}
96impl core::ops::Deref for CurrencyType {
97 type Target = TinyAsciiStr<3>;
98 fn deref(&self) -> &Self::Target {
99 &self.0
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use tinystr::tinystr;
107
108 #[test]
109 fn test_valid_currency_types() {
110 let valid = [
111 ("USD", "usd", "USD"),
112 ("uSd", "usd", "USD"),
113 ("usd", "usd", "USD"),
114 ("EUR", "eur", "EUR"),
115 ("JPY", "jpy", "JPY"),
116 ];
117 for (input, expected_subtag, expected_iso) in valid {
118 let parsed = CurrencyType::try_from_str(input).unwrap();
119 let expected_ts_iso = TinyAsciiStr::<3>::try_from_str(expected_iso).unwrap();
120 assert_eq!(parsed.as_str(), expected_subtag);
121 assert_eq!(parsed.iso_code(), expected_ts_iso);
122 assert_eq!(parsed, input.parse::<CurrencyType>().unwrap());
123 }
124 }
125
126 #[test]
127 fn test_invalid_currency_types() {
128 let invalid = [
129 "", "U", "US", "USDDD", "US1", "123", "U$D", " US", "US ", "ÉUR",
130 ];
131 for input in invalid {
132 assert!(CurrencyType::try_from_str(input).is_err());
133 assert!(input.parse::<CurrencyType>().is_err());
134 }
135 }
136}