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)]
14pub enum EdifactEvent<'a> {
15    /// Beginning of a new segment (e.g. `"BGM"`, `"NAD"`).
16    StartSegment {
17        /// Segment tag.
18        tag: &'a str,
19    },
20    /// A data element value — first (or only) component of a new element.
21    Element {
22        /// Element text value.
23        value: &'a str,
24    },
25    /// An additional component within the current element.
26    ComponentElement {
27        /// Component text value.
28        value: &'a str,
29    },
30    /// End of the current segment.
31    EndSegment,
32}
33
34/// An owned EDIFACT event — for collection and testing (no borrowed lifetimes).
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub enum OwnedEdifactEvent {
37    /// Owned segment-start event.
38    StartSegment {
39        /// Segment tag.
40        tag: String,
41    },
42    /// Owned element event.
43    Element {
44        /// Element text value.
45        value: String,
46    },
47    /// Owned component event.
48    ComponentElement {
49        /// Component text value.
50        value: String,
51    },
52    /// Owned segment-end event.
53    EndSegment,
54}
55
56impl<'a> EdifactEvent<'a> {
57    /// Convert to an owned event, cloning string data.
58    pub fn into_owned(self) -> OwnedEdifactEvent {
59        match self {
60            Self::StartSegment { tag } => OwnedEdifactEvent::StartSegment {
61                tag: tag.to_owned(),
62            },
63            Self::Element { value } => OwnedEdifactEvent::Element {
64                value: value.to_owned(),
65            },
66            Self::ComponentElement { value } => OwnedEdifactEvent::ComponentElement {
67                value: value.to_owned(),
68            },
69            Self::EndSegment => OwnedEdifactEvent::EndSegment,
70        }
71    }
72}
73
74// ── emitter trait ─────────────────────────────────────────────────────────────
75
76/// Trait for any sink that can consume [`EdifactEvent`]s.
77pub trait EventEmitter {
78    /// Consume one event.
79    fn emit(&mut self, event: EdifactEvent<'_>) -> Result<(), EdifactError>;
80}
81
82// ── VecEmitter ────────────────────────────────────────────────────────────────
83
84/// Collects events into a [`Vec<OwnedEdifactEvent>`].
85///
86/// Useful for testing and introspection.  Does not leak memory.
87#[derive(Debug, Default)]
88pub struct VecEmitter {
89    /// Collected owned events.
90    pub events: Vec<OwnedEdifactEvent>,
91}
92
93impl EventEmitter for VecEmitter {
94    fn emit(&mut self, event: EdifactEvent<'_>) -> Result<(), EdifactError> {
95        self.events.push(event.into_owned());
96        Ok(())
97    }
98}
99
100// ── WriterEmitter ─────────────────────────────────────────────────────────────
101
102/// Writes EDIFACT events directly to any [`Write`] implementation.
103///
104/// Each event is written to the underlying writer immediately — no intermediate
105/// buffering of element strings occurs, so no heap allocation is required per
106/// event.  This makes `WriterEmitter` suitable for high-throughput serialization
107/// of large EDIFACT messages.
108///
109/// # Protocol
110///
111/// Events must arrive in the order produced by [`crate::EdifactSerialize`]:
112/// `StartSegment` → zero or more `Element` / `ComponentElement` → `EndSegment`.
113/// Violating this order (e.g. `Element` before `StartSegment`) produces
114/// malformed output; no runtime check is performed for performance reasons.
115pub struct WriterEmitter<W: Write> {
116    writer: crate::Writer<W>,
117}
118
119impl<W: Write> WriterEmitter<W> {
120    /// Create a new `WriterEmitter` with default EDIFACT delimiters.
121    pub fn new(inner: W) -> Self {
122        Self {
123            writer: crate::Writer::new(inner),
124        }
125    }
126
127    /// Flush and consume the emitter, returning the underlying writer.
128    pub fn finish(self) -> Result<W, EdifactError> {
129        self.writer.finish()
130    }
131
132    /// Number of complete segments written so far.
133    pub fn segment_count(&self) -> u32 {
134        self.writer.segment_count()
135    }
136}
137
138impl<W: Write> EventEmitter for WriterEmitter<W> {
139    fn emit(&mut self, event: EdifactEvent<'_>) -> Result<(), EdifactError> {
140        match event {
141            EdifactEvent::StartSegment { tag } => {
142                self.writer.write_tag_only(tag)?;
143            }
144            EdifactEvent::Element { value } => {
145                self.writer.write_element_sep()?;
146                self.writer.write_escaped(value)?;
147            }
148            EdifactEvent::ComponentElement { value } => {
149                self.writer.write_component_sep()?;
150                self.writer.write_escaped(value)?;
151            }
152            EdifactEvent::EndSegment => {
153                self.writer.write_segment_term_and_count()?;
154            }
155        }
156        Ok(())
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    fn vec_emitter_no_memory_leak() {
166        let mut e = VecEmitter::default();
167        e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
168        e.emit(EdifactEvent::Element { value: "E03" }).unwrap();
169        e.emit(EdifactEvent::EndSegment).unwrap();
170        assert_eq!(
171            e.events[0],
172            OwnedEdifactEvent::StartSegment {
173                tag: "BGM".to_owned()
174            }
175        );
176        assert_eq!(
177            e.events[1],
178            OwnedEdifactEvent::Element {
179                value: "E03".to_owned()
180            }
181        );
182    }
183
184    #[test]
185    fn writer_emitter_produces_valid_edifact() {
186        let mut buf = Vec::new();
187        {
188            let mut e = WriterEmitter::new(&mut buf);
189            e.emit(EdifactEvent::StartSegment { tag: "BGM" }).unwrap();
190            e.emit(EdifactEvent::Element { value: "E03" }).unwrap();
191            e.emit(EdifactEvent::Element { value: "11042" }).unwrap();
192            e.emit(EdifactEvent::EndSegment).unwrap();
193            e.finish().unwrap();
194        }
195        assert_eq!(buf, b"BGM+E03+11042'");
196    }
197
198    #[test]
199    fn writer_emitter_handles_components() {
200        let mut buf = Vec::new();
201        {
202            let mut e = WriterEmitter::new(&mut buf);
203            e.emit(EdifactEvent::StartSegment { tag: "NAD" }).unwrap();
204            e.emit(EdifactEvent::Element { value: "MS" }).unwrap();
205            e.emit(EdifactEvent::Element {
206                value: "9900112233445",
207            })
208            .unwrap();
209            e.emit(EdifactEvent::ComponentElement { value: "" })
210                .unwrap();
211            e.emit(EdifactEvent::ComponentElement { value: "293" })
212                .unwrap();
213            e.emit(EdifactEvent::EndSegment).unwrap();
214            e.finish().unwrap();
215        }
216        let s = std::str::from_utf8(&buf).unwrap();
217        assert_eq!(s, "NAD+MS+9900112233445::293'");
218    }
219}