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}