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    /// End of the current segment.
32    EndSegment,
33}
34
35/// An owned EDIFACT event — for collection and testing (no borrowed lifetimes).
36#[derive(Debug, Clone, PartialEq, Eq)]
37#[non_exhaustive]
38pub enum OwnedEdifactEvent {
39    /// Owned segment-start event.
40    StartSegment {
41        /// Segment tag.
42        tag: String,
43    },
44    /// Owned element event.
45    Element {
46        /// Element text value.
47        value: String,
48    },
49    /// Owned component event.
50    ComponentElement {
51        /// Component text value.
52        value: String,
53    },
54    /// Owned segment-end event.
55    EndSegment,
56}
57
58impl<'a> EdifactEvent<'a> {
59    /// Convert to an owned event, cloning string data.
60    pub fn into_owned(self) -> OwnedEdifactEvent {
61        match self {
62            Self::StartSegment { tag } => OwnedEdifactEvent::StartSegment {
63                tag: tag.to_owned(),
64            },
65            Self::Element { value } => OwnedEdifactEvent::Element {
66                value: value.to_owned(),
67            },
68            Self::ComponentElement { value } => OwnedEdifactEvent::ComponentElement {
69                value: value.to_owned(),
70            },
71            Self::EndSegment => OwnedEdifactEvent::EndSegment,
72        }
73    }
74}
75
76// ── emitter trait ─────────────────────────────────────────────────────────────
77
78/// Trait for any sink that can consume [`EdifactEvent`]s.
79pub trait EventEmitter {
80    /// Consume one event.
81    fn emit(&mut self, event: EdifactEvent<'_>) -> Result<(), EdifactError>;
82}
83
84// ── VecEmitter ────────────────────────────────────────────────────────────────
85
86/// Collects events into a [`Vec<OwnedEdifactEvent>`].
87///
88/// Useful for testing and introspection.  Does not leak memory.
89#[derive(Debug, Default)]
90pub struct VecEmitter {
91    /// Collected owned events.
92    pub events: Vec<OwnedEdifactEvent>,
93}
94
95impl EventEmitter for VecEmitter {
96    fn emit(&mut self, event: EdifactEvent<'_>) -> Result<(), EdifactError> {
97        self.events.push(event.into_owned());
98        Ok(())
99    }
100}
101
102// ── WriterEmitter ─────────────────────────────────────────────────────────────
103
104/// Internal protocol-state machine for [`WriterEmitter`].
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106enum EmitterState {
107    /// Between segments: no open segment.
108    Idle,
109    /// A [`EdifactEvent::StartSegment`] has been emitted; no element written yet.
110    InSegment,
111    /// An [`EdifactEvent::Element`] has been emitted; `ComponentElement` is valid.
112    InElement,
113}
114
115/// Writes EDIFACT events directly to any [`Write`] implementation.
116///
117/// Each event is written to the underlying writer immediately — no intermediate
118/// buffering of element strings occurs, so no heap allocation is required per
119/// event.  This makes `WriterEmitter` suitable for high-throughput serialization
120/// of large EDIFACT messages.
121///
122/// # Protocol
123///
124/// Events must arrive in the order produced by [`crate::EdifactSerialize`]:
125/// `StartSegment` → zero or more (`Element` → zero or more `ComponentElement`) → `EndSegment`.
126///
127/// Any violation of this protocol returns
128/// [`EdifactError::InvalidEventSequence`] immediately.  Violations are
129/// detected in both debug and release builds.
130pub struct WriterEmitter<W: Write> {
131    writer: crate::Writer<W>,
132    state: EmitterState,
133}
134
135impl<W: Write> WriterEmitter<W> {
136    /// Create a new `WriterEmitter` with default EDIFACT delimiters.
137    pub fn new(inner: W) -> Self {
138        Self {
139            writer: crate::Writer::new(inner),
140            state: EmitterState::Idle,
141        }
142    }
143
144    /// Flush and consume the emitter, returning the underlying writer.
145    pub fn finish(self) -> Result<W, EdifactError> {
146        self.writer.finish()
147    }
148
149    /// Number of complete segments written so far.
150    pub fn segment_count(&self) -> u64 {
151        self.writer.segment_count()
152    }
153}
154
155impl<W: Write> EventEmitter for WriterEmitter<W> {
156    fn emit(&mut self, event: EdifactEvent<'_>) -> Result<(), EdifactError> {
157        match event {
158            EdifactEvent::StartSegment { tag } => {
159                if self.state != EmitterState::Idle {
160                    return Err(EdifactError::InvalidEventSequence {
161                        message: "StartSegment emitted while a segment is already open; emit EndSegment first",
162                    });
163                }
164                self.state = EmitterState::InSegment;
165                self.writer.write_tag_only(tag)?;
166            }
167            EdifactEvent::Element { value } => {
168                if self.state == EmitterState::Idle {
169                    return Err(EdifactError::InvalidEventSequence {
170                        message: "Element emitted outside of a segment; emit StartSegment first",
171                    });
172                }
173                self.state = EmitterState::InElement;
174                self.writer.write_element_sep()?;
175                self.writer.write_escaped(value)?;
176            }
177            EdifactEvent::ComponentElement { value } => {
178                if self.state != EmitterState::InElement {
179                    return Err(EdifactError::InvalidEventSequence {
180                        message: "ComponentElement emitted without a preceding Element in the same segment",
181                    });
182                }
183                self.writer.write_component_sep()?;
184                self.writer.write_escaped(value)?;
185            }
186            EdifactEvent::EndSegment => {
187                if self.state == EmitterState::Idle {
188                    return Err(EdifactError::InvalidEventSequence {
189                        message: "EndSegment emitted while no segment is open; emit StartSegment first",
190                    });
191                }
192                self.state = EmitterState::Idle;
193                self.writer.write_segment_term_and_count()?;
194            }
195        }
196        Ok(())
197    }
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    #[test]
205    fn vec_emitter_no_memory_leak() {
206        let mut e = VecEmitter::default();
207        e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
208        e.emit(EdifactEvent::Element { value: "E03" }).unwrap();
209        e.emit(EdifactEvent::EndSegment).unwrap();
210        assert_eq!(
211            e.events[0],
212            OwnedEdifactEvent::StartSegment {
213                tag: "BGM".to_owned()
214            }
215        );
216        assert_eq!(
217            e.events[1],
218            OwnedEdifactEvent::Element {
219                value: "E03".to_owned()
220            }
221        );
222    }
223
224    #[test]
225    fn writer_emitter_produces_valid_edifact() {
226        let mut buf = Vec::new();
227        {
228            let mut e = WriterEmitter::new(&mut buf);
229            e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
230            e.emit(EdifactEvent::Element { value: "E03" }).unwrap();
231            e.emit(EdifactEvent::Element { value: "11042" }).unwrap();
232            e.emit(EdifactEvent::EndSegment).unwrap();
233            e.finish().unwrap();
234        }
235        assert_eq!(buf, b"BGM+E03+11042'");
236    }
237
238    #[test]
239    fn writer_emitter_handles_components() {
240        let mut buf = Vec::new();
241        {
242            let mut e = WriterEmitter::new(&mut buf);
243            e.emit(EdifactEvent::StartSegment { tag: "NAD" }).unwrap();
244            e.emit(EdifactEvent::Element { value: "MS" }).unwrap();
245            e.emit(EdifactEvent::Element {
246                value: "9900112233445",
247            })
248            .unwrap();
249            e.emit(EdifactEvent::ComponentElement { value: "" })
250                .unwrap();
251            e.emit(EdifactEvent::ComponentElement { value: "293" })
252                .unwrap();
253            e.emit(EdifactEvent::EndSegment).unwrap();
254            e.finish().unwrap();
255        }
256        let s = std::str::from_utf8(&buf).unwrap();
257        assert_eq!(s, "NAD+MS+9900112233445::293'");
258    }
259
260    // ── protocol-violation tests (BUG 2.1) ───────────────────────────────────
261
262    #[test]
263    fn writer_emitter_element_before_start_segment_is_err() {
264        let mut e = WriterEmitter::new(Vec::<u8>::new());
265        let err = e.emit(EdifactEvent::Element { value: "X" }).unwrap_err();
266        assert!(
267            matches!(err, crate::EdifactError::InvalidEventSequence { .. }),
268            "expected InvalidEventSequence, got {err:?}"
269        );
270    }
271
272    #[test]
273    fn writer_emitter_component_before_element_is_err() {
274        let mut e = WriterEmitter::new(Vec::<u8>::new());
275        e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
276        let err = e
277            .emit(EdifactEvent::ComponentElement { value: "X" })
278            .unwrap_err();
279        assert!(
280            matches!(err, crate::EdifactError::InvalidEventSequence { .. }),
281            "expected InvalidEventSequence, got {err:?}"
282        );
283    }
284
285    #[test]
286    fn writer_emitter_double_start_segment_is_err() {
287        let mut e = WriterEmitter::new(Vec::<u8>::new());
288        e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
289        let err = e
290            .emit(EdifactEvent::StartSegment { tag: "DTM" })
291            .unwrap_err();
292        assert!(
293            matches!(err, crate::EdifactError::InvalidEventSequence { .. }),
294            "expected InvalidEventSequence, got {err:?}"
295        );
296    }
297
298    #[test]
299    fn writer_emitter_end_segment_without_start_is_err() {
300        let mut e = WriterEmitter::new(Vec::<u8>::new());
301        let err = e.emit(EdifactEvent::EndSegment).unwrap_err();
302        assert!(
303            matches!(err, crate::EdifactError::InvalidEventSequence { .. }),
304            "expected InvalidEventSequence, got {err:?}"
305        );
306    }
307}