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/// Writes EDIFACT events directly to any [`Write`] implementation.
105///
106/// Each event is written to the underlying writer immediately — no intermediate
107/// buffering of element strings occurs, so no heap allocation is required per
108/// event.  This makes `WriterEmitter` suitable for high-throughput serialization
109/// of large EDIFACT messages.
110///
111/// # Protocol
112///
113/// Events must arrive in the order produced by [`crate::EdifactSerialize`]:
114/// `StartSegment` → zero or more `Element` / `ComponentElement` → `EndSegment`.
115/// In debug builds, any violation of this order (e.g. `Element` before
116/// `StartSegment`, or `StartSegment` inside an open segment) panics immediately.
117pub struct WriterEmitter<W: Write> {
118    writer: crate::Writer<W>,
119    #[cfg(debug_assertions)]
120    in_segment: bool,
121}
122
123impl<W: Write> WriterEmitter<W> {
124    /// Create a new `WriterEmitter` with default EDIFACT delimiters.
125    pub fn new(inner: W) -> Self {
126        Self {
127            writer: crate::Writer::new(inner),
128            #[cfg(debug_assertions)]
129            in_segment: false,
130        }
131    }
132
133    /// Flush and consume the emitter, returning the underlying writer.
134    pub fn finish(self) -> Result<W, EdifactError> {
135        self.writer.finish()
136    }
137
138    /// Number of complete segments written so far.
139    pub fn segment_count(&self) -> u32 {
140        self.writer.segment_count()
141    }
142}
143
144impl<W: Write> EventEmitter for WriterEmitter<W> {
145    fn emit(&mut self, event: EdifactEvent<'_>) -> Result<(), EdifactError> {
146        match event {
147            EdifactEvent::StartSegment { tag } => {
148                #[cfg(debug_assertions)]
149                {
150                    assert!(
151                        !self.in_segment,
152                        "WriterEmitter: StartSegment emitted while a segment is already open (missing EndSegment)"
153                    );
154                    self.in_segment = true;
155                }
156                self.writer.write_tag_only(tag)?;
157            }
158            EdifactEvent::Element { value } => {
159                #[cfg(debug_assertions)]
160                assert!(
161                    self.in_segment,
162                    "WriterEmitter: Element emitted outside of a segment (missing StartSegment)"
163                );
164                self.writer.write_element_sep()?;
165                self.writer.write_escaped(value)?;
166            }
167            EdifactEvent::ComponentElement { value } => {
168                #[cfg(debug_assertions)]
169                assert!(
170                    self.in_segment,
171                    "WriterEmitter: ComponentElement emitted outside of a segment (missing StartSegment)"
172                );
173                self.writer.write_component_sep()?;
174                self.writer.write_escaped(value)?;
175            }
176            EdifactEvent::EndSegment => {
177                #[cfg(debug_assertions)]
178                {
179                    assert!(
180                        self.in_segment,
181                        "WriterEmitter: EndSegment emitted while no segment is open (missing StartSegment)"
182                    );
183                    self.in_segment = false;
184                }
185                self.writer.write_segment_term_and_count()?;
186            }
187        }
188        Ok(())
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195
196    #[test]
197    fn vec_emitter_no_memory_leak() {
198        let mut e = VecEmitter::default();
199        e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
200        e.emit(EdifactEvent::Element { value: "E03" }).unwrap();
201        e.emit(EdifactEvent::EndSegment).unwrap();
202        assert_eq!(
203            e.events[0],
204            OwnedEdifactEvent::StartSegment {
205                tag: "BGM".to_owned()
206            }
207        );
208        assert_eq!(
209            e.events[1],
210            OwnedEdifactEvent::Element {
211                value: "E03".to_owned()
212            }
213        );
214    }
215
216    #[test]
217    fn writer_emitter_produces_valid_edifact() {
218        let mut buf = Vec::new();
219        {
220            let mut e = WriterEmitter::new(&mut buf);
221            e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
222            e.emit(EdifactEvent::Element { value: "E03" }).unwrap();
223            e.emit(EdifactEvent::Element { value: "11042" }).unwrap();
224            e.emit(EdifactEvent::EndSegment).unwrap();
225            e.finish().unwrap();
226        }
227        assert_eq!(buf, b"BGM+E03+11042'");
228    }
229
230    #[test]
231    fn writer_emitter_handles_components() {
232        let mut buf = Vec::new();
233        {
234            let mut e = WriterEmitter::new(&mut buf);
235            e.emit(EdifactEvent::StartSegment { tag: "NAD" }).unwrap();
236            e.emit(EdifactEvent::Element { value: "MS" }).unwrap();
237            e.emit(EdifactEvent::Element {
238                value: "9900112233445",
239            })
240            .unwrap();
241            e.emit(EdifactEvent::ComponentElement { value: "" })
242                .unwrap();
243            e.emit(EdifactEvent::ComponentElement { value: "293" })
244                .unwrap();
245            e.emit(EdifactEvent::EndSegment).unwrap();
246            e.finish().unwrap();
247        }
248        let s = std::str::from_utf8(&buf).unwrap();
249        assert_eq!(s, "NAD+MS+9900112233445::293'");
250    }
251}