Skip to main content

edifact_rs/
event.rs

1//! Event model for EDIFACT (de)serialization.
2//!
3//! [`EdifactEvent`] is the borrowed, zero-allocation form used during real-time
4//! emission.  [`OwnedEdifactEvent`] is the owned form collected by [`VecEmitter`]
5//! for testing and introspection — no `Box::leak` anywhere.
6
7use crate::EdifactError;
8use std::io::Write;
9
10// ── event types ───────────────────────────────────────────────────────────────
11
12/// A borrowed EDIFACT event emitted during serialization.
13#[derive(Debug, Clone, PartialEq, Eq)]
14#[non_exhaustive]
15pub enum EdifactEvent<'a> {
16    /// Beginning of a new segment (e.g. `"BGM"`, `"NAD"`).
17    StartSegment {
18        /// Segment tag.
19        tag: &'a str,
20    },
21    /// A data element value — first (or only) component of a new element.
22    Element {
23        /// Element text value.
24        value: &'a str,
25    },
26    /// An additional component within the current element.
27    ComponentElement {
28        /// Component text value.
29        value: &'a str,
30    },
31    /// The first component of a further occurrence of the current data element.
32    ///
33    /// The write-side mirror of [`Token::RepeatElement`][crate::Token::RepeatElement]:
34    /// the parser splits repeating data elements, so the serializer has to be
35    /// able to produce them, or a value that round-trips through a typed struct
36    /// comes back collapsed into one occurrence.
37    ///
38    /// Requires an active repetition separator — see
39    /// [`ServiceStringAdvice::is_repetition_active`][crate::ServiceStringAdvice::is_repetition_active].
40    /// Without one there is no byte to separate occurrences with, so
41    /// [`WriterEmitter`] returns
42    /// [`EdifactError::RepetitionSeparatorNotDeclared`] rather than emitting
43    /// output that reads back as a single occurrence.
44    RepeatElement {
45        /// First component of the new occurrence.
46        value: &'a str,
47    },
48    /// End of the current segment.
49    EndSegment,
50}
51
52/// An owned EDIFACT event — for collection and testing (no borrowed lifetimes).
53#[derive(Debug, Clone, PartialEq, Eq)]
54#[non_exhaustive]
55pub enum OwnedEdifactEvent {
56    /// Owned segment-start event.
57    StartSegment {
58        /// Segment tag.
59        tag: String,
60    },
61    /// Owned element event.
62    Element {
63        /// Element text value.
64        value: String,
65    },
66    /// Owned component event.
67    ComponentElement {
68        /// Component text value.
69        value: String,
70    },
71    /// Owned repetition event.
72    RepeatElement {
73        /// First component of the new occurrence.
74        value: String,
75    },
76    /// Owned segment-end event.
77    EndSegment,
78}
79
80impl EdifactEvent<'_> {
81    /// Convert to an owned event, cloning string data.
82    pub fn into_owned(self) -> OwnedEdifactEvent {
83        match self {
84            Self::StartSegment { tag } => OwnedEdifactEvent::StartSegment {
85                tag: tag.to_owned(),
86            },
87            Self::Element { value } => OwnedEdifactEvent::Element {
88                value: value.to_owned(),
89            },
90            Self::ComponentElement { value } => OwnedEdifactEvent::ComponentElement {
91                value: value.to_owned(),
92            },
93            Self::RepeatElement { value } => OwnedEdifactEvent::RepeatElement {
94                value: value.to_owned(),
95            },
96            Self::EndSegment => OwnedEdifactEvent::EndSegment,
97        }
98    }
99}
100
101// ── emitter trait ─────────────────────────────────────────────────────────────
102
103/// Trait for any sink that can consume [`EdifactEvent`]s.
104pub trait EventEmitter {
105    /// Consume one event.
106    fn emit(&mut self, event: EdifactEvent<'_>) -> Result<(), EdifactError>;
107
108    /// Return the decimal-mark byte used by the interchange (`b'.'` by default).
109    ///
110    /// Serializers that format numeric values (e.g. [`crate::ser::DecimalFloat`])
111    /// call this to discover whether to emit `12.5` or `12,5`.
112    ///
113    /// The default implementation returns `b'.'`, which is correct for standard
114    /// EDIFACT interchanges that do not declare a UNA service string or that use
115    /// the ISO 9735 default.  Override this in emitters backed by a
116    /// [`crate::Writer`] with a custom [`crate::ServiceStringAdvice`].
117    #[inline]
118    fn decimal_mark(&self) -> u8 {
119        b'.'
120    }
121}
122
123// ── VecEmitter ────────────────────────────────────────────────────────────────
124
125/// Collects events into a [`Vec<OwnedEdifactEvent>`].
126///
127/// Useful for testing and introspection.  Does not leak memory.
128#[derive(Debug, Default)]
129pub struct VecEmitter {
130    /// Collected owned events.
131    pub events: Vec<OwnedEdifactEvent>,
132}
133
134impl EventEmitter for VecEmitter {
135    fn emit(&mut self, event: EdifactEvent<'_>) -> Result<(), EdifactError> {
136        self.events.push(event.into_owned());
137        Ok(())
138    }
139}
140
141// ── WriterEmitter ─────────────────────────────────────────────────────────────
142
143/// Internal protocol-state machine for [`WriterEmitter`].
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145enum EmitterState {
146    /// Between segments: no open segment.
147    Idle,
148    /// A [`EdifactEvent::StartSegment`] has been emitted; no element written yet.
149    InSegment,
150    /// An [`EdifactEvent::Element`] has been emitted; `ComponentElement` is valid.
151    InElement,
152}
153
154/// Writes EDIFACT events directly to any [`Write`] implementation.
155///
156/// Each event is written to the underlying writer immediately — no intermediate
157/// buffering of element strings occurs, so no heap allocation is required per
158/// event.  This makes `WriterEmitter` suitable for high-throughput serialization
159/// of large EDIFACT messages.
160///
161/// # Protocol
162///
163/// Events must arrive in the order produced by [`crate::EdifactSerialize`]:
164/// `StartSegment` → zero or more (`Element` → zero or more `ComponentElement`) → `EndSegment`.
165///
166/// Any violation of this protocol returns
167/// [`EdifactError::InvalidEventSequence`] immediately.  Violations are
168/// detected in both debug and release builds.
169pub struct WriterEmitter<W: Write> {
170    writer: crate::Writer<W>,
171    state: EmitterState,
172}
173
174impl<W: Write> WriterEmitter<W> {
175    /// Create a new `WriterEmitter` with default EDIFACT delimiters.
176    pub fn new(inner: W) -> Self {
177        Self {
178            writer: crate::Writer::new(inner),
179            state: EmitterState::Idle,
180        }
181    }
182
183    /// Create a new `WriterEmitter` with custom delimiters, writing a UNA header first.
184    ///
185    /// # Errors
186    ///
187    /// Returns [`EdifactError::InvalidUna`] when `ssa.is_valid()` is false.
188    pub fn with_una(
189        inner: W,
190        ssa: crate::tokenizer::ServiceStringAdvice,
191    ) -> Result<Self, crate::EdifactError> {
192        Ok(Self {
193            writer: crate::Writer::with_una(inner, ssa)?,
194            state: EmitterState::Idle,
195        })
196    }
197
198    /// Bind this emitter's writer to a character repertoire.
199    ///
200    /// The typed serialization path had no way to reach
201    /// [`Writer::with_charset`][crate::Writer::with_charset], so a `#[derive(EdifactSerialize)]`
202    /// struct could only ever go out as UTF-8 — which is wrong for every
203    /// `UNOC`…`UNOK` partner, and wrong in the silent way: `ü` arrives as two
204    /// mojibake characters rather than as an error.
205    ///
206    /// # Example
207    ///
208    /// ```
209    /// use edifact_rs::{Charset, EdifactEvent, EventEmitter, WriterEmitter};
210    ///
211    /// let mut emitter = WriterEmitter::new(Vec::new()).with_charset(Charset::UnoC);
212    /// emitter.emit(EdifactEvent::StartSegment { tag: "NAD" })?;
213    /// emitter.emit(EdifactEvent::Element { value: "Müller" })?;
214    /// emitter.emit(EdifactEvent::EndSegment)?;
215    /// assert_eq!(emitter.finish()?, b"NAD+M\xFCller'".to_vec());
216    /// # Ok::<(), edifact_rs::EdifactError>(())
217    /// ```
218    #[must_use]
219    pub fn with_charset(mut self, charset: crate::Charset) -> Self {
220        self.writer = self.writer.with_charset(charset);
221        self
222    }
223
224    /// Flush and consume the emitter, returning the underlying writer.
225    pub fn finish(self) -> Result<W, EdifactError> {
226        self.writer.finish()
227    }
228
229    /// Number of complete segments written so far.
230    pub fn segment_count(&self) -> u64 {
231        self.writer.segment_count()
232    }
233
234    /// Return the active [`ServiceStringAdvice`][crate::ServiceStringAdvice].
235    ///
236    /// Callers can use this to format values (e.g., floats) using the correct
237    /// decimal-mark character configured in the UNA header.
238    pub fn service_string_advice(&self) -> crate::tokenizer::ServiceStringAdvice {
239        self.writer.service_string_advice()
240    }
241}
242
243impl<W: Write> EventEmitter for WriterEmitter<W> {
244    #[inline]
245    fn decimal_mark(&self) -> u8 {
246        self.writer.service_string_advice().decimal_mark
247    }
248
249    fn emit(&mut self, event: EdifactEvent<'_>) -> Result<(), EdifactError> {
250        match event {
251            EdifactEvent::StartSegment { tag } => {
252                if self.state != EmitterState::Idle {
253                    return Err(EdifactError::InvalidEventSequence {
254                        message: "StartSegment emitted while a segment is already open; emit EndSegment first",
255                    });
256                }
257                self.state = EmitterState::InSegment;
258                self.writer.write_tag_only(tag)?;
259            }
260            EdifactEvent::Element { value } => {
261                if self.state == EmitterState::Idle {
262                    return Err(EdifactError::InvalidEventSequence {
263                        message: "Element emitted outside of a segment; emit StartSegment first",
264                    });
265                }
266                self.state = EmitterState::InElement;
267                self.writer.write_element_sep()?;
268                self.writer.write_escaped(value)?;
269            }
270            EdifactEvent::ComponentElement { value } => {
271                if self.state != EmitterState::InElement {
272                    return Err(EdifactError::InvalidEventSequence {
273                        message: "ComponentElement emitted without a preceding Element in the same segment",
274                    });
275                }
276                self.writer.write_component_sep()?;
277                self.writer.write_escaped(value)?;
278            }
279            EdifactEvent::RepeatElement { value } => {
280                if self.state != EmitterState::InElement {
281                    return Err(EdifactError::InvalidEventSequence {
282                        message: "RepeatElement emitted without a preceding Element in the same segment",
283                    });
284                }
285                // Checked before the separator is written, so a rejected
286                // repetition leaves nothing half-emitted behind it.
287                self.writer.write_repetition_sep()?;
288                self.writer.write_escaped(value)?;
289            }
290            EdifactEvent::EndSegment => {
291                if self.state == EmitterState::Idle {
292                    return Err(EdifactError::InvalidEventSequence {
293                        message: "EndSegment emitted while no segment is open; emit StartSegment first",
294                    });
295                }
296                self.state = EmitterState::Idle;
297                self.writer.write_segment_term_and_count()?;
298            }
299        }
300        Ok(())
301    }
302}
303
304#[cfg(test)]
305mod tests {
306    use super::*;
307
308    #[test]
309    fn vec_emitter_no_memory_leak() {
310        let mut e = VecEmitter::default();
311        e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
312        e.emit(EdifactEvent::Element { value: "E03" }).unwrap();
313        e.emit(EdifactEvent::EndSegment).unwrap();
314        assert_eq!(
315            e.events[0],
316            OwnedEdifactEvent::StartSegment {
317                tag: "BGM".to_owned()
318            }
319        );
320        assert_eq!(
321            e.events[1],
322            OwnedEdifactEvent::Element {
323                value: "E03".to_owned()
324            }
325        );
326    }
327
328    #[test]
329    fn writer_emitter_produces_valid_edifact() {
330        let mut buf = Vec::new();
331        {
332            let mut e = WriterEmitter::new(&mut buf);
333            e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
334            e.emit(EdifactEvent::Element { value: "E03" }).unwrap();
335            e.emit(EdifactEvent::Element { value: "11042" }).unwrap();
336            e.emit(EdifactEvent::EndSegment).unwrap();
337            e.finish().unwrap();
338        }
339        assert_eq!(buf, b"BGM+E03+11042'");
340    }
341
342    #[test]
343    fn writer_emitter_handles_components() {
344        let mut buf = Vec::new();
345        {
346            let mut e = WriterEmitter::new(&mut buf);
347            e.emit(EdifactEvent::StartSegment { tag: "NAD" }).unwrap();
348            e.emit(EdifactEvent::Element { value: "MS" }).unwrap();
349            e.emit(EdifactEvent::Element {
350                value: "9900112233445",
351            })
352            .unwrap();
353            e.emit(EdifactEvent::ComponentElement { value: "" })
354                .unwrap();
355            e.emit(EdifactEvent::ComponentElement { value: "293" })
356                .unwrap();
357            e.emit(EdifactEvent::EndSegment).unwrap();
358            e.finish().unwrap();
359        }
360        let s = std::str::from_utf8(&buf).unwrap();
361        assert_eq!(s, "NAD+MS+9900112233445::293'");
362    }
363
364    #[test]
365    fn repetitions_round_trip_through_the_event_layer() {
366        // The parser splits repeating data elements, so the serializer has to be
367        // able to produce them — otherwise a value that goes out through a typed
368        // struct comes back collapsed into one occurrence.
369        let ssa = crate::ServiceStringAdvice::from_bytes(b"UNA:+.?*'").unwrap();
370        let mut buf = Vec::new();
371        {
372            let mut e = WriterEmitter::with_una(&mut buf, ssa).unwrap();
373            e.emit(EdifactEvent::StartSegment { tag: "RFF" }).unwrap();
374            e.emit(EdifactEvent::Element { value: "ON" }).unwrap();
375            e.emit(EdifactEvent::ComponentElement { value: "1" })
376                .unwrap();
377            e.emit(EdifactEvent::RepeatElement { value: "ON" }).unwrap();
378            e.emit(EdifactEvent::ComponentElement { value: "2" })
379                .unwrap();
380            e.emit(EdifactEvent::EndSegment).unwrap();
381            e.finish().unwrap();
382        }
383        assert_eq!(
384            std::str::from_utf8(&buf).unwrap(),
385            "UNA:+.?*'RFF+ON:1*ON:2'"
386        );
387
388        let segments: Vec<_> = crate::from_bytes(&buf)
389            .collect::<Result<Vec<_>, _>>()
390            .unwrap();
391        let element = segments[0].get_element(0).unwrap();
392        assert_eq!(element.repeat_count(), 2);
393        assert_eq!(element.repetition(1).unwrap()[1].0, "2");
394    }
395
396    #[test]
397    fn a_repetition_without_a_declared_separator_is_refused() {
398        let mut e = WriterEmitter::new(Vec::<u8>::new());
399        e.emit(EdifactEvent::StartSegment { tag: "RFF" }).unwrap();
400        e.emit(EdifactEvent::Element { value: "ON" }).unwrap();
401        let err = e
402            .emit(EdifactEvent::RepeatElement { value: "ON" })
403            .unwrap_err();
404        assert!(
405            matches!(err, EdifactError::RepetitionSeparatorNotDeclared),
406            "expected RepetitionSeparatorNotDeclared, got {err:?}"
407        );
408    }
409
410    #[test]
411    fn a_repetition_before_any_element_is_refused() {
412        let ssa = crate::ServiceStringAdvice::from_bytes(b"UNA:+.?*'").unwrap();
413        let mut e = WriterEmitter::with_una(Vec::<u8>::new(), ssa).unwrap();
414        e.emit(EdifactEvent::StartSegment { tag: "RFF" }).unwrap();
415        let err = e
416            .emit(EdifactEvent::RepeatElement { value: "ON" })
417            .unwrap_err();
418        assert!(
419            matches!(err, EdifactError::InvalidEventSequence { .. }),
420            "expected InvalidEventSequence, got {err:?}"
421        );
422    }
423
424    // ── protocol-violation tests (BUG 2.1) ───────────────────────────────────
425
426    #[test]
427    fn writer_emitter_element_before_start_segment_is_err() {
428        let mut e = WriterEmitter::new(Vec::<u8>::new());
429        let err = e.emit(EdifactEvent::Element { value: "X" }).unwrap_err();
430        assert!(
431            matches!(err, crate::EdifactError::InvalidEventSequence { .. }),
432            "expected InvalidEventSequence, got {err:?}"
433        );
434    }
435
436    #[test]
437    fn writer_emitter_component_before_element_is_err() {
438        let mut e = WriterEmitter::new(Vec::<u8>::new());
439        e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
440        let err = e
441            .emit(EdifactEvent::ComponentElement { value: "X" })
442            .unwrap_err();
443        assert!(
444            matches!(err, crate::EdifactError::InvalidEventSequence { .. }),
445            "expected InvalidEventSequence, got {err:?}"
446        );
447    }
448
449    #[test]
450    fn writer_emitter_double_start_segment_is_err() {
451        let mut e = WriterEmitter::new(Vec::<u8>::new());
452        e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
453        let err = e
454            .emit(EdifactEvent::StartSegment { tag: "DTM" })
455            .unwrap_err();
456        assert!(
457            matches!(err, crate::EdifactError::InvalidEventSequence { .. }),
458            "expected InvalidEventSequence, got {err:?}"
459        );
460    }
461
462    #[test]
463    fn writer_emitter_end_segment_without_start_is_err() {
464        let mut e = WriterEmitter::new(Vec::<u8>::new());
465        let err = e.emit(EdifactEvent::EndSegment).unwrap_err();
466        assert!(
467            matches!(err, crate::EdifactError::InvalidEventSequence { .. }),
468            "expected InvalidEventSequence, got {err:?}"
469        );
470    }
471}