bo4e_core/bo/
location_properties.rs

1//! Location properties (Standorteigenschaften) business object.
2//!
3//! Represents properties and characteristics of a physical location.
4
5use serde::{Deserialize, Serialize};
6
7use crate::com::{Address, GeoCoordinates};
8use crate::traits::{Bo4eMeta, Bo4eObject};
9
10/// Properties of a physical location.
11///
12/// German: Standorteigenschaften
13///
14/// Location properties describe characteristics of a physical
15/// site, such as address, coordinates, and site-specific details.
16///
17/// # Example
18///
19/// ```rust
20/// use bo4e_core::bo::LocationProperties;
21///
22/// let props = LocationProperties {
23///     location_properties_id: Some("LOC001".to_string()),
24///     building_type: Some("Residential".to_string()),
25///     ..Default::default()
26/// };
27/// ```
28#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
29#[serde(rename_all = "camelCase")]
30pub struct LocationProperties {
31    /// BO4E metadata
32    #[serde(flatten)]
33    pub meta: Bo4eMeta,
34
35    /// Location properties ID (Standorteigenschaften-ID)
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub location_properties_id: Option<String>,
38
39    /// Location address (Adresse)
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub address: Option<Address>,
42
43    /// Geographic coordinates (Geokoordinaten)
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub coordinates: Option<GeoCoordinates>,
46
47    /// Building type (Gebaeudeart)
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub building_type: Option<String>,
50
51    /// Construction year (Baujahr)
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub construction_year: Option<i32>,
54
55    /// Floor area in square meters (Flaeche)
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub floor_area: Option<f64>,
58
59    /// Number of floors (Anzahl Etagen)
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub number_of_floors: Option<i32>,
62
63    /// Number of residential units (Anzahl Wohneinheiten)
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub number_of_units: Option<i32>,
66
67    /// Heating type (Heizungsart)
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub heating_type: Option<String>,
70
71    /// Energy efficiency class (Energieeffizienzklasse)
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub energy_efficiency_class: Option<String>,
74
75    /// Has solar installation (Hat Solaranlage)
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub has_solar: Option<bool>,
78
79    /// Has electric vehicle charging (Hat E-Ladestation)
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub has_ev_charging: Option<bool>,
82
83    /// Has heat pump (Hat Waermepumpe)
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub has_heat_pump: Option<bool>,
86
87    /// Description (Beschreibung)
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub description: Option<String>,
90}
91
92impl Bo4eObject for LocationProperties {
93    fn type_name_german() -> &'static str {
94        "Standorteigenschaften"
95    }
96
97    fn type_name_english() -> &'static str {
98        "LocationProperties"
99    }
100
101    fn meta(&self) -> &Bo4eMeta {
102        &self.meta
103    }
104
105    fn meta_mut(&mut self) -> &mut Bo4eMeta {
106        &mut self.meta
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn test_location_properties_creation() {
116        let props = LocationProperties {
117            location_properties_id: Some("LOC001".to_string()),
118            building_type: Some("Residential".to_string()),
119            ..Default::default()
120        };
121
122        assert_eq!(props.location_properties_id, Some("LOC001".to_string()));
123    }
124
125    #[test]
126    fn test_serialize() {
127        let props = LocationProperties {
128            meta: Bo4eMeta::with_type("Standorteigenschaften"),
129            location_properties_id: Some("LOC001".to_string()),
130            ..Default::default()
131        };
132
133        let json = serde_json::to_string(&props).unwrap();
134        assert!(json.contains(r#""_typ":"Standorteigenschaften""#));
135    }
136
137    #[test]
138    fn test_roundtrip() {
139        let props = LocationProperties {
140            meta: Bo4eMeta::with_type("Standorteigenschaften"),
141            location_properties_id: Some("LOC001".to_string()),
142            building_type: Some("Residential".to_string()),
143            construction_year: Some(2010),
144            floor_area: Some(150.0),
145            number_of_floors: Some(2),
146            has_solar: Some(true),
147            ..Default::default()
148        };
149
150        let json = serde_json::to_string(&props).unwrap();
151        let parsed: LocationProperties = serde_json::from_str(&json).unwrap();
152        assert_eq!(props, parsed);
153    }
154
155    #[test]
156    fn test_bo4e_object_impl() {
157        assert_eq!(
158            LocationProperties::type_name_german(),
159            "Standorteigenschaften"
160        );
161        assert_eq!(
162            LocationProperties::type_name_english(),
163            "LocationProperties"
164        );
165    }
166}