Skip to main content

bo4e_core/com/
hardware.rs

1//! Hardware component.
2
3use serde::{Deserialize, Serialize};
4
5use crate::enums::{DeviceCategory, DeviceType};
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8/// Hardware component for representing physical devices.
9///
10/// This component represents hardware devices that are not meters,
11/// such as transformers, communication equipment, or other technical devices.
12///
13/// German: Hardware
14///
15/// # Example
16///
17/// ```rust
18/// use bo4e_core::com::Hardware;
19///
20/// let hw = Hardware {
21///     device_number: Some("HW-2024-001".to_string()),
22///     description: Some("Current transformer".to_string()),
23///     ..Default::default()
24/// };
25/// ```
26#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
27#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
28#[cfg_attr(feature = "json-schema", schemars(rename = "Hardware"))]
29#[serde(rename_all = "camelCase")]
30pub struct Hardware {
31    /// BO4E metadata
32    #[serde(flatten)]
33    pub meta: Bo4eMeta,
34
35    /// Device number assigned by the metering service operator (Gerätenummer)
36    #[serde(skip_serializing_if = "Option::is_none")]
37    #[cfg_attr(feature = "json-schema", schemars(rename = "geraetenummer"))]
38    pub device_number: Option<String>,
39
40    /// Description of the device (Bezeichnung)
41    #[serde(skip_serializing_if = "Option::is_none")]
42    #[cfg_attr(feature = "json-schema", schemars(rename = "bezeichnung"))]
43    pub description: Option<String>,
44
45    /// Category/class of the device (Geräteklasse)
46    #[serde(skip_serializing_if = "Option::is_none")]
47    #[cfg_attr(feature = "json-schema", schemars(rename = "geraeteklasse"))]
48    pub device_category: Option<DeviceCategory>,
49
50    /// Specific type of the device (Gerätetyp)
51    #[serde(skip_serializing_if = "Option::is_none")]
52    #[cfg_attr(feature = "json-schema", schemars(rename = "geraetetyp"))]
53    pub device_type: Option<DeviceType>,
54}
55
56impl Bo4eObject for Hardware {
57    fn type_name_german() -> &'static str {
58        "Hardware"
59    }
60
61    fn type_name_english() -> &'static str {
62        "Hardware"
63    }
64
65    fn meta(&self) -> &Bo4eMeta {
66        &self.meta
67    }
68
69    fn meta_mut(&mut self) -> &mut Bo4eMeta {
70        &mut self.meta
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_hardware_default() {
80        let hw = Hardware::default();
81        assert!(hw.device_number.is_none());
82        assert!(hw.description.is_none());
83    }
84
85    #[test]
86    fn test_hardware_serialize() {
87        let hw = Hardware {
88            device_number: Some("HW-001".to_string()),
89            description: Some("Transformer unit".to_string()),
90            ..Default::default()
91        };
92
93        let json = serde_json::to_string(&hw).unwrap();
94        assert!(json.contains(r#""deviceNumber":"HW-001""#));
95        assert!(json.contains(r#""description":"Transformer unit""#));
96    }
97
98    #[test]
99    fn test_hardware_roundtrip() {
100        let hw = Hardware {
101            meta: Bo4eMeta::with_type("Hardware"),
102            device_number: Some("HW-2024-002".to_string()),
103            description: Some("Communication gateway".to_string()),
104            device_category: None,
105            device_type: None,
106        };
107
108        let json = serde_json::to_string(&hw).unwrap();
109        let parsed: Hardware = serde_json::from_str(&json).unwrap();
110        assert_eq!(hw, parsed);
111    }
112
113    #[test]
114    fn test_bo4e_object_impl() {
115        assert_eq!(Hardware::type_name_german(), "Hardware");
116        assert_eq!(Hardware::type_name_english(), "Hardware");
117    }
118}