bo4e_core/com/
consumption.rs

1//! Consumption (Verbrauch) component.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::enums::{MeasuredValueStatus, Unit};
7use crate::traits::{Bo4eMeta, Bo4eObject};
8
9/// Consumption data for a specific period.
10///
11/// German: Verbrauch
12///
13/// # Example
14///
15/// ```rust
16/// use bo4e_core::com::Consumption;
17/// use bo4e_core::enums::Unit;
18///
19/// let consumption = Consumption {
20///     value: Some(3660.0),
21///     unit: Some(Unit::KilowattHour),
22///     obis_code: Some("1-1:1.8.0".to_string()),
23///     ..Default::default()
24/// };
25/// ```
26#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
27#[serde(rename_all = "camelCase")]
28pub struct Consumption {
29    /// BO4E metadata
30    #[serde(flatten)]
31    pub meta: Bo4eMeta,
32
33    /// Consumption value (Wert)
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub value: Option<f64>,
36
37    /// Unit of measurement (Einheit)
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub unit: Option<Unit>,
40
41    /// Start date of consumption period inclusive (Startdatum)
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub start_date: Option<DateTime<Utc>>,
44
45    /// End date of consumption period exclusive (Enddatum)
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub end_date: Option<DateTime<Utc>>,
48
49    /// OBIS code identifying the measured value (OBIS-Kennzahl)
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub obis_code: Option<String>,
52
53    /// Status of the measured value (Messwertstatus)
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub measured_value_status: Option<MeasuredValueStatus>,
56}
57
58impl Bo4eObject for Consumption {
59    fn type_name_german() -> &'static str {
60        "Verbrauch"
61    }
62
63    fn type_name_english() -> &'static str {
64        "Consumption"
65    }
66
67    fn meta(&self) -> &Bo4eMeta {
68        &self.meta
69    }
70
71    fn meta_mut(&mut self) -> &mut Bo4eMeta {
72        &mut self.meta
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_electricity_consumption() {
82        let consumption = Consumption {
83            value: Some(3660.0),
84            unit: Some(Unit::KilowattHour),
85            obis_code: Some("1-1:1.8.0".to_string()),
86            measured_value_status: Some(MeasuredValueStatus::Read),
87            ..Default::default()
88        };
89
90        assert_eq!(consumption.value, Some(3660.0));
91        assert_eq!(consumption.unit, Some(Unit::KilowattHour));
92    }
93
94    #[test]
95    fn test_gas_consumption() {
96        let consumption = Consumption {
97            value: Some(1250.0),
98            unit: Some(Unit::CubicMeter),
99            obis_code: Some("7-0:3.0.0".to_string()),
100            ..Default::default()
101        };
102
103        assert_eq!(consumption.unit, Some(Unit::CubicMeter));
104    }
105
106    #[test]
107    fn test_default() {
108        let consumption = Consumption::default();
109        assert!(consumption.value.is_none());
110        assert!(consumption.unit.is_none());
111        assert!(consumption.obis_code.is_none());
112    }
113
114    #[test]
115    fn test_serialize() {
116        let consumption = Consumption {
117            value: Some(1500.0),
118            unit: Some(Unit::KilowattHour),
119            obis_code: Some("1-1:1.8.1".to_string()),
120            ..Default::default()
121        };
122
123        let json = serde_json::to_string(&consumption).unwrap();
124        assert!(json.contains(r#""value":1500"#));
125        assert!(json.contains(r#""obisCode":"1-1:1.8.1""#));
126    }
127
128    #[test]
129    fn test_roundtrip() {
130        let consumption = Consumption {
131            value: Some(4500.5),
132            unit: Some(Unit::KilowattHour),
133            start_date: Some(
134                DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z")
135                    .unwrap()
136                    .into(),
137            ),
138            end_date: Some(
139                DateTime::parse_from_rfc3339("2024-12-31T23:59:59Z")
140                    .unwrap()
141                    .into(),
142            ),
143            obis_code: Some("1-1:1.8.0".to_string()),
144            measured_value_status: Some(MeasuredValueStatus::Read),
145            ..Default::default()
146        };
147
148        let json = serde_json::to_string(&consumption).unwrap();
149        let parsed: Consumption = serde_json::from_str(&json).unwrap();
150        assert_eq!(consumption, parsed);
151    }
152
153    #[test]
154    fn test_bo4e_object_impl() {
155        assert_eq!(Consumption::type_name_german(), "Verbrauch");
156        assert_eq!(Consumption::type_name_english(), "Consumption");
157    }
158}