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// ── decimal-mark-aware float wrapper ─────────────────────────────────────────
130
131/// Decimal-mark-aware wrapper for `f32` or `f64` serialization.
132///
133/// Rust's [`Display`][std::fmt::Display] for `f32`/`f64` always uses `.` as the
134/// decimal separator.  EDIFACT interchanges can declare a different decimal mark
135/// in the UNA service string — `,` is the common alternative.
136///
137/// # Required for float serialization
138///
139/// `edifact-rs` intentionally provides **no** blanket `EdifactSerialize` impl for
140/// `f32`/`f64`.  This forces callers to make the decimal-mark intent explicit:
141///
142/// ```
143/// use edifact_rs::ser::DecimalFloat;
144/// use edifact_rs::{EdifactSerialize, VecEmitter, OwnedEdifactEvent};
145///
146/// let mut emitter = VecEmitter::default();
147/// DecimalFloat(12.5_f64).edifact_serialize(&mut emitter).unwrap();
148/// assert!(matches!(&emitter.events[0], OwnedEdifactEvent::Element { value } if value == "12.5"));
149/// ```
150///
151/// When the emitter's [`EventEmitter::decimal_mark`] is `b','`, the output will be `"12,5"`.
152///
153/// # Supported inner types
154///
155/// `DecimalFloat<f32>`, `DecimalFloat<f64>`. For any type that implements
156/// [`std::fmt::Display`], use [`DecimalFloatDisplay`] instead.
157#[derive(Debug, Clone, Copy, PartialEq)]
158pub struct DecimalFloat<T>(pub T);
159
160/// Decimal-mark-aware serializer for any [`std::fmt::Display`] value.
161///
162/// Like [`DecimalFloat`] but works with any type whose `Display` uses `.` as a
163/// decimal point (e.g., `rust_decimal::Decimal`, `bigdecimal::BigDecimal`).
164#[derive(Debug, Clone, Copy, PartialEq)]
165pub struct DecimalFloatDisplay<T: std::fmt::Display>(pub T);
166
167fn serialize_with_decimal_mark<E: EventEmitter>(
168    display: &dyn std::fmt::Display,
169    emitter: &mut E,
170) -> Result<(), EdifactError> {
171    use std::io::Write as _;
172    let mark = emitter.decimal_mark();
173
174    // Fast path: standard interchange — avoid any string manipulation.
175    if mark == b'.' {
176        let mut buf = [0u8; 320];
177        let mut w: &mut [u8] = &mut buf;
178        if write!(w, "{display}").is_ok() {
179            let written = 320 - w.len();
180            // INVARIANT: float Display output is ASCII-only.
181            let s = std::str::from_utf8(&buf[..written]).map_err(|_| EdifactError::InvalidUtf8)?;
182            return emitter.emit(EdifactEvent::Element { value: s });
183        }
184        // Buffer overflow fallback (extraordinarily large exponent).
185        let s = format!("{display}");
186        return emitter.emit(EdifactEvent::Element { value: &s });
187    }
188
189    // Non-standard decimal mark: format as string then replace '.'.
190    // INVARIANT: `mark` is ASCII (validated by ServiceStringAdvice::is_valid()).
191    let s = format!("{display}");
192    if s.contains('.') {
193        // Encode `mark` as a 1–4 byte UTF-8 slice on the stack; no heap allocation.
194        let mut mark_buf = [0u8; 4];
195        let mark_str = (mark as char).encode_utf8(&mut mark_buf);
196        let replaced = s.replace('.', mark_str);
197        emitter.emit(EdifactEvent::Element { value: &replaced })
198    } else {
199        emitter.emit(EdifactEvent::Element { value: &s })
200    }
201}
202
203/// Reject `NaN` and infinity before they reach the wire.
204///
205/// `Display` renders them as `NaN`, `inf`, and `-inf` — text that is not an
206/// EDIFACT numeric data element (ISO 9735-1 §10 admits the ISO 6093
207/// representations: digits, an optional minus sign, a decimal mark, and an
208/// exponent), that no receiver can parse, and that this crate's own reader would
209/// hand back as a string rather than a number. Emitting it would turn a
210/// calculation bug into a wire-format bug discovered days later.
211#[inline]
212fn reject_non_finite(value: f64) -> Result<(), EdifactError> {
213    if value.is_finite() {
214        return Ok(());
215    }
216    Err(EdifactError::NonFiniteNumber {
217        value: value.to_string(),
218    })
219}
220
221impl EdifactSerialize for DecimalFloat<f32> {
222    #[inline]
223    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
224        reject_non_finite(f64::from(self.0))?;
225        serialize_with_decimal_mark(&self.0, emitter)
226    }
227}
228
229impl EdifactSerialize for DecimalFloat<f64> {
230    #[inline]
231    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
232        reject_non_finite(self.0)?;
233        serialize_with_decimal_mark(&self.0, emitter)
234    }
235}
236
237impl<T: std::fmt::Display> EdifactSerialize for DecimalFloatDisplay<T> {
238    #[inline]
239    fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
240        serialize_with_decimal_mark(&self.0, emitter)
241    }
242}
243
244/// Emit one segment from `(element_index, component_index, value)` triples.
245///
246/// Values may arrive in any order and need not cover every slot: the triples
247/// are sorted, gaps are filled with empty elements and components, and the
248/// result is a single well-formed segment.
249///
250/// This exists because a derive that addresses fields by UN/EDIFACT data element
251/// identifier (`#[edifact(element = "3055")]`) only learns the numeric slots
252/// during *const evaluation*, after the macro has already expanded — so it
253/// cannot lay out the emit order at expansion time the way a positional derive
254/// can.  Handing the resolved slots to this function moves the ordering to
255/// runtime, where they are known.
256///
257/// `parts` is taken by mutable reference because it is sorted in place; the
258/// caller keeps the allocation.
259///
260/// If two triples claim the same slot the **first** one wins and the rest are
261/// dropped — emitting both would shift every following component by one and
262/// silently corrupt the segment's positions. (The derive cannot produce a
263/// duplicate: it rejects colliding slots at compile time.)
264///
265/// # Example
266///
267/// ```rust
268/// use edifact_rs::{VecEmitter, emit_sparse_segment};
269/// use std::borrow::Cow;
270///
271/// let mut parts = vec![
272///     (1, 2, Cow::Borrowed("293")),
273///     (0, 0, Cow::Borrowed("MS")),
274///     (1, 0, Cow::Borrowed("9900112233445")),
275/// ];
276/// let mut emitter = VecEmitter::default();
277/// emit_sparse_segment(&mut emitter, "NAD", &mut parts)?;
278/// // Slot (1, 1) was never supplied, so it is emitted as an empty component:
279/// // NAD+MS+9900112233445::293'
280/// assert_eq!(emitter.events.len(), 6);
281/// # Ok::<(), edifact_rs::EdifactError>(())
282/// ```
283///
284/// # Errors
285///
286/// Propagates any error returned by the emitter.
287pub fn emit_sparse_segment<E: EventEmitter>(
288    emitter: &mut E,
289    tag: &str,
290    parts: &mut [(usize, usize, std::borrow::Cow<'_, str>)],
291) -> Result<(), EdifactError> {
292    parts.sort_by_key(|(element, component, _)| (*element, *component));
293
294    emitter.emit(EdifactEvent::StartSegment { tag })?;
295
296    // `parts` is sorted, so the last entry carries the highest element index and
297    // one linear walk covers every slot.
298    let mut cursor = 0usize;
299    let element_count = parts.last().map_or(0, |(element, _, _)| *element + 1);
300    for element in 0..element_count {
301        // Component 0 opens the element; every later component extends it.
302        let mut next_component = 0usize;
303        let mut opened = false;
304        while cursor < parts.len() && parts[cursor].0 == element {
305            let (_, component, value) = &parts[cursor];
306            if *component < next_component {
307                // A slot already emitted: first value wins.  Emitting this one
308                // too would push every later component one position right.
309                cursor += 1;
310                continue;
311            }
312            // Fill any skipped component slots so positions stay meaningful.
313            while next_component < *component {
314                let event = if opened {
315                    EdifactEvent::ComponentElement { value: "" }
316                } else {
317                    EdifactEvent::Element { value: "" }
318                };
319                emitter.emit(event)?;
320                opened = true;
321                next_component += 1;
322            }
323            let event = if opened {
324                EdifactEvent::ComponentElement { value }
325            } else {
326                EdifactEvent::Element { value }
327            };
328            emitter.emit(event)?;
329            opened = true;
330            next_component += 1;
331            cursor += 1;
332        }
333        if !opened {
334            // No value for this element at all — emit an empty placeholder so
335            // the following elements keep their positions.
336            emitter.emit(EdifactEvent::Element { value: "" })?;
337        }
338    }
339
340    emitter.emit(EdifactEvent::EndSegment)
341}
342
343/// Serialize `value` to the given [`Write`] implementation.
344pub fn to_writer<T, W>(inner: W, value: &T) -> Result<(), EdifactError>
345where
346    T: EdifactSerialize,
347    W: Write,
348{
349    let mut emitter = WriterEmitter::new(inner);
350    value.edifact_serialize(&mut emitter)?;
351    emitter.finish().map(|_| ())
352}
353
354/// Serialize `value` to an owned `Vec<u8>`.
355pub fn to_bytes<T: EdifactSerialize>(value: &T) -> Result<Vec<u8>, EdifactError> {
356    let mut buf = Vec::new();
357    to_writer(&mut buf, value)?;
358    Ok(buf)
359}
360
361/// Serialize `value` to a UTF-8 `String`.
362///
363/// # Allocations
364///
365/// Allocates one `Vec<u8>` via [`to_bytes`].  The subsequent conversion to
366/// `String` reuses that allocation in-place via [`String::from_utf8`] — no
367/// second allocation occurs.  When you only need raw bytes (e.g. for a network
368/// write), prefer [`to_bytes`] directly.
369pub fn to_edifact_string<T: EdifactSerialize>(value: &T) -> Result<String, EdifactError> {
370    let bytes = to_bytes(value)?;
371    String::from_utf8(bytes).map_err(|_| EdifactError::InvalidUtf8)
372}
373
374#[cfg(test)]
375mod tests {
376    use super::*;
377    use crate::event::{OwnedEdifactEvent, VecEmitter};
378    use std::borrow::Cow;
379
380    /// Render a sparse-emit result to wire bytes, which is what the shape of
381    /// the event stream actually has to produce.
382    fn sparse_to_wire(parts: &mut [(usize, usize, Cow<'_, str>)], tag: &str) -> String {
383        let mut buf = Vec::new();
384        {
385            let mut emitter = crate::WriterEmitter::new(&mut buf);
386            emit_sparse_segment(&mut emitter, tag, parts).expect("emit");
387            emitter.finish().expect("finish");
388        }
389        String::from_utf8(buf).expect("utf-8")
390    }
391
392    #[test]
393    fn emit_sparse_segment_fills_gaps_in_elements_and_components() {
394        let mut parts = vec![
395            (1, 2, Cow::Borrowed("293")),
396            (0, 0, Cow::Borrowed("MS")),
397            (3, 1, Cow::Borrowed("late")),
398        ];
399        // Element 2 is absent entirely, element 3 skips component 0, and
400        // element 1 skips component 1 — every gap holds its position.
401        assert_eq!(sparse_to_wire(&mut parts, "NAD"), "NAD+MS+::293++:late'");
402    }
403
404    #[test]
405    fn emit_sparse_segment_first_value_wins_on_a_duplicate_slot() {
406        // Emitting both would push `293` from component 2 to component 3 and
407        // silently corrupt every later position.
408        let mut parts = vec![
409            (0, 0, Cow::Borrowed("MS")),
410            (1, 0, Cow::Borrowed("first")),
411            (1, 0, Cow::Borrowed("second")),
412            (1, 2, Cow::Borrowed("293")),
413        ];
414        assert_eq!(sparse_to_wire(&mut parts, "NAD"), "NAD+MS+first::293'");
415    }
416
417    #[test]
418    fn emit_sparse_segment_with_no_parts_writes_a_bare_tag() {
419        assert_eq!(sparse_to_wire(&mut [], "UNS"), "UNS'");
420    }
421
422    struct BgmSegment {
423        doc_name_code: String,
424        pruef_id: String,
425        msg_function: Option<String>,
426    }
427
428    impl EdifactSerialize for BgmSegment {
429        fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
430            emitter.emit(EdifactEvent::StartSegment { tag: "BGM" })?;
431            emitter.emit(EdifactEvent::Element {
432                value: &self.doc_name_code,
433            })?;
434            emitter.emit(EdifactEvent::Element {
435                value: &self.pruef_id,
436            })?;
437            self.msg_function.edifact_serialize(emitter)?;
438            emitter.emit(EdifactEvent::EndSegment)?;
439            Ok(())
440        }
441    }
442
443    #[test]
444    fn vec_emitter_captures_segment_events() {
445        let seg = BgmSegment {
446            doc_name_code: "E03".to_owned(),
447            pruef_id: "11042".to_owned(),
448            msg_function: None,
449        };
450        let mut emitter = VecEmitter::default();
451        seg.edifact_serialize(&mut emitter).unwrap();
452
453        assert_eq!(
454            emitter.events[0],
455            OwnedEdifactEvent::StartSegment {
456                tag: "BGM".to_owned()
457            }
458        );
459        assert_eq!(emitter.events.last(), Some(&OwnedEdifactEvent::EndSegment));
460    }
461
462    #[test]
463    fn to_bytes_produces_valid_edifact() {
464        let seg = BgmSegment {
465            doc_name_code: "E03".to_owned(),
466            pruef_id: "11042".to_owned(),
467            msg_function: Some("9".to_owned()),
468        };
469        let bytes = to_bytes(&seg).unwrap();
470        assert_eq!(std::str::from_utf8(&bytes).unwrap(), "BGM+E03+11042+9'");
471    }
472
473    #[test]
474    fn option_none_emits_empty_element() {
475        let val: Option<String> = None;
476        let mut emitter = VecEmitter::default();
477        val.edifact_serialize(&mut emitter).unwrap();
478        assert_eq!(
479            emitter.events[0],
480            OwnedEdifactEvent::Element {
481                value: String::new()
482            }
483        );
484    }
485
486    #[test]
487    fn option_some_emits_value() {
488        let val: Option<String> = Some("TEST".to_owned());
489        let mut emitter = VecEmitter::default();
490        val.edifact_serialize(&mut emitter).unwrap();
491        assert_eq!(
492            emitter.events[0],
493            OwnedEdifactEvent::Element {
494                value: "TEST".to_owned()
495            }
496        );
497    }
498
499    #[test]
500    fn integer_types_serialize_without_alloc() {
501        let mut emitter = VecEmitter::default();
502        42u32.edifact_serialize(&mut emitter).unwrap();
503        assert_eq!(
504            emitter.events[0],
505            OwnedEdifactEvent::Element {
506                value: "42".to_owned()
507            }
508        );
509        // i128::MIN should fit exactly in the 40-byte buffer
510        let mut emitter2 = VecEmitter::default();
511        i128::MIN.edifact_serialize(&mut emitter2).unwrap();
512        assert_eq!(
513            emitter2.events[0],
514            OwnedEdifactEvent::Element {
515                value: "-170141183460469231731687303715884105728".to_owned()
516            }
517        );
518    }
519
520    #[test]
521    fn float_extremes_do_not_panic() {
522        use super::DecimalFloat;
523        // Rust Display for f64 picks the shortest round-trip form; a 320-byte buffer covers all values.
524        let mut emitter = VecEmitter::default();
525        DecimalFloat(f64::MAX)
526            .edifact_serialize(&mut emitter)
527            .unwrap();
528        let s = match &emitter.events[0] {
529            OwnedEdifactEvent::Element { value } => value.clone(),
530            _ => panic!("expected Element event"),
531        };
532        assert!(!s.is_empty());
533        // f32::MAX too
534        let mut emitter2 = VecEmitter::default();
535        DecimalFloat(f32::MAX)
536            .edifact_serialize(&mut emitter2)
537            .unwrap();
538        assert!(matches!(
539            &emitter2.events[0],
540            OwnedEdifactEvent::Element { .. }
541        ));
542    }
543
544    #[test]
545    fn vec_serializes_each_item() {
546        let segments = vec![
547            BgmSegment {
548                doc_name_code: "E03".to_owned(),
549                pruef_id: "11042".to_owned(),
550                msg_function: None,
551            },
552            BgmSegment {
553                doc_name_code: "E01".to_owned(),
554                pruef_id: "11043".to_owned(),
555                msg_function: None,
556            },
557        ];
558        let bytes = to_bytes(&segments).unwrap();
559        let s = std::str::from_utf8(&bytes).unwrap();
560        assert!(s.contains("BGM+E03+11042"));
561        assert!(s.contains("BGM+E01+11043"));
562    }
563}