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
11#[repr(transparent)]
#[doc = r" A Unicode Currency Identifier defines a type of currency."]
#[doc = r""]
#[doc =
r" The valid values are listed in [LDML](https://unicode.org/reports/tr35/#UnicodeCurrencyIdentifier)."]
pub struct CurrencyType(tinystr::TinyAsciiStr<3>);
#[automatically_derived]
impl ::core::fmt::Debug for CurrencyType {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "CurrencyType",
&&self.0)
}
}
#[automatically_derived]
impl ::core::marker::StructuralPartialEq for CurrencyType { }
#[automatically_derived]
impl ::core::cmp::PartialEq for CurrencyType {
#[inline]
fn eq(&self, other: &CurrencyType) -> bool { self.0 == other.0 }
}
#[automatically_derived]
impl ::core::cmp::Eq for CurrencyType {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<tinystr::TinyAsciiStr<3>>;
}
}
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for CurrencyType { }
#[automatically_derived]
impl ::core::clone::Clone for CurrencyType {
#[inline]
fn clone(&self) -> CurrencyType {
let _: ::core::clone::AssertParamIsClone<tinystr::TinyAsciiStr<3>>;
*self
}
}
#[automatically_derived]
impl ::core::hash::Hash for CurrencyType {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.0, state)
}
}
#[automatically_derived]
impl ::core::cmp::PartialOrd for CurrencyType {
#[inline]
fn partial_cmp(&self, other: &CurrencyType)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
}
}
#[automatically_derived]
impl ::core::cmp::Ord for CurrencyType {
#[inline]
fn cmp(&self, other: &CurrencyType) -> ::core::cmp::Ordering {
::core::cmp::Ord::cmp(&self.0, &other.0)
}
}
#[automatically_derived]
impl ::core::marker::Copy for CurrencyType { }
impl CurrencyType {
#[doc = "produces a well-formed [`CurrencyType`]."]
#[doc =
"use icu_locale_core::preferences :: extensions :: unicode :: keywords ::CurrencyType;"]
#[doc = "assert!(CurrencyType::try_from_str(\"usd\").is_ok());"]
#[doc = "assert!(CurrencyType::try_from_str(\"dollar\").is_err());"]
#[inline]
pub const fn try_from_str(s: &str)
-> Result<Self, crate::parser::errors::ParseError> {
Self::try_from_utf8(s.as_bytes())
}
pub const fn try_from_utf8(code_units: &[u8])
-> Result<Self, crate::parser::errors::ParseError> {
if code_units.len() < 3 || code_units.len() > 3 {
return Err(crate::parser::errors::ParseError::InvalidExtension);
}
match tinystr::TinyAsciiStr::try_from_utf8(code_units) {
Ok(s) if s.is_ascii_alphabetic() =>
Ok(Self(s.to_ascii_lowercase())),
_ => Err(crate::parser::errors::ParseError::InvalidExtension),
}
}
#[doc = "Safely creates a [`CurrencyType`] from its raw format"]
pub const fn try_from_raw(raw: [u8; 3])
-> Result<Self, crate::parser::errors::ParseError> {
if let Ok(s) = tinystr::TinyAsciiStr::<3>::try_from_raw(raw) {
if s.len() >= 3 &&
(s.is_ascii_alphabetic() && s.is_ascii_lowercase()) {
Ok(Self(s))
} else {
Err(crate::parser::errors::ParseError::InvalidExtension)
}
} else { Err(crate::parser::errors::ParseError::InvalidExtension) }
}
#[doc = "Unsafely creates a [`CurrencyType`] from its raw format"]
pub const unsafe fn from_raw_unchecked(v: [u8; 3]) -> Self {
unsafe { Self(tinystr::TinyAsciiStr::from_utf8_unchecked(v)) }
}
pub const fn into_raw(self) -> [u8; 3] { *self.0.all_bytes() }
#[inline]
pub const fn as_str(&self) -> &str { self.0.as_str() }
#[doc(hidden)]
pub const fn to_tinystr(&self) -> tinystr::TinyAsciiStr<3> { self.0 }
#[inline]
pub fn strict_cmp(self, other: &[u8]) -> core::cmp::Ordering {
self.as_str().as_bytes().cmp(other)
}
#[inline]
pub fn normalizing_eq(self, other: &str) -> bool {
self.as_str().eq_ignore_ascii_case(other)
}
}
impl core::str::FromStr for CurrencyType {
type Err = crate::parser::errors::ParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::try_from_str(s) }
}
impl<'l> From<&'l CurrencyType> for &'l str {
fn from(input: &'l CurrencyType) -> Self { input.as_str() }
}
impl From<CurrencyType> for tinystr::TinyAsciiStr<3> {
fn from(input: CurrencyType) -> Self { input.to_tinystr() }
}
impl ::writeable::Writeable for CurrencyType {
#[inline]
fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W)
-> core::fmt::Result {
(self.as_str()).write_to(sink)
}
#[inline]
fn write_to_parts<S: ::writeable::PartsWrite +
?Sized>(&self, sink: &mut S) -> core::fmt::Result {
(self.as_str()).write_to_parts(sink)
}
#[inline]
fn writeable_length_hint(&self) -> ::writeable::LengthHint {
(self.as_str()).writeable_length_hint()
}
#[inline]
fn writeable_borrow(&self) -> Option<&str> {
(self.as_str()).writeable_borrow()
}
}
impl core::fmt::Display for CurrencyType {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
::writeable::Writeable::write_to(&self, f)
}
}
#[doc =
"A macro allowing for compile-time construction of valid [`CurrencyType`] subtags."]
#[doc =
" icu_locale_core::preferences::extensions::unicode::keywords::currency!(\"usd\"),"]
#[doc =
" \"usd\".parse::<icu_locale_core::preferences::extensions::unicode::keywords::CurrencyType>().unwrap()"]
#[doc =
"icu_locale_core::preferences::extensions::unicode::keywords::currency!(\"dollar\");"]
#[doc =
"[`CurrencyType`]: crate::preferences::extensions::unicode::keywords::CurrencyType"]
#[macro_export]
#[doc(hidden)]
macro_rules! preferences_extensions_unicode_keywords_currency {
($string : literal) =>
{
const
{
use crate :: preferences :: extensions :: unicode :: keywords ::
CurrencyType; match CurrencyType ::
try_from_utf8($string.as_bytes())
{
Ok(r) => r, _ => panic!
(concat!
("Invalid ", stringify! (preferences), "::", stringify!
(extensions), "::", stringify! (unicode), "::", stringify!
(keywords), "::", stringify! (CurrencyType), ": ", $string)),
}
}
};
}
#[doc(inline)]
pub use preferences_extensions_unicode_keywords_currency as currency;
unsafe impl zerovec::ule::ULE for CurrencyType {
fn validate_bytes(bytes: &[u8]) -> Result<(), zerovec::ule::UleError> {
let it = bytes.chunks_exact(size_of::<Self>());
if !it.remainder().is_empty() {
return Err(zerovec::ule::UleError::length::<Self>(bytes.len()));
}
for v in it {
let mut a = [0; size_of::<Self>()];
a.copy_from_slice(v);
if Self::try_from_raw(a).is_err() {
return Err(zerovec::ule::UleError::parse::<Self>());
}
}
Ok(())
}
}
impl zerovec::ule::NicheBytes<3> for CurrencyType {
const NICHE_BIT_PATTERN: [u8; 3] =
<tinystr::TinyAsciiStr<3>>::NICHE_BIT_PATTERN;
}
impl zerovec::ule::AsULE for CurrencyType {
type ULE = Self;
fn to_unaligned(self) -> Self::ULE { self }
fn from_unaligned(unaligned: Self::ULE) -> Self { unaligned }
}impl_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 = const {
use crate::extensions::unicode::Key;
match Key::try_from_utf8("cu".as_bytes()) {
Ok(r) => r,
_ => {
::core::panicking::panic_fmt(format_args!("Invalid extensions::unicode::Key: cu"));
}
}
}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}