Skip to main content

ocpi_tariffs/
energy.rs

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