Skip to main content

bo4e_edifact_types/
model.rs

1use serde::{Deserialize, Serialize};
2
3/// EDIFACT interchange envelope metadata (UNB/UNZ).
4#[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/// EDIFACT message metadata (UNH/UNT).
16#[derive(Debug, Clone, Serialize, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct Nachrichtendaten {
19    pub unh_referenz: String,
20    pub nachrichten_typ: String,
21}
22
23/// A complete EDIFACT interchange. M = message stammdaten, T = transaction stammdaten.
24#[derive(Debug, Clone, Serialize, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct Interchange<M, T> {
27    pub interchangedaten: Interchangedaten,
28    pub nachrichten: Vec<Nachricht<M, T>>,
29}
30
31/// A single EDIFACT message within an interchange.
32#[derive(Debug, Clone, Serialize, Deserialize)]
33#[serde(rename_all = "camelCase")]
34pub struct Nachricht<M, T> {
35    pub nachrichtendaten: Nachrichtendaten,
36    pub stammdaten: M,
37    pub transaktionen: Vec<T>,
38}
39
40/// Dynamic variant for untyped usage.
41pub type DynamicInterchange = Interchange<serde_json::Value, serde_json::Value>;
42/// Dynamic variant for untyped message usage.
43pub type DynamicNachricht = Nachricht<serde_json::Value, serde_json::Value>;
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use serde_json::json;
49
50    #[test]
51    fn test_dynamic_interchange_serde_roundtrip() {
52        let interchange: DynamicInterchange = Interchange {
53            interchangedaten: Interchangedaten {
54                syntax_kennung: Some("UNOC".to_string()),
55                absender_code: Some("1234567890123".to_string()),
56                empfaenger_code: Some("9876543210987".to_string()),
57                datum: Some("20260326".to_string()),
58                zeit: Some("1430".to_string()),
59                interchange_ref: Some("00001".to_string()),
60            },
61            nachrichten: vec![Nachricht {
62                nachrichtendaten: Nachrichtendaten {
63                    unh_referenz: "00001".to_string(),
64                    nachrichten_typ: "UTILMD".to_string(),
65                },
66                stammdaten: json!({"key": "value"}),
67                transaktionen: vec![json!({"tx": 1}), json!({"tx": 2})],
68            }],
69        };
70
71        let json_str = serde_json::to_string(&interchange).unwrap();
72        let deserialized: DynamicInterchange = serde_json::from_str(&json_str).unwrap();
73
74        assert_eq!(
75            deserialized.interchangedaten.syntax_kennung,
76            Some("UNOC".to_string())
77        );
78        assert_eq!(
79            deserialized.interchangedaten.absender_code,
80            Some("1234567890123".to_string())
81        );
82        assert_eq!(
83            deserialized.interchangedaten.empfaenger_code,
84            Some("9876543210987".to_string())
85        );
86        assert_eq!(deserialized.nachrichten.len(), 1);
87        assert_eq!(
88            deserialized.nachrichten[0].nachrichtendaten.unh_referenz,
89            "00001"
90        );
91        assert_eq!(
92            deserialized.nachrichten[0].nachrichtendaten.nachrichten_typ,
93            "UTILMD"
94        );
95        assert_eq!(
96            deserialized.nachrichten[0].stammdaten,
97            json!({"key": "value"})
98        );
99        assert_eq!(deserialized.nachrichten[0].transaktionen.len(), 2);
100    }
101
102    #[test]
103    fn test_typed_interchange_serde_roundtrip() {
104        #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
105        struct TestStamm {
106            name: String,
107        }
108
109        #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
110        struct TestTx {
111            id: u32,
112        }
113
114        let interchange: Interchange<TestStamm, TestTx> = Interchange {
115            interchangedaten: Interchangedaten::default(),
116            nachrichten: vec![Nachricht {
117                nachrichtendaten: Nachrichtendaten {
118                    unh_referenz: "REF1".to_string(),
119                    nachrichten_typ: "ORDERS".to_string(),
120                },
121                stammdaten: TestStamm {
122                    name: "test".to_string(),
123                },
124                transaktionen: vec![TestTx { id: 1 }, TestTx { id: 2 }],
125            }],
126        };
127
128        let json_str = serde_json::to_string(&interchange).unwrap();
129        let deserialized: Interchange<TestStamm, TestTx> = serde_json::from_str(&json_str).unwrap();
130
131        assert_eq!(deserialized.nachrichten.len(), 1);
132        assert_eq!(
133            deserialized.nachrichten[0].stammdaten,
134            TestStamm {
135                name: "test".to_string()
136            }
137        );
138        assert_eq!(deserialized.nachrichten[0].transaktionen.len(), 2);
139        assert_eq!(
140            deserialized.nachrichten[0].transaktionen[0],
141            TestTx { id: 1 }
142        );
143        assert_eq!(
144            deserialized.nachrichten[0].transaktionen[1],
145            TestTx { id: 2 }
146        );
147    }
148
149    #[test]
150    fn test_interchangedaten_camel_case_serialization() {
151        let data = Interchangedaten {
152            syntax_kennung: Some("UNOC".to_string()),
153            absender_code: Some("SENDER".to_string()),
154            empfaenger_code: None,
155            datum: None,
156            zeit: None,
157            interchange_ref: Some("REF".to_string()),
158        };
159
160        let json_value: serde_json::Value = serde_json::to_value(&data).unwrap();
161        let obj = json_value.as_object().unwrap();
162
163        // Verify camelCase keys
164        assert!(obj.contains_key("syntaxKennung"));
165        assert!(obj.contains_key("absenderCode"));
166        assert!(obj.contains_key("empfaengerCode"));
167        assert!(obj.contains_key("datum"));
168        assert!(obj.contains_key("zeit"));
169        assert!(obj.contains_key("interchangeRef"));
170
171        // Verify no snake_case keys
172        assert!(!obj.contains_key("syntax_kennung"));
173        assert!(!obj.contains_key("absender_code"));
174        assert!(!obj.contains_key("empfaenger_code"));
175        assert!(!obj.contains_key("interchange_ref"));
176    }
177}