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 `docs/writing.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_int {
100    ($($t:ty),+ $(,)?) => {
101        $(
102            impl EdifactSerialize for $t {
103                fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
104                    // 42-byte buffer: i128::MIN is 40 chars; 2 spare bytes as safety margin.
105                    use std::io::Write as _;
106                    let mut buf = [0u8; 42];
107                    let mut w: &mut [u8] = &mut buf;
108                    if write!(w, "{self}").is_ok() {
109                        let written = 42 - w.len();
110                        // Display output for all integer/bool types is ASCII-only.
111                        let s = std::str::from_utf8(&buf[..written]).map_err(|_| EdifactError::InvalidUtf8)?;
112                        emitter.emit(EdifactEvent::Element { value: s })
113                    } else {
114                        // Extraordinary case: fall back to heap to avoid any panic.
115                        let s = format!("{self}");
116                        emitter.emit(EdifactEvent::Element { value: &s })
117                    }
118                }
119            }
120        )+
121    };
122}
123
124// Boolean is also bounded (max "false" = 5 bytes).
125impl_serialize_int!(
126    u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, bool
127);
128
129// Rust's float Display picks the shortest round-trip decimal representation,
130// which may be fixed-point or scientific notation depending on the magnitude.
131// A 320-byte stack buffer covers all known finite f32/f64 Display forms;
132// if the buffer is somehow exceeded we fall back to a heap-allocated String
133// so no panic ever escapes to the caller.
134macro_rules! impl_serialize_float {
135    ($($t:ty),+ $(,)?) => {
136        $(
137            impl EdifactSerialize for $t {
138                fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
139                    use std::io::Write as _;
140                    let mut buf = [0u8; 320];
141                    let mut w: &mut [u8] = &mut buf;
142                    if write!(w, "{self}").is_ok() {
143                        let written = 320 - w.len();
144                        // SAFETY: float Display only emits ASCII digits and punctuation.
145                        let s = std::str::from_utf8(&buf[..written]).map_err(|_| EdifactError::InvalidUtf8)?;
146                        emitter.emit(EdifactEvent::Element { value: s })
147                    } else {
148                        // Extraordinary case: format via heap to avoid any panic.
149                        let s = format!("{self}");
150                        emitter.emit(EdifactEvent::Element { value: &s })
151                    }
152                }
153            }
154        )+
155    };
156}
157
158impl_serialize_float!(f32, f64);
159
160// ── decimal-mark-aware bare float wrappers ────────────────────────────────────
161//
162// The bare f32/f64 impls above always use `.` as the decimal separator
163// (Rust's standard Display).  They are intentionally kept as the default to
164// avoid a performance penalty on the common case.  If your interchange
165// declares a different decimal mark in UNA byte 5 (e.g. `,`), you MUST wrap
166// the value in `DecimalFloat` — the bare impls will produce **silent data
167// corruption** for non-`.` interchanges.
168//
169// See also: the `DecimalFloat` / `DecimalFloatDisplay` section below.
170
171// ── decimal-mark-aware float wrapper ─────────────────────────────────────────
172
173/// Decimal-mark-aware wrapper for `f32` or `f64` serialization.
174///
175/// Rust's [`Display`][std::fmt::Display] for `f32`/`f64` always uses `.` as the
176/// decimal separator.  EDIFACT interchanges can declare a different decimal mark
177/// in the UNA service string — most commonly `,` in German EDI\@Energy messages.
178///
179/// # Why you need this
180///
181/// The bare `f32`/`f64` [`EdifactSerialize`] impls are correct for standard (`.`)
182/// interchanges **but produce silent data corruption for any interchange that
183/// declares `decimal_mark != b'.'`**.  Wrap the value in `DecimalFloat` whenever
184/// the interchange may use a non-`.` decimal mark:
185///
186/// ```
187/// use edifact_rs::ser::DecimalFloat;
188/// use edifact_rs::{EdifactSerialize, VecEmitter, OwnedEdifactEvent};
189///
190/// let mut emitter = VecEmitter::default();
191/// DecimalFloat(12.5_f64).edifact_serialize(&mut emitter).unwrap();
192/// assert!(matches!(&emitter.events[0], OwnedEdifactEvent::Element { value } if value == "12.5"));
193/// ```
194///
195/// When the emitter's [`EventEmitter::decimal_mark`] is `b','`, the output will be `"12,5"`.
196///
197/// # Supported inner types
198///
199/// `DecimalFloat<f32>`, `DecimalFloat<f64>`. For any type that implements
200/// [`std::fmt::Display`], use [`DecimalFloatDisplay`] instead.
201#[derive(Debug, Clone, Copy, PartialEq)]
202pub struct DecimalFloat<T>(pub T);
203
204/// Decimal-mark-aware serializer for any [`std::fmt::Display`] value.
205///
206/// Like [`DecimalFloat`] but works with any type whose `Display` uses `.` as a
207/// decimal point (e.g., `rust_decimal::Decimal`, `bigdecimal::BigDecimal`).
208#[derive(Debug, Clone, Copy, PartialEq)]
209pub struct DecimalFloatDisplay<T: std::fmt::Display>(pub T);
210
211fn serialize_with_decimal_mark<E: EventEmitter>(
212    display: &dyn std::fmt::Display,
213    emitter: &mut E,
214) -> Result<(), EdifactError> {
215    use std::io::Write as _;
216    let mark = emitter.decimal_mark();
217
218    // Fast path: standard interchange — avoid any string manipulation.
219    if mark == b'.' {
220        let mut buf = [0u8; 320];
221        let mut w: &mut [u8] = &mut buf;
222        if write!(w, "{display}").is_ok() {
223            let written = 320 - w.len();
224            // INVARIANT: float Display output is ASCII-only.
225            let s = std::str::from_utf8(&buf[..written]).map_err(|_| EdifactError::InvalidUtf8)?;
226            return emitter.emit(EdifactEvent::Element { value: s });
227        }
228        // Buffer overflow fallback (extraordinarily large exponent).
229        let s = format!("{display}");
230        return emitter.emit(EdifactEvent::Element { value: &s });
231    }
232
233    // Non-standard decimal mark: format as string then replace '.'.
234    // INVARIANT: `mark` is ASCII (validated by ServiceStringAdvice::is_valid()).
235    let s = format!("{display}");
236    if s.contains('.') {
237        // Encode `mark` as a 1–4 byte UTF-8 slice on the stack; no heap allocation.
238        let mut mark_buf = [0u8; 4];
239        let mark_str = (mark as char).encode_utf8(&mut mark_buf);
240        let replaced = s.replace('.', mark_str);
241        emitter.emit(EdifactEvent::Element { value: &replaced })
242    } else {
243        emitter.emit(EdifactEvent::Element { value: &s })
244    }
245}
246
247impl EdifactSerialize for DecimalFloat<f32> {
248    #[inline]
249    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
250        serialize_with_decimal_mark(&self.0, emitter)
251    }
252}
253
254impl EdifactSerialize for DecimalFloat<f64> {
255    #[inline]
256    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
257        serialize_with_decimal_mark(&self.0, emitter)
258    }
259}
260
261impl<T: std::fmt::Display> EdifactSerialize for DecimalFloatDisplay<T> {
262    #[inline]
263    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
264        serialize_with_decimal_mark(&self.0, emitter)
265    }
266}
267
268/// Serialize `value` to the given [`Write`] implementation.
269pub fn to_writer<T, W>(inner: W, value: &T) -> Result<(), EdifactError>
270where
271    T: EdifactSerialize,
272    W: Write,
273{
274    let mut emitter = WriterEmitter::new(inner);
275    value.edifact_serialize(&mut emitter)?;
276    emitter.finish().map(|_| ())
277}
278
279/// Serialize `value` to an owned `Vec<u8>`.
280pub fn to_bytes<T: EdifactSerialize>(value: &T) -> Result<Vec<u8>, EdifactError> {
281    let mut buf = Vec::new();
282    to_writer(&mut buf, value)?;
283    Ok(buf)
284}
285
286/// Serialize `value` to a UTF-8 `String`.
287///
288/// # Allocations
289///
290/// Allocates one `Vec<u8>` via [`to_bytes`].  The subsequent conversion to
291/// `String` reuses that allocation in-place via [`String::from_utf8`] — no
292/// second allocation occurs.  When you only need raw bytes (e.g. for a network
293/// write), prefer [`to_bytes`] directly.
294pub fn to_edifact_string<T: EdifactSerialize>(value: &T) -> Result<String, EdifactError> {
295    let bytes = to_bytes(value)?;
296    String::from_utf8(bytes).map_err(|_| EdifactError::InvalidUtf8)
297}
298
299#[cfg(test)]
300mod tests {
301    use super::*;
302    use crate::event::{OwnedEdifactEvent, VecEmitter};
303
304    struct BgmSegment {
305        doc_name_code: String,
306        pruef_id: String,
307        msg_function: Option<String>,
308    }
309
310    impl EdifactSerialize for BgmSegment {
311        fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
312            emitter.emit(EdifactEvent::StartSegment { tag: "BGM" })?;
313            emitter.emit(EdifactEvent::Element {
314                value: &self.doc_name_code,
315            })?;
316            emitter.emit(EdifactEvent::Element {
317                value: &self.pruef_id,
318            })?;
319            self.msg_function.edifact_serialize(emitter)?;
320            emitter.emit(EdifactEvent::EndSegment)?;
321            Ok(())
322        }
323    }
324
325    #[test]
326    fn vec_emitter_captures_segment_events() {
327        let seg = BgmSegment {
328            doc_name_code: "E03".to_owned(),
329            pruef_id: "11042".to_owned(),
330            msg_function: None,
331        };
332        let mut emitter = VecEmitter::default();
333        seg.edifact_serialize(&mut emitter).unwrap();
334
335        assert_eq!(
336            emitter.events[0],
337            OwnedEdifactEvent::StartSegment {
338                tag: "BGM".to_owned()
339            }
340        );
341        assert_eq!(emitter.events.last(), Some(&OwnedEdifactEvent::EndSegment));
342    }
343
344    #[test]
345    fn to_bytes_produces_valid_edifact() {
346        let seg = BgmSegment {
347            doc_name_code: "E03".to_owned(),
348            pruef_id: "11042".to_owned(),
349            msg_function: Some("9".to_owned()),
350        };
351        let bytes = to_bytes(&seg).unwrap();
352        assert_eq!(std::str::from_utf8(&bytes).unwrap(), "BGM+E03+11042+9'");
353    }
354
355    #[test]
356    fn option_none_emits_empty_element() {
357        let val: Option<String> = None;
358        let mut emitter = VecEmitter::default();
359        val.edifact_serialize(&mut emitter).unwrap();
360        assert_eq!(
361            emitter.events[0],
362            OwnedEdifactEvent::Element {
363                value: String::new()
364            }
365        );
366    }
367
368    #[test]
369    fn option_some_emits_value() {
370        let val: Option<String> = Some("TEST".to_owned());
371        let mut emitter = VecEmitter::default();
372        val.edifact_serialize(&mut emitter).unwrap();
373        assert_eq!(
374            emitter.events[0],
375            OwnedEdifactEvent::Element {
376                value: "TEST".to_owned()
377            }
378        );
379    }
380
381    #[test]
382    fn integer_types_serialize_without_alloc() {
383        let mut emitter = VecEmitter::default();
384        42u32.edifact_serialize(&mut emitter).unwrap();
385        assert_eq!(
386            emitter.events[0],
387            OwnedEdifactEvent::Element {
388                value: "42".to_owned()
389            }
390        );
391        // i128::MIN should fit exactly in the 40-byte buffer
392        let mut emitter2 = VecEmitter::default();
393        i128::MIN.edifact_serialize(&mut emitter2).unwrap();
394        assert_eq!(
395            emitter2.events[0],
396            OwnedEdifactEvent::Element {
397                value: "-170141183460469231731687303715884105728".to_owned()
398            }
399        );
400    }
401
402    #[test]
403    fn float_extremes_do_not_panic() {
404        // Rust Display for f64 picks the shortest round-trip form; a 320-byte buffer covers all values.
405        let mut emitter = VecEmitter::default();
406        f64::MAX.edifact_serialize(&mut emitter).unwrap();
407        let s = match &emitter.events[0] {
408            OwnedEdifactEvent::Element { value } => value.clone(),
409            _ => panic!("expected Element event"),
410        };
411        assert!(!s.is_empty());
412        // f32::MAX too
413        let mut emitter2 = VecEmitter::default();
414        f32::MAX.edifact_serialize(&mut emitter2).unwrap();
415        assert!(matches!(
416            &emitter2.events[0],
417            OwnedEdifactEvent::Element { .. }
418        ));
419    }
420
421    #[test]
422    fn vec_serializes_each_item() {
423        let segments = vec![
424            BgmSegment {
425                doc_name_code: "E03".to_owned(),
426                pruef_id: "11042".to_owned(),
427                msg_function: None,
428            },
429            BgmSegment {
430                doc_name_code: "E01".to_owned(),
431                pruef_id: "11043".to_owned(),
432                msg_function: None,
433            },
434        ];
435        let bytes = to_bytes(&segments).unwrap();
436        let s = std::str::from_utf8(&bytes).unwrap();
437        assert!(s.contains("BGM+E03+11042"));
438        assert!(s.contains("BGM+E01+11043"));
439    }
440}