Skip to main content

edifact_rs/
model.rs

1use crate::directory_validator::{ElementPath, SegmentLayout};
2use crate::error::EdifactError;
3use smallvec::SmallVec;
4use std::borrow::Cow;
5
6/// Reject a layout whose tag does not describe `segment_tag`.
7///
8/// Resolving `"3055"` against the wrong definition would silently address a
9/// different element — the exact failure mode code-addressed access exists to
10/// eliminate — so the mismatch is an error rather than a lookup miss.
11#[inline]
12fn check_layout_tag<L: SegmentLayout + ?Sized>(
13    layout: &L,
14    segment_tag: &str,
15) -> Result<(), EdifactError> {
16    if layout.layout_tag() != segment_tag {
17        return Err(EdifactError::SegmentLayoutMismatch {
18            expected: layout.layout_tag().to_owned(),
19            actual: segment_tag.to_owned(),
20        });
21    }
22    Ok(())
23}
24
25/// A half-open byte span within an EDIFACT payload.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct Span {
29    /// Start byte offset (inclusive).
30    pub start: usize,
31    /// End byte offset (exclusive).
32    pub end: usize,
33}
34
35impl Span {
36    #[inline]
37    /// Construct a span from inclusive start and exclusive end offsets.
38    pub const fn new(start: usize, end: usize) -> Self {
39        Self { start, end }
40    }
41
42    #[inline]
43    /// Shift the span by `delta` bytes.
44    ///
45    /// Uses saturating addition to avoid integer overflow on malformed input.
46    pub const fn offset(self, delta: usize) -> Self {
47        Self {
48            start: self.start.saturating_add(delta),
49            end: self.end.saturating_add(delta),
50        }
51    }
52
53    /// Length of the span in bytes.
54    ///
55    /// In debug builds, asserts `end >= start` (inverted spans are a bug).
56    /// In release builds, returns 0 for inverted spans rather than panicking,
57    /// so a single corrupt span does not abort an entire validation run.
58    #[inline]
59    pub fn len(self) -> usize {
60        debug_assert!(
61            self.end >= self.start,
62            "Span::len: end ({}) < start ({})",
63            self.end,
64            self.start
65        );
66        self.end.saturating_sub(self.start)
67    }
68
69    /// Returns `true` if the span covers zero bytes.
70    #[inline]
71    pub const fn is_empty(self) -> bool {
72        self.start == self.end
73    }
74}
75
76impl std::fmt::Display for Span {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        write!(f, "{}..{}", self.start, self.end)
79    }
80}
81
82/// A single EDIFACT segment, borrowing its data from the source input.
83///
84/// `#[non_exhaustive]`: build one with [`Segment::new`] rather than a struct
85/// literal.  Adding `repeats` to [`Element`] in 0.14 broke every downstream
86/// literal, and the next field would do it again; a constructor plus builder
87/// setters keeps that additive.  The fields stay public, so reading and `..`
88/// destructuring are unaffected.
89#[derive(Debug, Clone, PartialEq, Eq)]
90#[non_exhaustive]
91pub struct Segment<'a> {
92    /// Segment tag, usually three uppercase letters.
93    pub tag: &'a str,
94    /// Span covering the whole segment payload.
95    pub span: Span,
96    /// Span covering only the segment tag.
97    pub tag_span: Span,
98    /// Segment elements in positional order.
99    pub elements: Vec<Element<'a>>,
100}
101
102impl<'a> Segment<'a> {
103    #[inline]
104    /// Construct a segment with default spans.
105    pub fn new(tag: &'a str, elements: Vec<Element<'a>>) -> Self {
106        Self {
107            tag,
108            span: Span::default(),
109            tag_span: Span::default(),
110            elements,
111        }
112    }
113
114    /// Return the element at position `n` (0-indexed), if it exists.
115    #[inline]
116    pub fn get_element(&self, n: usize) -> Option<&Element<'a>> {
117        self.elements.get(n)
118    }
119
120    /// Shorthand: get component 0 of element `n` — the most common access pattern.
121    #[inline]
122    pub fn element_str(&self, n: usize) -> Option<&str> {
123        self.elements.get(n)?.get_component(0)
124    }
125
126    /// Get component `comp` of element `elem` (both 0-based), or `None` if absent.
127    ///
128    /// Mirrors [`OwnedSegment::component_str`], eliminating the need to chain
129    /// `get_element(elem)?.get_component(comp)` in rule closures.
130    #[inline]
131    pub fn component_str(&self, elem: usize, comp: usize) -> Option<&str> {
132        self.elements.get(elem)?.get_component(comp)
133    }
134
135    /// Return the byte span of the element at position `n`, if it exists.
136    #[inline]
137    pub fn element_span(&self, n: usize) -> Option<Span> {
138        Some(self.elements.get(n)?.span)
139    }
140
141    // ── code-addressed access ─────────────────────────────────────────────────
142
143    /// Read the value at an already-resolved [`ElementPath`].
144    ///
145    /// Use this when the same path is reused across many segments — resolve once
146    /// with [`SegmentLayout::resolve_code`], then read without repeating the
147    /// lookup.
148    #[inline]
149    pub fn value_at(&self, path: ElementPath) -> Option<&str> {
150        self.elements
151            .get(path.element)?
152            .get_component(path.component_index())
153    }
154
155    /// Byte span of the value at an already-resolved [`ElementPath`].
156    #[inline]
157    pub fn span_at(&self, path: ElementPath) -> Option<Span> {
158        let element = self.elements.get(path.element)?;
159        match path.component {
160            Some(c) => element.component_span(c),
161            None => Some(element.span),
162        }
163    }
164
165    /// Read a value by its UN/EDIFACT data element identifier.
166    ///
167    /// Positional access (`seg.element_str(4)`) fails silently when the index is
168    /// wrong: it reads a different, usually still-plausible value.  Code-addressed
169    /// access cannot — a stale or mistyped identifier is a
170    /// [`EdifactError::UnknownDataElement`], checked against the directory.
171    ///
172    /// `Ok(None)` means the identifier is valid for this segment but the value is
173    /// absent from *this* instance, which is the normal state for a conditional
174    /// element.
175    ///
176    /// # Performance
177    ///
178    /// Each call scans the layout for the identifier. That is a handful of short
179    /// string comparisons and fine for one-off reads, but when pulling the same
180    /// identifier out of many segments, resolve once with
181    /// [`SegmentLayout::resolve_code`] and read with [`value_at`](Self::value_at).
182    ///
183    /// # Example
184    ///
185    /// ```rust
186    /// use edifact_rs::{ComponentRef, ElementRef, SegmentDefinition, Status};
187    ///
188    /// static C507: &[ComponentRef] = &[
189    ///     ComponentRef::new(1, "2005", Status::Mandatory),
190    ///     ComponentRef::new(2, "2380", Status::Conditional),
191    ///     ComponentRef::new(3, "2379", Status::Conditional),
192    /// ];
193    /// static DTM_ELEMENTS: &[ElementRef] =
194    ///     &[ElementRef::composite(1, "C507", Status::Mandatory, 1, C507)];
195    /// static DTM: SegmentDefinition =
196    ///     SegmentDefinition::new("DTM", "Date/time/period", DTM_ELEMENTS);
197    ///
198    /// let segments: Vec<_> = edifact_rs::from_bytes(b"DTM+137:20260101:102'")
199    ///     .collect::<Result<Vec<_>, _>>()?;
200    /// let dtm = &segments[0];
201    ///
202    /// assert_eq!(dtm.value_by_code(&DTM, "2380")?, Some("20260101"));
203    /// // A data element that this segment does not define is a hard error,
204    /// // not a wrong-but-quiet read.
205    /// assert!(dtm.value_by_code(&DTM, "3055").is_err());
206    /// # Ok::<(), edifact_rs::EdifactError>(())
207    /// ```
208    ///
209    /// # Errors
210    ///
211    /// Returns [`EdifactError::SegmentLayoutMismatch`] when `layout` describes a
212    /// different segment tag, [`EdifactError::UnknownDataElement`] when the
213    /// identifier is not in the definition, and
214    /// [`EdifactError::AmbiguousDataElement`] when it appears more than once.
215    pub fn value_by_code<L: SegmentLayout + ?Sized>(
216        &self,
217        layout: &L,
218        data_element: &str,
219    ) -> Result<Option<&str>, EdifactError> {
220        check_layout_tag(layout, self.tag)?;
221        Ok(self.value_at(layout.resolve_code(data_element)?))
222    }
223
224    /// Byte span of a value addressed by its UN/EDIFACT data element identifier.
225    ///
226    /// Use this to attach a precise [`Span`] to a
227    /// [`ValidationIssue`][crate::ValidationIssue] without hand-counting indices.
228    ///
229    /// # Errors
230    ///
231    /// As [`value_by_code`][Self::value_by_code].
232    pub fn span_by_code<L: SegmentLayout + ?Sized>(
233        &self,
234        layout: &L,
235        data_element: &str,
236    ) -> Result<Option<Span>, EdifactError> {
237        check_layout_tag(layout, self.tag)?;
238        Ok(self.span_at(layout.resolve_code(data_element)?))
239    }
240
241    /// Return the whole [`Element`] addressed by a data element identifier.
242    ///
243    /// When the identifier names a component inside a composite, the enclosing
244    /// composite element is returned.
245    ///
246    /// # Errors
247    ///
248    /// As [`value_by_code`][Self::value_by_code].
249    pub fn element_by_code<L: SegmentLayout + ?Sized>(
250        &self,
251        layout: &L,
252        data_element: &str,
253    ) -> Result<Option<&Element<'a>>, EdifactError> {
254        check_layout_tag(layout, self.tag)?;
255        let path = layout.resolve_code(data_element)?;
256        Ok(self.elements.get(path.element))
257    }
258}
259
260/// Components of one repetition of a data element, each paired with its span.
261pub type Components<'a> = SmallVec<[(Cow<'a, str>, Span); 4]>;
262
263/// Components of one repetition of an owned data element.
264pub type OwnedComponents = SmallVec<[(String, Span); 4]>;
265
266/// A data element, which may have one or more component values.
267///
268/// `#[non_exhaustive]`: build one with [`Element::of`] (plus
269/// [`and_repeat`][Element::and_repeat] / [`with_span`][Element::with_span])
270/// rather than a struct literal.
271///
272/// Uses [`SmallVec`] with an inline capacity of 4 to avoid heap allocation
273/// for the common case (≤ 4 components).  Component values borrow from the
274/// original input; if the value contained a release-character sequence the
275/// resolved string is stored as an owned [`Cow::Owned`] variant instead of
276/// using `Box::leak`.
277///
278/// Each entry is a `(value, span)` pair, guaranteeing that the component
279/// string and its byte span are always in sync.
280///
281/// # Repetition (ISO 9735-4 §3.1)
282///
283/// [`components`][Self::components] holds the **first** repetition, which is the
284/// only one for every interchange that does not declare a repetition separator
285/// in its `UNA` — that is, virtually all of them.  Further repetitions land in
286/// [`repeats`][Self::repeats]; read them together with
287/// [`repetitions`][Self::repetitions].
288#[derive(Debug, Clone, PartialEq, Eq)]
289#[non_exhaustive]
290pub struct Element<'a> {
291    /// Span covering the whole element, including every repetition.
292    pub span: Span,
293    /// Components of the first repetition, in positional order.
294    pub components: Components<'a>,
295    /// Second and subsequent repetitions of this data element.
296    ///
297    /// Empty — and therefore unallocated — unless the interchange declares a
298    /// repetition separator and the element actually repeats.
299    pub repeats: Vec<Components<'a>>,
300}
301
302impl<'a> Element<'a> {
303    /// Return the component at position `n` (0-indexed) of the first repetition.
304    #[inline]
305    pub fn get_component(&self, n: usize) -> Option<&str> {
306        self.components.get(n).map(|(c, _)| c.as_ref())
307    }
308
309    /// Number of repetitions of this data element — always at least 1.
310    #[inline]
311    pub fn repeat_count(&self) -> usize {
312        1 + self.repeats.len()
313    }
314
315    /// Components of repetition `n` (0-indexed), if it exists.
316    #[inline]
317    pub fn repetition(&self, n: usize) -> Option<&[(Cow<'a, str>, Span)]> {
318        match n {
319            0 => Some(&self.components),
320            _ => self.repeats.get(n - 1).map(|r| r.as_slice()),
321        }
322    }
323
324    /// Iterate over every repetition of this element, first one included.
325    ///
326    /// # Example
327    ///
328    /// ```
329    /// // `UNA` byte 7 declares `*` as the repetition separator.
330    /// let segments: Vec<_> = edifact_rs::from_bytes(b"UNA:+.?*'RFF+ON:1*ON:2'")
331    ///     .collect::<Result<Vec<_>, _>>()?;
332    /// let rff = segments[0].get_element(0).unwrap();
333    ///
334    /// let refs: Vec<&str> = rff
335    ///     .repetitions()
336    ///     .map(|components| components[1].0.as_ref())
337    ///     .collect();
338    /// assert_eq!(refs, ["1", "2"]);
339    /// # Ok::<(), edifact_rs::EdifactError>(())
340    /// ```
341    #[inline]
342    pub fn repetitions(&self) -> impl Iterator<Item = &[(Cow<'a, str>, Span)]> {
343        std::iter::once(self.components.as_slice()).chain(self.repeats.iter().map(|r| r.as_slice()))
344    }
345
346    /// Return the component at position `n`, or `""` if absent.
347    #[inline]
348    pub fn component_or_empty(&self, n: usize) -> &str {
349        self.components
350            .get(n)
351            .map(|(c, _)| c.as_ref())
352            .unwrap_or("")
353    }
354
355    /// Return the byte span of the component at position `n`, if it exists.
356    #[inline]
357    pub fn component_span(&self, n: usize) -> Option<Span> {
358        self.components.get(n).map(|(_, s)| *s)
359    }
360
361    /// Convenience constructor: wraps string literals as borrowed components.
362    ///
363    /// Useful in tests and when constructing segments for writing.
364    pub fn of(components: &[&'a str]) -> Self {
365        Self {
366            span: Span::default(),
367            components: components
368                .iter()
369                .copied()
370                .map(|c| (Cow::Borrowed(c), Span::default()))
371                .collect(),
372            repeats: Vec::new(),
373        }
374    }
375
376    /// Set the span covering this element.
377    ///
378    /// Parsed elements carry real spans; hand-built ones default to
379    /// [`Span::default`] and only need this when the caller is synthesising
380    /// input for diagnostics.
381    #[must_use]
382    pub fn with_span(mut self, span: Span) -> Self {
383        self.span = span;
384        self
385    }
386
387    /// Append a further repetition of this data element (ISO 9735-4 §3.1).
388    ///
389    /// Useful when building segments for [`Writer::write_segment`][crate::Writer::write_segment];
390    /// the writer joins repetitions with the active repetition separator.
391    #[must_use]
392    pub fn and_repeat(mut self, components: &[&'a str]) -> Self {
393        self.repeats.push(
394            components
395                .iter()
396                .copied()
397                .map(|c| (Cow::Borrowed(c), Span::default()))
398                .collect(),
399        );
400        self
401    }
402}
403
404/// Owned data element used by reader-based parsing APIs.
405///
406/// Each entry in `components` is a `(value, span)` pair, keeping the string
407/// and its byte span structurally in sync.
408///
409/// `#[non_exhaustive]`: build one with [`OwnedElement::of`].
410#[derive(Debug, Clone, PartialEq, Eq)]
411#[non_exhaustive]
412pub struct OwnedElement {
413    /// Span covering the whole element, including every repetition.
414    pub span: Span,
415    /// Components of the first repetition, in positional order.
416    pub components: OwnedComponents,
417    /// Second and subsequent repetitions (ISO 9735-4 §3.1); usually empty.
418    pub repeats: Vec<OwnedComponents>,
419}
420
421impl OwnedElement {
422    /// Build an owned data element from its component values.
423    ///
424    /// The owned counterpart of [`Element::of`].  Spans default to
425    /// [`Span::default`]; set the element span with
426    /// [`with_span`][Self::with_span] when synthesising input for diagnostics.
427    ///
428    /// # Example
429    ///
430    /// ```
431    /// use edifact_rs::{OwnedElement, OwnedSegment};
432    ///
433    /// let segment = OwnedSegment::new(
434    ///     "NAD",
435    ///     vec![
436    ///         OwnedElement::of(&["BY"]),
437    ///         OwnedElement::of(&["4000001000002", "", "9"]),
438    ///     ],
439    /// );
440    /// assert_eq!(segment.component_str(1, 2), Some("9"));
441    /// ```
442    #[must_use]
443    pub fn of<S: AsRef<str>>(components: &[S]) -> Self {
444        Self {
445            span: Span::default(),
446            components: components
447                .iter()
448                .map(|c| (c.as_ref().to_owned(), Span::default()))
449                .collect(),
450            repeats: Vec::new(),
451        }
452    }
453
454    /// Set the span covering this element.
455    #[must_use]
456    pub fn with_span(mut self, span: Span) -> Self {
457        self.span = span;
458        self
459    }
460
461    /// Append a further repetition of this data element (ISO 9735-4 §3.1).
462    ///
463    /// The owned counterpart of [`Element::and_repeat`].
464    #[must_use]
465    pub fn and_repeat<S: AsRef<str>>(mut self, components: &[S]) -> Self {
466        self.repeats.push(
467            components
468                .iter()
469                .map(|c| (c.as_ref().to_owned(), Span::default()))
470                .collect(),
471        );
472        self
473    }
474
475    #[inline]
476    /// Shift all stored spans by `delta` bytes.
477    pub fn offset(mut self, delta: usize) -> Self {
478        self.offset_in_place(delta);
479        self
480    }
481
482    /// Shift all stored spans by `delta` bytes, in place.
483    ///
484    /// Every repetition is shifted, not just the first: the reader parses each
485    /// segment from a zero-based slice and then rebases it onto the stream, so
486    /// a repetition left unshifted points into a different segment entirely.
487    #[inline]
488    pub fn offset_in_place(&mut self, delta: usize) {
489        self.span = self.span.offset(delta);
490        for (_, span) in &mut self.components {
491            *span = span.offset(delta);
492        }
493        for repeat in &mut self.repeats {
494            for (_, span) in repeat {
495                *span = span.offset(delta);
496            }
497        }
498    }
499
500    /// Number of repetitions of this data element — always at least 1.
501    #[inline]
502    pub fn repeat_count(&self) -> usize {
503        1 + self.repeats.len()
504    }
505
506    /// Components of repetition `n` (0-indexed), if it exists.
507    #[inline]
508    pub fn repetition(&self, n: usize) -> Option<&[(String, Span)]> {
509        match n {
510            0 => Some(&self.components),
511            _ => self.repeats.get(n - 1).map(|r| r.as_slice()),
512        }
513    }
514
515    /// Iterate over every repetition of this element, first one included.
516    #[inline]
517    pub fn repetitions(&self) -> impl Iterator<Item = &[(String, Span)]> {
518        std::iter::once(self.components.as_slice()).chain(self.repeats.iter().map(|r| r.as_slice()))
519    }
520}
521
522impl<'a> From<Element<'a>> for OwnedElement {
523    fn from(value: Element<'a>) -> Self {
524        fn own(components: Components<'_>) -> OwnedComponents {
525            components
526                .into_iter()
527                .map(|(c, s)| (c.into_owned(), s))
528                .collect()
529        }
530        Self {
531            span: value.span,
532            components: own(value.components),
533            repeats: value.repeats.into_iter().map(own).collect(),
534        }
535    }
536}
537
538/// Owned segment used by reader-based parsing APIs.
539///
540/// `#[non_exhaustive]`: build one with [`OwnedSegment::new`].
541#[derive(Debug, Clone, PartialEq, Eq)]
542#[non_exhaustive]
543pub struct OwnedSegment {
544    /// Segment tag, usually three uppercase letters.
545    pub tag: String,
546    /// Span covering the whole segment payload.
547    pub span: Span,
548    /// Span covering only the segment tag.
549    pub tag_span: Span,
550    /// Owned segment elements in positional order.
551    pub elements: Vec<OwnedElement>,
552}
553
554/// Zero-allocation view of an [`OwnedElement`].
555///
556/// Implements the same accessor methods as [`Element`] without constructing
557/// any intermediate `SmallVec` or `Cow` values.  Use this when you hold an
558/// `&OwnedSegment` reference and want to inspect element data without the
559/// `Vec<Element>` allocation that [`OwnedSegment::as_borrowed`] incurs.
560///
561/// Construct via `BorrowedElement::from(&owned_element)` or through
562/// [`BorrowedSegment::get_element`].
563#[derive(Debug, Clone, Copy)]
564pub struct BorrowedElement<'a>(pub(crate) &'a OwnedElement);
565
566impl<'a> From<&'a OwnedElement> for BorrowedElement<'a> {
567    #[inline]
568    fn from(elem: &'a OwnedElement) -> Self {
569        BorrowedElement(elem)
570    }
571}
572
573impl<'a> BorrowedElement<'a> {
574    /// Return the component at position `n` (0-indexed), if it exists.
575    #[inline]
576    pub fn get_component(&self, n: usize) -> Option<&'a str> {
577        self.0.components.get(n).map(|(s, _)| s.as_str())
578    }
579
580    /// Return the component at position `n`, or `""` if absent.
581    #[inline]
582    pub fn component_or_empty(&self, n: usize) -> &'a str {
583        self.0
584            .components
585            .get(n)
586            .map(|(s, _)| s.as_str())
587            .unwrap_or("")
588    }
589
590    /// Return the byte span of the component at position `n`, if it exists.
591    #[inline]
592    pub fn component_span(&self, n: usize) -> Option<Span> {
593        self.0.components.get(n).map(|(_, s)| *s)
594    }
595
596    /// The byte span covering the whole element.
597    #[inline]
598    pub fn span(&self) -> Span {
599        self.0.span
600    }
601
602    /// Number of components in this element.
603    #[inline]
604    pub fn len(&self) -> usize {
605        self.0.components.len()
606    }
607
608    /// Returns `true` if this element has no components.
609    #[inline]
610    pub fn is_empty(&self) -> bool {
611        self.0.components.is_empty()
612    }
613
614    /// Iterate over all component strings.
615    #[inline]
616    pub fn iter(&self) -> impl Iterator<Item = &'a str> {
617        self.0.components.iter().map(|(c, _)| c.as_str())
618    }
619
620    /// Number of repetitions of this data element — always at least 1.
621    #[inline]
622    pub fn repeat_count(&self) -> usize {
623        self.0.repeat_count()
624    }
625
626    /// Components of repetition `n` (0-indexed), if it exists.
627    #[inline]
628    pub fn repetition(&self, n: usize) -> Option<&'a [(String, Span)]> {
629        match n {
630            0 => Some(&self.0.components),
631            _ => self.0.repeats.get(n - 1).map(|r| r.as_slice()),
632        }
633    }
634
635    /// Iterate over every repetition of this element, first one included.
636    #[inline]
637    pub fn repetitions(&self) -> impl Iterator<Item = &'a [(String, Span)]> {
638        std::iter::once(self.0.components.as_slice())
639            .chain(self.0.repeats.iter().map(|r| r.as_slice()))
640    }
641}
642
643/// Zero-allocation view of an [`OwnedSegment`].
644///
645/// Implements the same accessor methods as [`Segment`] without constructing
646/// a `Vec<Element>`.  Use this when you hold an `&OwnedSegment` reference and
647/// want to read data without the allocations incurred by
648/// [`OwnedSegment::as_borrowed`].
649///
650/// # Construction
651///
652/// The idiomatic way to obtain a `BorrowedSegment` is via [`OwnedSegment::borrow`]
653/// or the [`From`] impl:
654///
655/// ```rust
656/// use edifact_rs::{BorrowedSegment, OwnedSegment, Span};
657///
658/// let seg = OwnedSegment::new("BGM", vec![]).with_spans(Span::new(0, 3), Span::new(0, 3));
659/// let borrowed = BorrowedSegment::from(&seg);
660/// assert_eq!(borrowed.tag(), "BGM");
661/// ```
662///
663/// The `'a` lifetime is tied to the referent — you cannot outlive the
664/// `OwnedSegment` you borrowed from.
665#[derive(Debug, Clone, Copy)]
666pub struct BorrowedSegment<'a>(pub(crate) &'a OwnedSegment);
667
668impl<'a> From<&'a OwnedSegment> for BorrowedSegment<'a> {
669    #[inline]
670    fn from(seg: &'a OwnedSegment) -> Self {
671        BorrowedSegment(seg)
672    }
673}
674
675impl<'a> BorrowedSegment<'a> {
676    /// The segment tag (e.g. `"BGM"`).
677    #[inline]
678    pub fn tag(&self) -> &'a str {
679        &self.0.tag
680    }
681
682    /// Byte span covering the whole segment.
683    #[inline]
684    pub fn span(&self) -> Span {
685        self.0.span
686    }
687
688    /// Byte span covering only the segment tag.
689    #[inline]
690    pub fn tag_span(&self) -> Span {
691        self.0.tag_span
692    }
693
694    /// Return the element at position `n` (0-indexed), if it exists.
695    #[inline]
696    pub fn get_element(&self, n: usize) -> Option<BorrowedElement<'a>> {
697        self.0.elements.get(n).map(BorrowedElement)
698    }
699
700    /// Shorthand: first component of element `n` — the most common access pattern.
701    #[inline]
702    pub fn element_str(&self, n: usize) -> Option<&'a str> {
703        self.0
704            .elements
705            .get(n)?
706            .components
707            .first()
708            .map(|(c, _)| c.as_str())
709    }
710
711    /// Get component `comp` of element `elem` (both 0-based), or `None` if absent.
712    ///
713    /// Mirrors [`OwnedSegment::component_str`].
714    #[inline]
715    pub fn component_str(&self, elem: usize, comp: usize) -> Option<&'a str> {
716        self.0
717            .elements
718            .get(elem)?
719            .components
720            .get(comp)
721            .map(|(c, _)| c.as_str())
722    }
723
724    /// Return the byte span of the element at position `n`, if it exists.
725    #[inline]
726    pub fn element_span(&self, n: usize) -> Option<Span> {
727        Some(self.0.elements.get(n)?.span)
728    }
729
730    /// Iterate over all elements as zero-allocation views.
731    #[inline]
732    pub fn elements(&self) -> impl Iterator<Item = BorrowedElement<'a>> {
733        self.0.elements.iter().map(BorrowedElement)
734    }
735
736    // ── code-addressed access ─────────────────────────────────────────────────
737
738    /// Read the value at an already-resolved [`ElementPath`].
739    #[inline]
740    pub fn value_at(&self, path: ElementPath) -> Option<&'a str> {
741        self.0
742            .elements
743            .get(path.element)?
744            .components
745            .get(path.component_index())
746            .map(|(c, _)| c.as_str())
747    }
748
749    /// Byte span of the value at an already-resolved [`ElementPath`].
750    #[inline]
751    pub fn span_at(&self, path: ElementPath) -> Option<Span> {
752        let element = self.0.elements.get(path.element)?;
753        match path.component {
754            Some(c) => element.components.get(c).map(|(_, s)| *s),
755            None => Some(element.span),
756        }
757    }
758
759    /// Read a value by its UN/EDIFACT data element identifier.
760    ///
761    /// Zero-allocation counterpart of [`Segment::value_by_code`].
762    ///
763    /// # Errors
764    ///
765    /// As [`Segment::value_by_code`].
766    pub fn value_by_code<L: SegmentLayout + ?Sized>(
767        &self,
768        layout: &L,
769        data_element: &str,
770    ) -> Result<Option<&'a str>, EdifactError> {
771        check_layout_tag(layout, &self.0.tag)?;
772        Ok(self.value_at(layout.resolve_code(data_element)?))
773    }
774
775    /// Byte span of a value addressed by its UN/EDIFACT data element identifier.
776    ///
777    /// # Errors
778    ///
779    /// As [`Segment::value_by_code`].
780    pub fn span_by_code<L: SegmentLayout + ?Sized>(
781        &self,
782        layout: &L,
783        data_element: &str,
784    ) -> Result<Option<Span>, EdifactError> {
785        check_layout_tag(layout, &self.0.tag)?;
786        Ok(self.span_at(layout.resolve_code(data_element)?))
787    }
788
789    /// Return the whole element addressed by a data element identifier.
790    ///
791    /// When the identifier names a component inside a composite, the enclosing
792    /// composite element is returned.
793    ///
794    /// # Errors
795    ///
796    /// As [`Segment::value_by_code`].
797    pub fn element_by_code<L: SegmentLayout + ?Sized>(
798        &self,
799        layout: &L,
800        data_element: &str,
801    ) -> Result<Option<BorrowedElement<'a>>, EdifactError> {
802        check_layout_tag(layout, &self.0.tag)?;
803        let path = layout.resolve_code(data_element)?;
804        Ok(self.0.elements.get(path.element).map(BorrowedElement))
805    }
806}
807
808impl OwnedSegment {
809    /// Build an owned segment from a tag and its data elements.
810    ///
811    /// The owned counterpart of [`Segment::new`].  Spans default to
812    /// [`Span::default`], which is what a segment synthesised from a non-EDIFACT
813    /// source should carry — there is no input to point at.  Use
814    /// [`with_spans`][Self::with_spans] when there is.
815    ///
816    /// # Example
817    ///
818    /// ```
819    /// use edifact_rs::{OwnedElement, OwnedSegment, segments_to_bytes_owned};
820    ///
821    /// let segment = OwnedSegment::new("BGM", vec![OwnedElement::of(&["220"])]);
822    /// assert_eq!(segments_to_bytes_owned(&[segment])?, b"BGM+220'".to_vec());
823    /// # Ok::<(), edifact_rs::EdifactError>(())
824    /// ```
825    #[must_use]
826    pub fn new(tag: impl Into<String>, elements: Vec<OwnedElement>) -> Self {
827        Self {
828            tag: tag.into(),
829            span: Span::default(),
830            tag_span: Span::default(),
831            elements,
832        }
833    }
834
835    /// Set the segment and tag spans.
836    #[must_use]
837    pub fn with_spans(mut self, span: Span, tag_span: Span) -> Self {
838        self.span = span;
839        self.tag_span = tag_span;
840        self
841    }
842
843    /// Get the first component of element `n`, or `None` if absent.
844    ///
845    /// This is the zero-allocation equivalent of `as_borrowed().element_str(n)`.
846    /// Used internally by [`crate::find_segment_owned`] and the derived
847    /// [`crate::EdifactDeserialize::edifact_deserialize_owned`] implementations.
848    #[inline]
849    pub fn element_str(&self, n: usize) -> Option<&str> {
850        self.elements
851            .get(n)?
852            .components
853            .first()
854            .map(|(s, _)| s.as_str())
855    }
856
857    /// Get component `comp` of element `elem`, or `None` if absent.
858    ///
859    /// Zero-allocation equivalent of `as_borrowed().get_element(elem)?.get_component(comp)`.
860    #[inline]
861    pub fn component_str(&self, elem: usize, comp: usize) -> Option<&str> {
862        self.elements
863            .get(elem)?
864            .components
865            .get(comp)
866            .map(|(s, _)| s.as_str())
867    }
868
869    #[inline]
870    /// Shift all stored spans by `delta` bytes.
871    ///
872    /// Delegates to [`OwnedElement::offset_in_place`] rather than walking the
873    /// components inline: an inline walk shifted `components` but silently left
874    /// `repeats` at their segment-relative offsets, so on the reader path every
875    /// repetition after the first pointed at the wrong bytes.
876    pub fn offset(mut self, delta: usize) -> Self {
877        self.span = self.span.offset(delta);
878        self.tag_span = self.tag_span.offset(delta);
879        for element in &mut self.elements {
880            element.offset_in_place(delta);
881        }
882        self
883    }
884
885    #[inline]
886    /// View this owned segment as a borrowed [`Segment`].
887    ///
888    /// **Performance note**: allocates a `Vec<Element<'_>>` on every call.
889    /// When only individual field access is needed, prefer
890    /// [`OwnedSegment::borrow`] → [`BorrowedSegment`] which is O(1).
891    /// `as_borrowed` remains necessary when the callee requires `&[Segment<'_>]`.
892    pub fn as_borrowed(&self) -> Segment<'_> {
893        Segment {
894            tag: self.tag.as_str(),
895            span: self.span,
896            tag_span: self.tag_span,
897            elements: self
898                .elements
899                .iter()
900                .map(|elem| {
901                    fn borrow(components: &OwnedComponents) -> Components<'_> {
902                        components
903                            .iter()
904                            .map(|(c, s)| (Cow::Borrowed(c.as_str()), *s))
905                            .collect()
906                    }
907                    Element {
908                        span: elem.span,
909                        components: borrow(&elem.components),
910                        repeats: elem.repeats.iter().map(borrow).collect(),
911                    }
912                })
913                .collect(),
914        }
915    }
916
917    /// Return a zero-allocation view of this segment.
918    ///
919    /// Unlike [`as_borrowed`][OwnedSegment::as_borrowed], this is `O(1)` and
920    /// performs no heap allocation.  The view cannot be passed to APIs that
921    /// require `&[Segment<'_>]`; use [`as_borrowed`][OwnedSegment::as_borrowed]
922    /// for those call sites.
923    #[inline]
924    pub fn borrow(&self) -> BorrowedSegment<'_> {
925        BorrowedSegment(self)
926    }
927
928    // ── code-addressed access ─────────────────────────────────────────────────
929
930    /// Read the value at an already-resolved [`ElementPath`].
931    #[inline]
932    pub fn value_at(&self, path: ElementPath) -> Option<&str> {
933        self.elements
934            .get(path.element)?
935            .components
936            .get(path.component_index())
937            .map(|(s, _)| s.as_str())
938    }
939
940    /// Byte span of the value at an already-resolved [`ElementPath`].
941    #[inline]
942    pub fn span_at(&self, path: ElementPath) -> Option<Span> {
943        let element = self.elements.get(path.element)?;
944        match path.component {
945            Some(c) => element.components.get(c).map(|(_, s)| *s),
946            None => Some(element.span),
947        }
948    }
949
950    /// Read a value by its UN/EDIFACT data element identifier.
951    ///
952    /// Owned-storage counterpart of [`Segment::value_by_code`]; allocates nothing.
953    ///
954    /// # Errors
955    ///
956    /// As [`Segment::value_by_code`].
957    pub fn value_by_code<L: SegmentLayout + ?Sized>(
958        &self,
959        layout: &L,
960        data_element: &str,
961    ) -> Result<Option<&str>, EdifactError> {
962        check_layout_tag(layout, &self.tag)?;
963        Ok(self.value_at(layout.resolve_code(data_element)?))
964    }
965
966    /// Byte span of a value addressed by its UN/EDIFACT data element identifier.
967    ///
968    /// # Errors
969    ///
970    /// As [`Segment::value_by_code`].
971    pub fn span_by_code<L: SegmentLayout + ?Sized>(
972        &self,
973        layout: &L,
974        data_element: &str,
975    ) -> Result<Option<Span>, EdifactError> {
976        check_layout_tag(layout, &self.tag)?;
977        Ok(self.span_at(layout.resolve_code(data_element)?))
978    }
979
980    /// Return the whole [`OwnedElement`] addressed by a data element identifier.
981    ///
982    /// When the identifier names a component inside a composite, the enclosing
983    /// composite element is returned.
984    ///
985    /// # Errors
986    ///
987    /// As [`Segment::value_by_code`].
988    pub fn element_by_code<L: SegmentLayout + ?Sized>(
989        &self,
990        layout: &L,
991        data_element: &str,
992    ) -> Result<Option<&OwnedElement>, EdifactError> {
993        check_layout_tag(layout, &self.tag)?;
994        let path = layout.resolve_code(data_element)?;
995        Ok(self.elements.get(path.element))
996    }
997}
998
999impl<'a> From<Segment<'a>> for OwnedSegment {
1000    fn from(value: Segment<'a>) -> Self {
1001        Self {
1002            tag: value.tag.to_string(),
1003            span: value.span,
1004            tag_span: value.tag_span,
1005            elements: value.elements.into_iter().map(OwnedElement::from).collect(),
1006        }
1007    }
1008}