Skip to main content

hmrc_rates/
rate.rs

1use rust_decimal::Decimal;
2
3use crate::types::{Currency, Period};
4
5/// A resolved HMRC rate: currency units per £1, with exact `Decimal` arithmetic.
6///
7/// The crate never rounds.
8/// Callers apply whatever rounding their tax context requires.
9#[derive(Copy, Clone, PartialEq, Eq, Debug)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct Rate {
12    units_per_gbp: Decimal,
13    currency: Currency,
14    period: Period,
15}
16
17impl Rate {
18    pub(crate) fn new(units_per_gbp: Decimal, currency: Currency, period: Period) -> Rate {
19        Rate {
20            units_per_gbp,
21            currency,
22            period,
23        }
24    }
25
26    /// The canonical HMRC figure: how many currency units £1 buys.
27    pub fn units_per_gbp(&self) -> Decimal {
28        self.units_per_gbp
29    }
30
31    /// Converts an amount in this rate's currency to GBP (`amount / units_per_gbp`).
32    ///
33    /// Exact division, round the result yourself.
34    ///
35    /// # Examples
36    ///
37    /// ```
38    /// use hmrc_rates::{YearMonth, Rates};
39    /// use rust_decimal::Decimal;
40    ///
41    /// let rates = Rates::new();
42    /// let usd = rates.monthly_rate("USD", YearMonth::new(2025, 8).unwrap())?;
43    /// let gbp = usd.to_gbp(Decimal::from(2500));
44    /// println!("£{}", gbp.round_dp(2));
45    /// # Ok::<(), hmrc_rates::LookupError>(())
46    /// ```
47    pub fn to_gbp(&self, amount: Decimal) -> Decimal {
48        amount / self.units_per_gbp
49    }
50
51    /// Converts a GBP amount to this rate's currency (`gbp * units_per_gbp`).
52    pub fn from_gbp(&self, gbp: Decimal) -> Decimal {
53        gbp * self.units_per_gbp
54    }
55
56    /// The currency this rate quotes against GBP.
57    pub fn currency(&self) -> Currency {
58        self.currency
59    }
60
61    /// The period the rate was published for.
62    /// Reveals the substituted month after
63    /// [`Rates::monthly_rate_or_earlier`](crate::Rates::monthly_rate_or_earlier).
64    pub fn period(&self) -> Period {
65        self.period
66    }
67}