unitx_core/
currency.rs

1use crate::providers::ExchangeRateProvider;
2use crate::providers::LiveExchangeProvider;
3use rust_decimal::Decimal;
4use std::fmt;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
7pub enum CurrencyUnit {
8    USD,
9    EUR,
10    GBP,
11    JPY,
12}
13
14impl CurrencyUnit {
15    pub fn parse(s: &str) -> Option<Self> {
16        match s.to_ascii_uppercase().as_str() {
17            "USD" => Some(Self::USD),
18            "EUR" => Some(Self::EUR),
19            "GBP" => Some(Self::GBP),
20            "JPY" => Some(Self::JPY),
21            _ => None,
22        }
23    }
24
25    pub fn as_str(&self) -> &'static str {
26        match self {
27            Self::USD => "USD",
28            Self::EUR => "EUR",
29            Self::GBP => "GBP",
30            Self::JPY => "JPY",
31        }
32    }
33}
34
35impl fmt::Display for CurrencyUnit {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        f.write_str(self.as_str())
38    }
39}
40
41pub fn convert_with_provider<P: ExchangeRateProvider>(
42    value: Decimal,
43    from: CurrencyUnit,
44    to: CurrencyUnit,
45    provider: &P,
46) -> Result<Decimal, String> {
47    let rate = provider.get_rate(from, to)?;
48    Ok(value * rate)
49}
50
51/// Convenience helper that fetches live rates directly.
52///
53/// This performs a blocking HTTP call using the default live provider. For deterministic or
54/// offline scenarios prefer `convert_with_provider` so you can supply your own rate source.
55pub fn convert(value: Decimal, from: CurrencyUnit, to: CurrencyUnit) -> Result<Decimal, String> {
56    let provider = LiveExchangeProvider::new(None);
57    convert_with_provider(value, from, to, &provider)
58}