Skip to main content

ocpi_tariffs/
energy.rs

1//! Types to represent energy.
2
3use rust_decimal::Decimal;
4use rust_decimal_macros::dec;
5
6use crate::{
7    impl_dec_newtype,
8    money::Cost,
9    number::{self, approx_eq_dec, FromDecimal as _, IsZero},
10    Money,
11};
12
13impl_dec_newtype!(Ampere, "A");
14impl_dec_newtype!(Kw, "kW");
15impl_dec_newtype!(Kwh, "kWh");
16
17/// A value of kilo watt hours.
18#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Default)]
19#[cfg_attr(test, derive(serde::Deserialize))]
20pub struct Kwh(Decimal);
21
22impl IsZero for Kwh {
23    fn is_zero(&self) -> bool {
24        const TOLERANCE: Decimal = dec!(0.001);
25
26        approx_eq_dec(&self.0, &Decimal::ZERO, TOLERANCE)
27    }
28}
29
30impl Cost for Kwh {
31    fn cost(&self, money: Money) -> Money {
32        let cost = self.0.saturating_mul(money.into());
33        Money::from_decimal(cost)
34    }
35}
36
37const KILO: Decimal = dec!(1000);
38
39impl Kwh {
40    #[must_use]
41    pub(crate) const fn zero() -> Self {
42        Self(Decimal::ZERO)
43    }
44
45    /// Return the value in watt hours, rather than the kilowatt hours it is stored as.
46    pub fn watt_hours(self) -> Decimal {
47        self.0.saturating_mul(KILO)
48    }
49
50    #[expect(clippy::missing_panics_doc, reason = "divisor is non-zero")]
51    #[expect(clippy::unwrap_used, reason = "divisor is non-zero")]
52    /// Build a value from an amount given in watt hours.
53    pub fn from_watt_hours(num: Decimal) -> Self {
54        Self(num.checked_div(KILO).unwrap())
55    }
56}
57
58/// A value of kilo watts.
59#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
60pub struct Kw(Decimal);
61
62impl IsZero for Kw {
63    fn is_zero(&self) -> bool {
64        const TOLERANCE: Decimal = dec!(0.001);
65
66        approx_eq_dec(&self.0, &Decimal::ZERO, TOLERANCE)
67    }
68}
69
70/// A value of amperes.
71#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
72pub struct Ampere(Decimal);
73
74impl IsZero for Ampere {
75    fn is_zero(&self) -> bool {
76        const TOLERANCE: Decimal = dec!(0.001);
77
78        approx_eq_dec(&self.0, &Decimal::ZERO, TOLERANCE)
79    }
80}