currency.rs (4141B)
1 use core::fmt; 2 use core::str::FromStr; 3 4 #[cfg(all(feature = "serde", not(feature = "std")))] 5 use alloc::string::String; 6 #[cfg(feature = "serde")] 7 #[cfg(feature = "std")] 8 use std::string::String; 9 10 #[cfg(feature = "serde")] 11 use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeError}; 12 13 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 14 pub struct RadrootsCoreCurrency([u8; 3]); 15 16 impl RadrootsCoreCurrency { 17 #[inline] 18 pub const fn from_const(bytes: [u8; 3]) -> Result<Self, RadrootsCoreCurrencyParseError> { 19 if Self::is_ascii_upper(bytes[0]) 20 && Self::is_ascii_upper(bytes[1]) 21 && Self::is_ascii_upper(bytes[2]) 22 { 23 Ok(Self(bytes)) 24 } else { 25 Err(RadrootsCoreCurrencyParseError::InvalidFormat) 26 } 27 } 28 29 const fn is_ascii_upper(byte: u8) -> bool { 30 byte >= b'A' && byte <= b'Z' 31 } 32 33 #[inline] 34 pub fn from_str_upper(s: &str) -> Result<Self, RadrootsCoreCurrencyParseError> { 35 let b = s.as_bytes(); 36 if b.len() != 3 || b.iter().any(|c| !c.is_ascii_uppercase()) { 37 return Err(RadrootsCoreCurrencyParseError::InvalidFormat); 38 } 39 Ok(Self([b[0], b[1], b[2]])) 40 } 41 42 #[inline] 43 pub fn as_str(&self) -> &str { 44 core::str::from_utf8(&self.0).unwrap_or("???") 45 } 46 47 pub const USD: RadrootsCoreCurrency = RadrootsCoreCurrency(*b"USD"); 48 pub const EUR: RadrootsCoreCurrency = RadrootsCoreCurrency(*b"EUR"); 49 pub const GBP: RadrootsCoreCurrency = RadrootsCoreCurrency(*b"GBP"); 50 pub const JPY: RadrootsCoreCurrency = RadrootsCoreCurrency(*b"JPY"); 51 pub const CAD: RadrootsCoreCurrency = RadrootsCoreCurrency(*b"CAD"); 52 pub const AUD: RadrootsCoreCurrency = RadrootsCoreCurrency(*b"AUD"); 53 54 #[inline] 55 pub const fn minor_unit_exponent(&self) -> u32 { 56 match self.0 { 57 [b'J', b'P', b'Y'] | [b'K', b'R', b'W'] | [b'V', b'N', b'D'] => 0, 58 [b'B', b'H', b'D'] 59 | [b'I', b'Q', b'D'] 60 | [b'J', b'O', b'D'] 61 | [b'K', b'W', b'D'] 62 | [b'L', b'Y', b'D'] 63 | [b'O', b'M', b'R'] 64 | [b'T', b'N', b'D'] => 3, 65 _ => 2, 66 } 67 } 68 } 69 70 impl fmt::Debug for RadrootsCoreCurrency { 71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 72 f.debug_tuple("RadrootsCoreCurrency") 73 .field(&self.as_str()) 74 .finish() 75 } 76 } 77 78 impl fmt::Display for RadrootsCoreCurrency { 79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 80 f.write_str(self.as_str()) 81 } 82 } 83 84 impl TryFrom<&str> for RadrootsCoreCurrency { 85 type Error = RadrootsCoreCurrencyParseError; 86 fn try_from(s: &str) -> Result<Self, Self::Error> { 87 s.parse() 88 } 89 } 90 91 impl FromStr for RadrootsCoreCurrency { 92 type Err = RadrootsCoreCurrencyParseError; 93 94 fn from_str(s: &str) -> Result<Self, Self::Err> { 95 let s = s.trim(); 96 if s.len() != 3 || !s.chars().all(|c| c.is_ascii_alphabetic()) { 97 return Err(RadrootsCoreCurrencyParseError::InvalidFormat); 98 } 99 let upper = s.to_ascii_uppercase(); 100 Self::from_str_upper(&upper) 101 } 102 } 103 104 #[non_exhaustive] 105 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 106 pub enum RadrootsCoreCurrencyParseError { 107 InvalidFormat, 108 } 109 110 impl fmt::Display for RadrootsCoreCurrencyParseError { 111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 112 match self { 113 RadrootsCoreCurrencyParseError::InvalidFormat => { 114 write!(f, "currency must be a 3-letter code") 115 } 116 } 117 } 118 } 119 120 #[cfg(feature = "std")] 121 impl std::error::Error for RadrootsCoreCurrencyParseError {} 122 123 #[cfg(feature = "serde")] 124 impl Serialize for RadrootsCoreCurrency { 125 fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> { 126 ser.serialize_str(self.as_str()) 127 } 128 } 129 130 #[cfg(feature = "serde")] 131 impl<'de> Deserialize<'de> for RadrootsCoreCurrency { 132 fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> { 133 let s = String::deserialize(de)?; 134 s.parse().map_err(D::Error::custom) 135 } 136 }