ocpi_tariffs/
energy.rs

1use std::fmt;
2
3use rust_decimal::Decimal;
4use serde::{Deserialize, Serialize};
5
6use crate::{json, money::Cost, number, Money, Number};
7
8/// A value of kilo watt hours.
9#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Default)]
10#[serde(transparent)]
11pub struct Kwh(Number);
12
13impl json::FromJson<'_, '_> for Kwh {
14    type WarningKind = number::WarningKind;
15
16    fn from_json(elem: &'_ json::Element<'_>) -> crate::Verdict<Self, Self::WarningKind> {
17        Ok(Number::from_json(elem)?.map(Kwh))
18    }
19}
20
21impl Cost for Kwh {
22    fn cost(&self, price: Money) -> Money {
23        let cost = self.0.saturating_mul(price.into());
24        Money::from_number(cost)
25    }
26}
27
28impl Kwh {
29    #[expect(dead_code, reason = "Soon to be used in generate feature")]
30    pub(crate) fn from_decimal(d: Decimal) -> Self {
31        Self(Number::from_decimal(d))
32    }
33
34    pub fn zero() -> Self {
35        Self(Number::default())
36    }
37
38    /// Saturating addition
39    #[must_use]
40    pub fn saturating_add(self, other: Self) -> Self {
41        Self(self.0.saturating_add(other.0))
42    }
43
44    /// Saturating subtraction
45    #[must_use]
46    pub fn saturating_sub(self, other: Self) -> Self {
47        Self(self.0.saturating_sub(other.0))
48    }
49
50    pub fn watt_hours(self) -> Number {
51        self.0.saturating_mul(Number::from(1000))
52    }
53
54    #[expect(clippy::missing_panics_doc, reason = "divisor is non-zero")]
55    #[expect(clippy::unwrap_used, reason = "divisor is non-zero")]
56    pub fn from_watt_hours(num: Number) -> Self {
57        Self(num.checked_div(Number::from(1000)).unwrap())
58    }
59
60    /// Round this number to the OCPI specified amount of decimals.
61    #[must_use]
62    pub fn rescale(self) -> Self {
63        Self(self.0.rescale())
64    }
65
66    #[must_use]
67    pub fn round_dp(self, digits: u32) -> Self {
68        Self(self.0.round_dp(digits))
69    }
70}
71
72impl From<Kwh> for Decimal {
73    fn from(value: Kwh) -> Self {
74        value.0.into()
75    }
76}
77
78impl From<Kwh> for Number {
79    fn from(value: Kwh) -> Self {
80        value.0
81    }
82}
83
84impl fmt::Display for Kwh {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        write!(f, "{:.4}", self.0)
87    }
88}
89
90/// A value of kilo watts.
91#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
92#[serde(transparent)]
93pub struct Kw(Number);
94
95impl json::FromJson<'_, '_> for Kw {
96    type WarningKind = number::WarningKind;
97
98    fn from_json(elem: &'_ json::Element<'_>) -> crate::Verdict<Self, Self::WarningKind> {
99        Ok(Number::from_json(elem)?.map(Kw))
100    }
101}
102
103impl From<Kw> for Decimal {
104    fn from(value: Kw) -> Self {
105        value.0.into()
106    }
107}
108
109/// A value of amperes.
110#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
111#[serde(transparent)]
112pub struct Ampere(Number);
113
114impl json::FromJson<'_, '_> for Ampere {
115    type WarningKind = number::WarningKind;
116
117    fn from_json(elem: &'_ json::Element<'_>) -> crate::Verdict<Self, Self::WarningKind> {
118        Ok(Number::from_json(elem)?.map(Ampere))
119    }
120}
121
122impl From<Ampere> for Decimal {
123    fn from(value: Ampere) -> Self {
124        value.0.into()
125    }
126}
127
128#[cfg(test)]
129mod test {
130    use crate::Number;
131
132    use super::{Ampere, Kw, Kwh};
133
134    impl From<u64> for Kw {
135        fn from(value: u64) -> Self {
136            Self(value.into())
137        }
138    }
139
140    impl From<u64> for Kwh {
141        fn from(value: u64) -> Self {
142            Self(value.into())
143        }
144    }
145
146    impl From<Number> for Ampere {
147        fn from(value: Number) -> Self {
148            Self(value)
149        }
150    }
151}