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