1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Default, Serialize, Deserialize)]
5#[serde(rename_all = "camelCase")]
6pub struct Interchangedaten {
7 pub syntax_kennung: Option<String>,
8 pub absender_code: Option<String>,
9 pub empfaenger_code: Option<String>,
10 pub datum: Option<String>,
11 pub zeit: Option<String>,
12 pub interchange_ref: Option<String>,
13 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub absender_qualifier: Option<String>,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
18 pub empfaenger_qualifier: Option<String>,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub anwendungsreferenz: Option<String>,
22}
23
24#[derive(Debug, Clone, Default, Serialize, Deserialize)]
32#[serde(rename_all = "camelCase")]
33pub struct Nachrichtendaten {
34 pub unh_referenz: String,
35 pub nachrichten_typ: String,
36
37 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub zuordnungsreferenz: Option<String>,
44
45 #[serde(default, skip_serializing_if = "Option::is_none")]
51 pub uebermittlungsfolgenummer: Option<String>,
52
53 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub uebermittlungsabschnitt: Option<Uebermittlungsabschnitt>,
59
60 #[serde(flatten, default)]
69 pub nachricht: serde_json::Map<String, serde_json::Value>,
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
78#[serde(rename_all = "camelCase")]
79pub enum Uebermittlungsabschnitt {
80 Beginn,
81 Ende,
82}
83
84impl Uebermittlungsabschnitt {
85 pub fn code(self) -> &'static str {
87 match self {
88 Self::Beginn => "C",
89 Self::Ende => "F",
90 }
91 }
92
93 pub fn from_code(code: &str) -> Option<Self> {
97 match code {
98 "C" => Some(Self::Beginn),
99 "F" => Some(Self::Ende),
100 _ => None,
101 }
102 }
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107#[serde(rename_all = "camelCase")]
108pub struct Interchange<M, T> {
109 pub interchangedaten: Interchangedaten,
110 pub nachrichten: Vec<Nachricht<M, T>>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
115#[serde(rename_all = "camelCase")]
116pub struct Nachricht<M, T> {
117 pub nachrichtendaten: Nachrichtendaten,
118 pub stammdaten: M,
119 pub transaktionen: Vec<T>,
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
130#[serde(rename_all = "camelCase")]
131pub struct Transaktion<D, S> {
132 #[serde(default)]
136 pub transaktionsdaten: D,
137 pub stammdaten: S,
138}
139
140pub fn is_wrapped_transaktion(tx: &serde_json::Value) -> bool {
156 tx.get("stammdaten").is_some() || tx.get("transaktionsdaten").is_some()
157}
158
159pub type DynamicTransaktion = Transaktion<serde_json::Value, serde_json::Value>;
161pub type DynamicInterchange = Interchange<serde_json::Value, DynamicTransaktion>;
163pub type DynamicNachricht = Nachricht<serde_json::Value, DynamicTransaktion>;
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use serde_json::json;
170
171 #[test]
172 fn test_dynamic_interchange_serde_roundtrip() {
173 let interchange: DynamicInterchange = Interchange {
174 interchangedaten: Interchangedaten {
175 syntax_kennung: Some("UNOC".to_string()),
176 absender_code: Some("1234567890123".to_string()),
177 empfaenger_code: Some("9876543210987".to_string()),
178 datum: Some("260326".to_string()),
179 zeit: Some("1430".to_string()),
180 interchange_ref: Some("00001".to_string()),
181 absender_qualifier: None,
182 empfaenger_qualifier: None,
183 anwendungsreferenz: None,
184 },
185 nachrichten: vec![Nachricht {
186 nachrichtendaten: Nachrichtendaten {
187 unh_referenz: "00001".to_string(),
188 nachrichten_typ: "UTILMD".to_string(),
189 nachricht: Default::default(),
190 ..Default::default()
191 },
192 stammdaten: json!({"key": "value"}),
193 transaktionen: vec![
194 Transaktion {
195 transaktionsdaten: json!({"vorgangId": "1"}),
196 stammdaten: json!({"tx": 1}),
197 },
198 Transaktion {
199 transaktionsdaten: json!({"vorgangId": "2"}),
200 stammdaten: json!({"tx": 2}),
201 },
202 ],
203 }],
204 };
205
206 let json_str = serde_json::to_string(&interchange).unwrap();
207 let deserialized: DynamicInterchange = serde_json::from_str(&json_str).unwrap();
208
209 assert_eq!(
210 deserialized.interchangedaten.syntax_kennung,
211 Some("UNOC".to_string())
212 );
213 assert_eq!(
214 deserialized.interchangedaten.absender_code,
215 Some("1234567890123".to_string())
216 );
217 assert_eq!(
218 deserialized.interchangedaten.empfaenger_code,
219 Some("9876543210987".to_string())
220 );
221 assert_eq!(deserialized.nachrichten.len(), 1);
222 assert_eq!(
223 deserialized.nachrichten[0].nachrichtendaten.unh_referenz,
224 "00001"
225 );
226 assert_eq!(
227 deserialized.nachrichten[0].nachrichtendaten.nachrichten_typ,
228 "UTILMD"
229 );
230 assert_eq!(
231 deserialized.nachrichten[0].stammdaten,
232 json!({"key": "value"})
233 );
234 assert_eq!(deserialized.nachrichten[0].transaktionen.len(), 2);
235 }
236
237 #[test]
238 fn test_typed_interchange_serde_roundtrip() {
239 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
240 struct TestStamm {
241 name: String,
242 }
243
244 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
245 struct TestTx {
246 id: u32,
247 }
248
249 let interchange: Interchange<TestStamm, TestTx> = Interchange {
250 interchangedaten: Interchangedaten::default(),
251 nachrichten: vec![Nachricht {
252 nachrichtendaten: Nachrichtendaten {
253 unh_referenz: "REF1".to_string(),
254 nachrichten_typ: "ORDERS".to_string(),
255 nachricht: Default::default(),
256 ..Default::default()
257 },
258 stammdaten: TestStamm {
259 name: "test".to_string(),
260 },
261 transaktionen: vec![TestTx { id: 1 }, TestTx { id: 2 }],
262 }],
263 };
264
265 let json_str = serde_json::to_string(&interchange).unwrap();
266 let deserialized: Interchange<TestStamm, TestTx> = serde_json::from_str(&json_str).unwrap();
267
268 assert_eq!(deserialized.nachrichten.len(), 1);
269 assert_eq!(
270 deserialized.nachrichten[0].stammdaten,
271 TestStamm {
272 name: "test".to_string()
273 }
274 );
275 assert_eq!(deserialized.nachrichten[0].transaktionen.len(), 2);
276 assert_eq!(
277 deserialized.nachrichten[0].transaktionen[0],
278 TestTx { id: 1 }
279 );
280 assert_eq!(
281 deserialized.nachrichten[0].transaktionen[1],
282 TestTx { id: 2 }
283 );
284 }
285
286 #[test]
287 fn test_interchangedaten_camel_case_serialization() {
288 let data = Interchangedaten {
289 syntax_kennung: Some("UNOC".to_string()),
290 absender_code: Some("SENDER".to_string()),
291 empfaenger_code: None,
292 datum: None,
293 zeit: None,
294 interchange_ref: Some("REF".to_string()),
295 absender_qualifier: None,
296 empfaenger_qualifier: None,
297 anwendungsreferenz: None,
298 };
299
300 let json_value: serde_json::Value = serde_json::to_value(&data).unwrap();
301 let obj = json_value.as_object().unwrap();
302
303 assert!(obj.contains_key("syntaxKennung"));
305 assert!(obj.contains_key("absenderCode"));
306 assert!(obj.contains_key("empfaengerCode"));
307 assert!(obj.contains_key("datum"));
308 assert!(obj.contains_key("zeit"));
309 assert!(obj.contains_key("interchangeRef"));
310
311 assert!(!obj.contains_key("syntax_kennung"));
313 assert!(!obj.contains_key("absender_code"));
314 assert!(!obj.contains_key("empfaenger_code"));
315 assert!(!obj.contains_key("interchange_ref"));
316 }
317}