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}
14
15#[derive(Debug, Clone, Default, Serialize, Deserialize)]
23#[serde(rename_all = "camelCase")]
24pub struct Nachrichtendaten {
25 pub unh_referenz: String,
26 pub nachrichten_typ: String,
27
28 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub zuordnungsreferenz: Option<String>,
35
36 #[serde(default, skip_serializing_if = "Option::is_none")]
42 pub uebermittlungsfolgenummer: Option<String>,
43
44 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub uebermittlungsabschnitt: Option<Uebermittlungsabschnitt>,
50
51 #[serde(flatten, default)]
60 pub nachricht: serde_json::Map<String, serde_json::Value>,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
69#[serde(rename_all = "camelCase")]
70pub enum Uebermittlungsabschnitt {
71 Beginn,
72 Ende,
73}
74
75impl Uebermittlungsabschnitt {
76 pub fn code(self) -> &'static str {
78 match self {
79 Self::Beginn => "C",
80 Self::Ende => "F",
81 }
82 }
83
84 pub fn from_code(code: &str) -> Option<Self> {
88 match code {
89 "C" => Some(Self::Beginn),
90 "F" => Some(Self::Ende),
91 _ => None,
92 }
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98#[serde(rename_all = "camelCase")]
99pub struct Interchange<M, T> {
100 pub interchangedaten: Interchangedaten,
101 pub nachrichten: Vec<Nachricht<M, T>>,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
106#[serde(rename_all = "camelCase")]
107pub struct Nachricht<M, T> {
108 pub nachrichtendaten: Nachrichtendaten,
109 pub stammdaten: M,
110 pub transaktionen: Vec<T>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
121#[serde(rename_all = "camelCase")]
122pub struct Transaktion<D, S> {
123 #[serde(default)]
127 pub transaktionsdaten: D,
128 pub stammdaten: S,
129}
130
131pub fn is_wrapped_transaktion(tx: &serde_json::Value) -> bool {
147 tx.get("stammdaten").is_some() || tx.get("transaktionsdaten").is_some()
148}
149
150pub type DynamicTransaktion = Transaktion<serde_json::Value, serde_json::Value>;
152pub type DynamicInterchange = Interchange<serde_json::Value, DynamicTransaktion>;
154pub type DynamicNachricht = Nachricht<serde_json::Value, DynamicTransaktion>;
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use serde_json::json;
161
162 #[test]
163 fn test_dynamic_interchange_serde_roundtrip() {
164 let interchange: DynamicInterchange = Interchange {
165 interchangedaten: Interchangedaten {
166 syntax_kennung: Some("UNOC".to_string()),
167 absender_code: Some("1234567890123".to_string()),
168 empfaenger_code: Some("9876543210987".to_string()),
169 datum: Some("260326".to_string()),
170 zeit: Some("1430".to_string()),
171 interchange_ref: Some("00001".to_string()),
172 },
173 nachrichten: vec![Nachricht {
174 nachrichtendaten: Nachrichtendaten {
175 unh_referenz: "00001".to_string(),
176 nachrichten_typ: "UTILMD".to_string(),
177 nachricht: Default::default(),
178 ..Default::default()
179 },
180 stammdaten: json!({"key": "value"}),
181 transaktionen: vec![
182 Transaktion {
183 transaktionsdaten: json!({"vorgangId": "1"}),
184 stammdaten: json!({"tx": 1}),
185 },
186 Transaktion {
187 transaktionsdaten: json!({"vorgangId": "2"}),
188 stammdaten: json!({"tx": 2}),
189 },
190 ],
191 }],
192 };
193
194 let json_str = serde_json::to_string(&interchange).unwrap();
195 let deserialized: DynamicInterchange = serde_json::from_str(&json_str).unwrap();
196
197 assert_eq!(
198 deserialized.interchangedaten.syntax_kennung,
199 Some("UNOC".to_string())
200 );
201 assert_eq!(
202 deserialized.interchangedaten.absender_code,
203 Some("1234567890123".to_string())
204 );
205 assert_eq!(
206 deserialized.interchangedaten.empfaenger_code,
207 Some("9876543210987".to_string())
208 );
209 assert_eq!(deserialized.nachrichten.len(), 1);
210 assert_eq!(
211 deserialized.nachrichten[0].nachrichtendaten.unh_referenz,
212 "00001"
213 );
214 assert_eq!(
215 deserialized.nachrichten[0].nachrichtendaten.nachrichten_typ,
216 "UTILMD"
217 );
218 assert_eq!(
219 deserialized.nachrichten[0].stammdaten,
220 json!({"key": "value"})
221 );
222 assert_eq!(deserialized.nachrichten[0].transaktionen.len(), 2);
223 }
224
225 #[test]
226 fn test_typed_interchange_serde_roundtrip() {
227 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
228 struct TestStamm {
229 name: String,
230 }
231
232 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
233 struct TestTx {
234 id: u32,
235 }
236
237 let interchange: Interchange<TestStamm, TestTx> = Interchange {
238 interchangedaten: Interchangedaten::default(),
239 nachrichten: vec![Nachricht {
240 nachrichtendaten: Nachrichtendaten {
241 unh_referenz: "REF1".to_string(),
242 nachrichten_typ: "ORDERS".to_string(),
243 nachricht: Default::default(),
244 ..Default::default()
245 },
246 stammdaten: TestStamm {
247 name: "test".to_string(),
248 },
249 transaktionen: vec![TestTx { id: 1 }, TestTx { id: 2 }],
250 }],
251 };
252
253 let json_str = serde_json::to_string(&interchange).unwrap();
254 let deserialized: Interchange<TestStamm, TestTx> = serde_json::from_str(&json_str).unwrap();
255
256 assert_eq!(deserialized.nachrichten.len(), 1);
257 assert_eq!(
258 deserialized.nachrichten[0].stammdaten,
259 TestStamm {
260 name: "test".to_string()
261 }
262 );
263 assert_eq!(deserialized.nachrichten[0].transaktionen.len(), 2);
264 assert_eq!(
265 deserialized.nachrichten[0].transaktionen[0],
266 TestTx { id: 1 }
267 );
268 assert_eq!(
269 deserialized.nachrichten[0].transaktionen[1],
270 TestTx { id: 2 }
271 );
272 }
273
274 #[test]
275 fn test_interchangedaten_camel_case_serialization() {
276 let data = Interchangedaten {
277 syntax_kennung: Some("UNOC".to_string()),
278 absender_code: Some("SENDER".to_string()),
279 empfaenger_code: None,
280 datum: None,
281 zeit: None,
282 interchange_ref: Some("REF".to_string()),
283 };
284
285 let json_value: serde_json::Value = serde_json::to_value(&data).unwrap();
286 let obj = json_value.as_object().unwrap();
287
288 assert!(obj.contains_key("syntaxKennung"));
290 assert!(obj.contains_key("absenderCode"));
291 assert!(obj.contains_key("empfaengerCode"));
292 assert!(obj.contains_key("datum"));
293 assert!(obj.contains_key("zeit"));
294 assert!(obj.contains_key("interchangeRef"));
295
296 assert!(!obj.contains_key("syntax_kennung"));
298 assert!(!obj.contains_key("absender_code"));
299 assert!(!obj.contains_key("empfaenger_code"));
300 assert!(!obj.contains_key("interchange_ref"));
301 }
302}