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