Skip to main content

edifact_rs/
ser.rs

1//! Custom serialization trait for EDIFACT.
2//!
3//! [`EdifactSerialize`] emits typed EDIFACT events rather than the generic
4//! key/value tokens of standard `serde`.  This matches EDIFACT's positional,
5//! qualifier-based data model — see `SPIKE_NOTES.md` for the design rationale.
6
7use crate::EdifactError;
8use crate::event::{EdifactEvent, EventEmitter, WriterEmitter};
9use std::io::Write;
10
11// ── trait ─────────────────────────────────────────────────────────────────────
12
13/// Types that can serialize themselves to an EDIFACT event stream.
14///
15/// Implement manually or derive with `#[derive(EdifactSerialize)]` from the
16/// `edifact-rs-derive` crate.
17pub trait EdifactSerialize {
18    /// Serialize `self` by emitting events into `emitter`.
19    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError>;
20}
21
22/// Types that can serialize themselves as a composite EDIFACT element.
23///
24/// Implement this for custom composite structs used with
25/// `#[edifact(composite)]` in derive macros.
26pub trait EdifactCompositeSerialize {
27    /// Serialize `self` as one composite element into `emitter`.
28    fn edifact_serialize_composite<E: EventEmitter>(
29        &self,
30        emitter: &mut E,
31    ) -> Result<(), EdifactError>;
32}
33
34impl EdifactCompositeSerialize for Vec<String> {
35    fn edifact_serialize_composite<E: EventEmitter>(
36        &self,
37        emitter: &mut E,
38    ) -> Result<(), EdifactError> {
39        if self.is_empty() {
40            return emitter.emit(EdifactEvent::Element { value: "" });
41        }
42
43        emitter.emit(EdifactEvent::Element { value: &self[0] })?;
44        for component in self.iter().skip(1) {
45            emitter.emit(EdifactEvent::ComponentElement { value: component })?;
46        }
47        Ok(())
48    }
49}
50
51// ── blanket impls for scalar types ────────────────────────────────────────────
52
53impl EdifactSerialize for str {
54    #[inline]
55    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
56        emitter.emit(EdifactEvent::Element { value: self })
57    }
58}
59
60impl EdifactSerialize for String {
61    #[inline]
62    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
63        emitter.emit(EdifactEvent::Element {
64            value: self.as_str(),
65        })
66    }
67}
68
69/// `None` → empty element `""`; `Some(v)` → `v.edifact_serialize(emitter)`.
70impl<T: EdifactSerialize> EdifactSerialize for Option<T> {
71    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
72        match self {
73            Some(v) => v.edifact_serialize(emitter),
74            None => emitter.emit(EdifactEvent::Element { value: "" }),
75        }
76    }
77}
78
79/// Each element is serialized independently (repeated segments for groups).
80impl<T: EdifactSerialize> EdifactSerialize for Vec<T> {
81    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
82        for item in self {
83            item.edifact_serialize(emitter)?;
84        }
85        Ok(())
86    }
87}
88
89/// Each element is serialized independently (repeated segments for groups).
90impl<T: EdifactSerialize> EdifactSerialize for [T] {
91    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
92        for item in self {
93            item.edifact_serialize(emitter)?;
94        }
95        Ok(())
96    }
97}
98
99macro_rules! impl_serialize_via_display {
100    ($($t:ty),+ $(,)?) => {
101        $(
102            impl EdifactSerialize for $t {
103                fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
104                    // Use a stack buffer to avoid heap allocation for short numeric values.
105                    // 40 bytes covers i128::MIN ("-170141183460469231731687303715884105728") + NUL.
106                    let mut buf = [0u8; 40];
107                    let s = {
108                        use std::io::Write as _;
109                        let mut cursor = std::io::Cursor::new(&mut buf[..]);
110                        write!(cursor, "{}", self).expect("numeric format into fixed buffer");
111                        let len = cursor.position() as usize;
112                        std::str::from_utf8(&buf[..len]).expect("numeric output is valid UTF-8")
113                    };
114                    emitter.emit(EdifactEvent::Element { value: s })
115                }
116            }
117        )+
118    };
119}
120
121impl_serialize_via_display!(
122    u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, bool
123);
124
125// ── public API ────────────────────────────────────────────────────────────────
126
127/// Serialize `value` to the given [`Write`] implementation.
128pub fn to_writer<T, W>(inner: W, value: &T) -> Result<(), EdifactError>
129where
130    T: EdifactSerialize,
131    W: Write,
132{
133    let mut emitter = WriterEmitter::new(inner);
134    value.edifact_serialize(&mut emitter)?;
135    emitter.finish().map(|_| ())
136}
137
138/// Serialize `value` to an owned `Vec<u8>`.
139pub fn to_bytes<T: EdifactSerialize>(value: &T) -> Result<Vec<u8>, EdifactError> {
140    let mut buf = Vec::new();
141    to_writer(&mut buf, value)?;
142    Ok(buf)
143}
144
145/// Serialize `value` to a UTF-8 `String`.
146///
147/// # Allocations
148///
149/// This function allocates twice: once for the `Vec<u8>` produced by
150/// [`to_bytes`], and once to convert that buffer into a `String`.  When you
151/// only need the raw bytes (e.g. for a network write), prefer [`to_bytes`]
152/// directly to avoid the second allocation.
153pub fn to_edifact_string<T: EdifactSerialize>(value: &T) -> Result<String, EdifactError> {
154    let bytes = to_bytes(value)?;
155    String::from_utf8(bytes).map_err(|_| EdifactError::InvalidUtf8)
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161    use crate::event::{OwnedEdifactEvent, VecEmitter};
162
163    struct BgmSegment {
164        doc_name_code: String,
165        pruef_id: String,
166        msg_function: Option<String>,
167    }
168
169    impl EdifactSerialize for BgmSegment {
170        fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
171            emitter.emit(EdifactEvent::StartSegment { tag: "BGM" })?;
172            emitter.emit(EdifactEvent::Element {
173                value: &self.doc_name_code,
174            })?;
175            emitter.emit(EdifactEvent::Element {
176                value: &self.pruef_id,
177            })?;
178            self.msg_function.edifact_serialize(emitter)?;
179            emitter.emit(EdifactEvent::EndSegment)?;
180            Ok(())
181        }
182    }
183
184    #[test]
185    fn vec_emitter_captures_segment_events() {
186        let seg = BgmSegment {
187            doc_name_code: "E03".to_owned(),
188            pruef_id: "11042".to_owned(),
189            msg_function: None,
190        };
191        let mut emitter = VecEmitter::default();
192        seg.edifact_serialize(&mut emitter).unwrap();
193
194        assert_eq!(
195            emitter.events[0],
196            OwnedEdifactEvent::StartSegment {
197                tag: "BGM".to_owned()
198            }
199        );
200        assert_eq!(emitter.events.last(), Some(&OwnedEdifactEvent::EndSegment));
201    }
202
203    #[test]
204    fn to_bytes_produces_valid_edifact() {
205        let seg = BgmSegment {
206            doc_name_code: "E03".to_owned(),
207            pruef_id: "11042".to_owned(),
208            msg_function: Some("9".to_owned()),
209        };
210        let bytes = to_bytes(&seg).unwrap();
211        assert_eq!(std::str::from_utf8(&bytes).unwrap(), "BGM+E03+11042+9'");
212    }
213
214    #[test]
215    fn option_none_emits_empty_element() {
216        let val: Option<String> = None;
217        let mut emitter = VecEmitter::default();
218        val.edifact_serialize(&mut emitter).unwrap();
219        assert_eq!(
220            emitter.events[0],
221            OwnedEdifactEvent::Element {
222                value: String::new()
223            }
224        );
225    }
226
227    #[test]
228    fn option_some_emits_value() {
229        let val: Option<String> = Some("TEST".to_owned());
230        let mut emitter = VecEmitter::default();
231        val.edifact_serialize(&mut emitter).unwrap();
232        assert_eq!(
233            emitter.events[0],
234            OwnedEdifactEvent::Element {
235                value: "TEST".to_owned()
236            }
237        );
238    }
239
240    #[test]
241    fn numeric_types_serialize_via_display() {
242        let mut emitter = VecEmitter::default();
243        42u32.edifact_serialize(&mut emitter).unwrap();
244        assert_eq!(
245            emitter.events[0],
246            OwnedEdifactEvent::Element {
247                value: "42".to_owned()
248            }
249        );
250    }
251
252    #[test]
253    fn vec_serializes_each_item() {
254        let segments = vec![
255            BgmSegment {
256                doc_name_code: "E03".to_owned(),
257                pruef_id: "11042".to_owned(),
258                msg_function: None,
259            },
260            BgmSegment {
261                doc_name_code: "E01".to_owned(),
262                pruef_id: "11043".to_owned(),
263                msg_function: None,
264            },
265        ];
266        let bytes = to_bytes(&segments).unwrap();
267        let s = std::str::from_utf8(&bytes).unwrap();
268        assert!(s.contains("BGM+E03+11042"));
269        assert!(s.contains("BGM+E01+11043"));
270    }
271}