bo4e_core/bo/
region.rs

1//! Region (Region) business object.
2
3use serde::{Deserialize, Serialize};
4
5use crate::com::RegionCriterion;
6use crate::enums::RegionType;
7use crate::traits::{Bo4eMeta, Bo4eObject};
8
9/// A geographical region in the energy market.
10///
11/// German: Region
12///
13/// # Example
14///
15/// ```rust
16/// use bo4e_core::bo::Region;
17/// use bo4e_core::enums::RegionType;
18///
19/// let region = Region {
20///     region_code: Some("DE-BY".to_string()),
21///     name: Some("Bavaria".to_string()),
22///     region_type: Some(RegionType::SupplyArea),
23///     ..Default::default()
24/// };
25/// ```
26#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
27#[serde(rename_all = "camelCase")]
28pub struct Region {
29    /// BO4E metadata
30    #[serde(flatten)]
31    pub meta: Bo4eMeta,
32
33    /// Region code (Regionscode)
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub region_code: Option<String>,
36
37    /// Region name (Name)
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub name: Option<String>,
40
41    /// Description (Beschreibung)
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub description: Option<String>,
44
45    /// Type of region (Gebietstyp)
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub region_type: Option<RegionType>,
48
49    /// Criteria that define this region (Regionskriterien)
50    #[serde(default, skip_serializing_if = "Vec::is_empty")]
51    pub criteria: Vec<RegionCriterion>,
52
53    /// Parent region (Uebergeordnete Region)
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub parent_region: Option<Box<Region>>,
56
57    /// Sub-regions (Unterregionen)
58    #[serde(default, skip_serializing_if = "Vec::is_empty")]
59    pub sub_regions: Vec<Box<Region>>,
60}
61
62impl Bo4eObject for Region {
63    fn type_name_german() -> &'static str {
64        "Region"
65    }
66
67    fn type_name_english() -> &'static str {
68        "Region"
69    }
70
71    fn meta(&self) -> &Bo4eMeta {
72        &self.meta
73    }
74
75    fn meta_mut(&mut self) -> &mut Bo4eMeta {
76        &mut self.meta
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn test_region_creation() {
86        let region = Region {
87            region_code: Some("DE-BY".to_string()),
88            name: Some("Bavaria".to_string()),
89            region_type: Some(RegionType::SupplyArea),
90            ..Default::default()
91        };
92
93        assert_eq!(region.region_code, Some("DE-BY".to_string()));
94        assert_eq!(region.region_type, Some(RegionType::SupplyArea));
95    }
96
97    #[test]
98    fn test_market_area_region() {
99        let region = Region {
100            region_code: Some("TRADING-HUB-EUROPE".to_string()),
101            name: Some("Trading Hub Europe".to_string()),
102            region_type: Some(RegionType::MarketArea),
103            ..Default::default()
104        };
105
106        assert_eq!(region.region_type, Some(RegionType::MarketArea));
107    }
108
109    #[test]
110    fn test_nested_regions() {
111        let sub_region = Box::new(Region {
112            region_code: Some("DE-BY-M".to_string()),
113            name: Some("Munich Area".to_string()),
114            ..Default::default()
115        });
116
117        let region = Region {
118            region_code: Some("DE-BY".to_string()),
119            name: Some("Bavaria".to_string()),
120            sub_regions: vec![sub_region],
121            ..Default::default()
122        };
123
124        assert_eq!(region.sub_regions.len(), 1);
125        assert_eq!(region.sub_regions[0].name, Some("Munich Area".to_string()));
126    }
127
128    #[test]
129    fn test_serialize() {
130        let region = Region {
131            meta: Bo4eMeta::with_type("Region"),
132            region_code: Some("DE-BY".to_string()),
133            name: Some("Bavaria".to_string()),
134            ..Default::default()
135        };
136
137        let json = serde_json::to_string(&region).unwrap();
138        assert!(json.contains(r#""regionCode":"DE-BY""#));
139        assert!(json.contains(r#""name":"Bavaria""#));
140    }
141
142    #[test]
143    fn test_roundtrip() {
144        let region = Region {
145            meta: Bo4eMeta::with_type("Region"),
146            region_code: Some("DE-BY".to_string()),
147            name: Some("Bavaria".to_string()),
148            description: Some("State of Bavaria".to_string()),
149            region_type: Some(RegionType::SupplyArea),
150            ..Default::default()
151        };
152
153        let json = serde_json::to_string(&region).unwrap();
154        let parsed: Region = serde_json::from_str(&json).unwrap();
155        assert_eq!(region, parsed);
156    }
157
158    #[test]
159    fn test_bo4e_object_impl() {
160        assert_eq!(Region::type_name_german(), "Region");
161        assert_eq!(Region::type_name_english(), "Region");
162    }
163}