bo4e_core/com/
energy_mix.rs1use serde::{Deserialize, Serialize};
4
5use crate::enums::{Division, EcoCertificate, EcoLabel};
6use crate::traits::{Bo4eMeta, Bo4eObject};
7
8use super::EnergySource;
9
10#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
40#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
41#[cfg_attr(feature = "json-schema", schemars(rename = "Energiemix"))]
42#[serde(rename_all = "camelCase")]
43pub struct EnergyMix {
44 #[serde(flatten)]
46 pub meta: Bo4eMeta,
47
48 #[serde(skip_serializing_if = "Option::is_none")]
50 #[cfg_attr(feature = "json-schema", schemars(rename = "energiemixnummer"))]
51 pub energy_mix_number: Option<i32>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 #[cfg_attr(feature = "json-schema", schemars(rename = "sparte"))]
56 pub division: Option<Division>,
57
58 #[serde(skip_serializing_if = "Option::is_none")]
60 #[cfg_attr(feature = "json-schema", schemars(rename = "bezeichnung"))]
61 pub designation: Option<String>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 #[cfg_attr(feature = "json-schema", schemars(rename = "gueltigkeitsjahr"))]
66 pub valid_year: Option<i32>,
67
68 #[serde(default, skip_serializing_if = "Vec::is_empty")]
70 #[cfg_attr(feature = "json-schema", schemars(rename = "anteil"))]
71 pub sources: Vec<EnergySource>,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
75 #[cfg_attr(feature = "json-schema", schemars(rename = "bemerkung"))]
76 pub notes: Option<String>,
77
78 #[serde(skip_serializing_if = "Option::is_none")]
80 #[cfg_attr(feature = "json-schema", schemars(rename = "co2Emission"))]
81 pub co2_emission: Option<f64>,
82
83 #[serde(skip_serializing_if = "Option::is_none")]
85 #[cfg_attr(feature = "json-schema", schemars(rename = "atommuell"))]
86 pub nuclear_waste: Option<f64>,
87
88 #[serde(default, skip_serializing_if = "Vec::is_empty")]
90 #[cfg_attr(feature = "json-schema", schemars(rename = "oekozertifikate"))]
91 pub eco_certificates: Vec<EcoCertificate>,
92
93 #[serde(default, skip_serializing_if = "Vec::is_empty")]
95 #[cfg_attr(feature = "json-schema", schemars(rename = "oekolabel"))]
96 pub eco_labels: Vec<EcoLabel>,
97
98 #[serde(skip_serializing_if = "Option::is_none")]
100 #[cfg_attr(feature = "json-schema", schemars(rename = "istInOekoTopTen"))]
101 pub in_eco_top_ten: Option<bool>,
102
103 #[serde(skip_serializing_if = "Option::is_none")]
105 #[cfg_attr(feature = "json-schema", schemars(rename = "website"))]
106 pub website: Option<String>,
107}
108
109impl Bo4eObject for EnergyMix {
110 fn type_name_german() -> &'static str {
111 "Energiemix"
112 }
113
114 fn type_name_english() -> &'static str {
115 "EnergyMix"
116 }
117
118 fn meta(&self) -> &Bo4eMeta {
119 &self.meta
120 }
121
122 fn meta_mut(&mut self) -> &mut Bo4eMeta {
123 &mut self.meta
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use crate::enums::GenerationType;
131
132 #[test]
133 fn test_green_energy_mix() {
134 let mix = EnergyMix {
135 energy_mix_number: Some(1),
136 designation: Some("100% Renewable".to_string()),
137 valid_year: Some(2024),
138 sources: vec![
139 EnergySource {
140 generation_type: Some(GenerationType::Solar),
141 percentage_share: Some(40.0),
142 ..Default::default()
143 },
144 EnergySource {
145 generation_type: Some(GenerationType::Wind),
146 percentage_share: Some(60.0),
147 ..Default::default()
148 },
149 ],
150 co2_emission: Some(0.0),
151 nuclear_waste: Some(0.0),
152 in_eco_top_ten: Some(true),
153 ..Default::default()
154 };
155
156 assert_eq!(mix.sources.len(), 2);
157 assert_eq!(mix.co2_emission, Some(0.0));
158 assert_eq!(mix.in_eco_top_ten, Some(true));
159 }
160
161 #[test]
162 fn test_default() {
163 let mix = EnergyMix::default();
164 assert!(mix.energy_mix_number.is_none());
165 assert!(mix.designation.is_none());
166 assert!(mix.sources.is_empty());
167 }
168
169 #[test]
170 fn test_serialize() {
171 let mix = EnergyMix {
172 energy_mix_number: Some(42),
173 designation: Some("Test Mix".to_string()),
174 valid_year: Some(2024),
175 co2_emission: Some(150.5),
176 ..Default::default()
177 };
178
179 let json = serde_json::to_string(&mix).unwrap();
180 assert!(json.contains(r#""energyMixNumber":42"#));
181 assert!(json.contains(r#""designation":"Test Mix""#));
182 assert!(json.contains(r#""co2Emission":150.5"#));
183 }
184
185 #[test]
186 fn test_roundtrip() {
187 let mix = EnergyMix {
188 energy_mix_number: Some(1),
189 division: Some(Division::Electricity),
190 designation: Some("Ökostrom".to_string()),
191 valid_year: Some(2024),
192 sources: vec![EnergySource {
193 generation_type: Some(GenerationType::Hydro),
194 percentage_share: Some(100.0),
195 ..Default::default()
196 }],
197 co2_emission: Some(0.0),
198 eco_labels: vec![EcoLabel::GruenerStrom],
199 in_eco_top_ten: Some(true),
200 website: Some("https://example.com/energiemix".to_string()),
201 ..Default::default()
202 };
203
204 let json = serde_json::to_string(&mix).unwrap();
205 let parsed: EnergyMix = serde_json::from_str(&json).unwrap();
206 assert_eq!(mix, parsed);
207 }
208
209 #[test]
210 fn test_bo4e_object_impl() {
211 assert_eq!(EnergyMix::type_name_german(), "Energiemix");
212 assert_eq!(EnergyMix::type_name_english(), "EnergyMix");
213 }
214}