Skip to main content

bo4e_core/com/
interval.rs

1//! Interval component.
2
3use serde::{Deserialize, Serialize};
4
5use crate::enums::TimeUnit;
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8/// A time interval with duration and unit.
9///
10/// German: Intervall
11///
12/// # Example
13///
14/// ```rust
15/// use bo4e_core::com::Interval;
16/// use bo4e_core::enums::TimeUnit;
17///
18/// let interval = Interval {
19///     duration: Some(15),
20///     unit: Some(TimeUnit::Minute),
21///     ..Default::default()
22/// };
23/// ```
24#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
25#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
26#[cfg_attr(feature = "json-schema", schemars(rename = "Intervall"))]
27#[serde(rename_all = "camelCase")]
28pub struct Interval {
29    /// BO4E metadata
30    #[serde(flatten)]
31    pub meta: Bo4eMeta,
32
33    /// Duration value (Dauer)
34    #[serde(skip_serializing_if = "Option::is_none")]
35    #[cfg_attr(feature = "json-schema", schemars(rename = "dauer"))]
36    pub duration: Option<i32>,
37
38    /// Time unit (Zeiteinheit)
39    #[serde(skip_serializing_if = "Option::is_none")]
40    #[cfg_attr(feature = "json-schema", schemars(rename = "zeiteinheit"))]
41    pub unit: Option<TimeUnit>,
42}
43
44impl Bo4eObject for Interval {
45    fn type_name_german() -> &'static str {
46        "Intervall"
47    }
48
49    fn type_name_english() -> &'static str {
50        "Interval"
51    }
52
53    fn meta(&self) -> &Bo4eMeta {
54        &self.meta
55    }
56
57    fn meta_mut(&mut self) -> &mut Bo4eMeta {
58        &mut self.meta
59    }
60}
61
62impl Interval {
63    /// Create a 15-minute interval (common for load profiles).
64    pub fn minutes_15() -> Self {
65        Self {
66            duration: Some(15),
67            unit: Some(TimeUnit::Minute),
68            ..Default::default()
69        }
70    }
71
72    /// Create an hourly interval.
73    pub fn hourly() -> Self {
74        Self {
75            duration: Some(1),
76            unit: Some(TimeUnit::Hour),
77            ..Default::default()
78        }
79    }
80
81    /// Create a daily interval.
82    pub fn daily() -> Self {
83        Self {
84            duration: Some(1),
85            unit: Some(TimeUnit::Day),
86            ..Default::default()
87        }
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn test_15_minute_interval() {
97        let interval = Interval::minutes_15();
98        assert_eq!(interval.duration, Some(15));
99        assert_eq!(interval.unit, Some(TimeUnit::Minute));
100    }
101
102    #[test]
103    fn test_hourly_interval() {
104        let interval = Interval::hourly();
105        assert_eq!(interval.duration, Some(1));
106        assert_eq!(interval.unit, Some(TimeUnit::Hour));
107    }
108
109    #[test]
110    fn test_roundtrip() {
111        let interval = Interval::daily();
112        let json = serde_json::to_string(&interval).unwrap();
113        let parsed: Interval = serde_json::from_str(&json).unwrap();
114        assert_eq!(interval, parsed);
115    }
116
117    #[test]
118    fn test_bo4e_object_impl() {
119        assert_eq!(Interval::type_name_german(), "Intervall");
120        assert_eq!(Interval::type_name_english(), "Interval");
121    }
122}