Skip to main content

edifact_rs/
model.rs

1use smallvec::SmallVec;
2use std::borrow::Cow;
3
4/// A half-open byte span within an EDIFACT payload.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
6pub struct Span {
7    /// Start byte offset (inclusive).
8    pub start: usize,
9    /// End byte offset (exclusive).
10    pub end: usize,
11}
12
13impl Span {
14    #[inline]
15    /// Construct a span from inclusive start and exclusive end offsets.
16    pub const fn new(start: usize, end: usize) -> Self {
17        Self { start, end }
18    }
19
20    #[inline]
21    /// Shift the span by `delta` bytes.
22    pub const fn offset(self, delta: usize) -> Self {
23        Self {
24            start: self.start + delta,
25            end: self.end + delta,
26        }
27    }
28}
29
30impl std::fmt::Display for Span {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        write!(f, "{}..{}", self.start, self.end)
33    }
34}
35
36/// A single EDIFACT segment, borrowing its data from the source input.
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct Segment<'a> {
39    /// Segment tag, usually three uppercase letters.
40    pub tag: &'a str,
41    /// Span covering the whole segment payload.
42    pub span: Span,
43    /// Span covering only the segment tag.
44    pub tag_span: Span,
45    /// Segment elements in positional order.
46    pub elements: Vec<Element<'a>>,
47}
48
49impl<'a> Segment<'a> {
50    #[inline]
51    /// Construct a segment with default spans.
52    pub fn new(tag: &'a str, elements: Vec<Element<'a>>) -> Self {
53        Self {
54            tag,
55            span: Span::default(),
56            tag_span: Span::default(),
57            elements,
58        }
59    }
60
61    /// Return the element at position `n` (0-indexed), if it exists.
62    #[inline]
63    pub fn get_element(&self, n: usize) -> Option<&Element<'a>> {
64        self.elements.get(n)
65    }
66
67    /// Shorthand: get component 0 of element `n` — the most common access pattern.
68    #[inline]
69    pub fn element_str(&self, n: usize) -> Option<&str> {
70        self.elements.get(n)?.get_component(0)
71    }
72
73    /// Return the byte span of the element at position `n`, if it exists.
74    #[inline]
75    pub fn element_span(&self, n: usize) -> Option<Span> {
76        Some(self.elements.get(n)?.span)
77    }
78}
79
80/// A data element, which may have one or more component values.
81///
82/// Uses [`SmallVec`] with an inline capacity of 4 to avoid heap allocation
83/// for the common case (≤ 4 components).  Component values borrow from the
84/// original input; if the value contained a release-character sequence the
85/// resolved string is stored as an owned [`Cow::Owned`] variant instead of
86/// using `Box::leak`.
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct Element<'a> {
89    /// Span covering the whole element.
90    pub span: Span,
91    /// Element components in positional order.
92    pub components: SmallVec<[Cow<'a, str>; 4]>,
93    /// Byte spans for each component in [`Self::components`].
94    pub component_spans: SmallVec<[Span; 4]>,
95}
96
97impl<'a> Element<'a> {
98    /// Return the component at position `n` (0-indexed), if it exists.
99    #[inline]
100    pub fn get_component(&self, n: usize) -> Option<&str> {
101        self.components.get(n).map(|c| c.as_ref())
102    }
103
104    /// Return the component at position `n`, or `""` if absent.
105    #[inline]
106    pub fn component_or_empty(&self, n: usize) -> &str {
107        self.components.get(n).map(|c| c.as_ref()).unwrap_or("")
108    }
109
110    /// Return the byte span of the component at position `n`, if it exists.
111    #[inline]
112    pub fn component_span(&self, n: usize) -> Option<Span> {
113        self.component_spans.get(n).copied()
114    }
115
116    /// Convenience constructor: wraps string literals as borrowed components.
117    ///
118    /// Useful in tests and when constructing segments for writing.
119    pub fn of(components: &[&'a str]) -> Self {
120        Self {
121            span: Span::default(),
122            components: components.iter().copied().map(Cow::Borrowed).collect(),
123            component_spans: std::iter::repeat_n(Span::default(), components.len()).collect(),
124        }
125    }
126}
127
128/// Owned data element used by reader-based parsing APIs.
129#[derive(Debug, Clone, PartialEq, Eq)]
130pub struct OwnedElement {
131    /// Span covering the whole element.
132    pub span: Span,
133    /// Owned element components in positional order.
134    pub components: SmallVec<[String; 4]>,
135    /// Byte spans for each component in [`Self::components`].
136    pub component_spans: SmallVec<[Span; 4]>,
137}
138
139impl OwnedElement {
140    #[inline]
141    /// View this owned element as a borrowed [`Element`].
142    ///
143    /// **Performance note**: allocates a `SmallVec<[Cow<str>; N]>` on every
144    /// call.  If you only need to inspect individual components, use
145    /// [`OwnedSegment::borrow`] → [`BorrowedElement`] instead, which is O(1).
146    pub fn as_borrowed(&self) -> Element<'_> {
147        Element {
148            span: self.span,
149            components: self
150                .components
151                .iter()
152                .map(|component| Cow::Borrowed(component.as_str()))
153                .collect(),
154            component_spans: self.component_spans.clone(),
155        }
156    }
157
158    #[inline]
159    /// Shift all stored spans by `delta` bytes.
160    pub fn offset(mut self, delta: usize) -> Self {
161        self.span = self.span.offset(delta);
162        for span in &mut self.component_spans {
163            *span = span.offset(delta);
164        }
165        self
166    }
167}
168
169impl<'a> From<Element<'a>> for OwnedElement {
170    fn from(value: Element<'a>) -> Self {
171        Self {
172            span: value.span,
173            components: value
174                .components
175                .into_iter()
176                .map(|component| component.into_owned())
177                .collect(),
178            component_spans: value.component_spans,
179        }
180    }
181}
182
183/// Owned segment used by reader-based parsing APIs.
184#[derive(Debug, Clone, PartialEq, Eq)]
185pub struct OwnedSegment {
186    /// Segment tag, usually three uppercase letters.
187    pub tag: String,
188    /// Span covering the whole segment payload.
189    pub span: Span,
190    /// Span covering only the segment tag.
191    pub tag_span: Span,
192    /// Owned segment elements in positional order.
193    pub elements: Vec<OwnedElement>,
194}
195
196/// Zero-allocation view of an [`OwnedElement`].
197///
198/// Implements the same accessor methods as [`Element`] without constructing
199/// any intermediate `SmallVec` or `Cow` values.  Use this when you hold an
200/// `&OwnedSegment` reference and want to inspect element data without the
201/// `Vec<Element>` allocation that [`OwnedSegment::as_borrowed`] incurs.
202#[derive(Debug, Clone, Copy)]
203pub struct BorrowedElement<'a>(pub(crate) &'a OwnedElement);
204
205impl<'a> BorrowedElement<'a> {
206    /// Return the component at position `n` (0-indexed), if it exists.
207    #[inline]
208    pub fn get_component(&self, n: usize) -> Option<&'a str> {
209        self.0.components.get(n).map(|s| s.as_str())
210    }
211
212    /// Return the component at position `n`, or `""` if absent.
213    #[inline]
214    pub fn component_or_empty(&self, n: usize) -> &'a str {
215        self.0.components.get(n).map(|s| s.as_str()).unwrap_or("")
216    }
217
218    /// Return the byte span of the component at position `n`, if it exists.
219    #[inline]
220    pub fn component_span(&self, n: usize) -> Option<Span> {
221        self.0.component_spans.get(n).copied()
222    }
223
224    /// The byte span covering the whole element.
225    #[inline]
226    pub fn span(&self) -> Span {
227        self.0.span
228    }
229
230    /// Number of components in this element.
231    #[inline]
232    pub fn len(&self) -> usize {
233        self.0.components.len()
234    }
235
236    /// Returns `true` if this element has no components.
237    #[inline]
238    pub fn is_empty(&self) -> bool {
239        self.0.components.is_empty()
240    }
241
242    /// Iterate over all component strings.
243    #[inline]
244    pub fn iter(&self) -> impl Iterator<Item = &'a str> {
245        self.0.components.iter().map(|c| c.as_str())
246    }
247}
248
249/// Zero-allocation view of an [`OwnedSegment`].
250///
251/// Implements the same accessor methods as [`Segment`] without constructing
252/// a `Vec<Element>`.  Use this when you hold an `&OwnedSegment` reference and
253/// want to read data without the allocations incurred by
254/// [`OwnedSegment::as_borrowed`].
255#[derive(Debug, Clone, Copy)]
256pub struct BorrowedSegment<'a>(pub(crate) &'a OwnedSegment);
257
258impl<'a> BorrowedSegment<'a> {
259    /// The segment tag (e.g. `"BGM"`).
260    #[inline]
261    pub fn tag(&self) -> &'a str {
262        &self.0.tag
263    }
264
265    /// Byte span covering the whole segment.
266    #[inline]
267    pub fn span(&self) -> Span {
268        self.0.span
269    }
270
271    /// Byte span covering only the segment tag.
272    #[inline]
273    pub fn tag_span(&self) -> Span {
274        self.0.tag_span
275    }
276
277    /// Return the element at position `n` (0-indexed), if it exists.
278    #[inline]
279    pub fn get_element(&self, n: usize) -> Option<BorrowedElement<'a>> {
280        self.0.elements.get(n).map(BorrowedElement)
281    }
282
283    /// Shorthand: first component of element `n` — the most common access pattern.
284    #[inline]
285    pub fn element_str(&self, n: usize) -> Option<&'a str> {
286        self.0.elements.get(n)?.components.first().map(|c| c.as_str())
287    }
288
289    /// Return the byte span of the element at position `n`, if it exists.
290    #[inline]
291    pub fn element_span(&self, n: usize) -> Option<Span> {
292        Some(self.0.elements.get(n)?.span)
293    }
294
295    /// Iterate over all elements as zero-allocation views.
296    #[inline]
297    pub fn elements(&self) -> impl Iterator<Item = BorrowedElement<'a>> {
298        self.0.elements.iter().map(BorrowedElement)
299    }
300}
301
302impl OwnedSegment {
303    /// Get the first component of element `n`, or `None` if absent.
304    ///
305    /// This is the zero-allocation equivalent of `as_borrowed().element_str(n)`.
306    /// Used internally by [`crate::find_segment_owned`] and the derived
307    /// [`crate::EdifactDeserialize::edifact_deserialize_owned`] implementations.
308    #[inline]
309    pub fn element_str(&self, n: usize) -> Option<&str> {
310        self.elements.get(n)?.components.first().map(|s| s.as_str())
311    }
312
313    /// Get component `comp` of element `elem`, or `None` if absent.
314    ///
315    /// Zero-allocation equivalent of `as_borrowed().get_element(elem)?.get_component(comp)`.
316    #[inline]
317    pub fn component_str(&self, elem: usize, comp: usize) -> Option<&str> {
318        self.elements.get(elem)?.components.get(comp).map(|s| s.as_str())
319    }
320
321    #[inline]
322    /// Shift all stored spans by `delta` bytes.
323    pub fn offset(mut self, delta: usize) -> Self {
324        self.span = self.span.offset(delta);
325        self.tag_span = self.tag_span.offset(delta);
326        for element in &mut self.elements {
327            element.span = element.span.offset(delta);
328            for span in &mut element.component_spans {
329                *span = span.offset(delta);
330            }
331        }
332        self
333    }
334
335    #[inline]
336    /// View this owned segment as a borrowed [`Segment`].
337    ///
338    /// **Performance note**: allocates a `Vec<Element<'_>>` on every call.
339    /// When only individual field access is needed, prefer
340    /// [`OwnedSegment::borrow`] → [`BorrowedSegment`] which is O(1).
341    /// `as_borrowed` remains necessary when the callee requires `&[Segment<'_>]`.
342    pub fn as_borrowed(&self) -> Segment<'_> {
343        Segment {
344            tag: self.tag.as_str(),
345            span: self.span,
346            tag_span: self.tag_span,
347            elements: self
348                .elements
349                .iter()
350                .map(OwnedElement::as_borrowed)
351                .collect(),
352        }
353    }
354
355    /// Return a zero-allocation view of this segment.
356    ///
357    /// Unlike [`as_borrowed`][OwnedSegment::as_borrowed], this is `O(1)` and
358    /// performs no heap allocation.  The view cannot be passed to APIs that
359    /// require `&[Segment<'_>]`; use [`as_borrowed`][OwnedSegment::as_borrowed]
360    /// for those call sites.
361    #[inline]
362    pub fn borrow(&self) -> BorrowedSegment<'_> {
363        BorrowedSegment(self)
364    }
365}
366
367impl<'a> From<Segment<'a>> for OwnedSegment {
368    fn from(value: Segment<'a>) -> Self {
369        Self {
370            tag: value.tag.to_string(),
371            span: value.span,
372            tag_span: value.tag_span,
373            elements: value.elements.into_iter().map(OwnedElement::from).collect(),
374        }
375    }
376}