lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

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 }