Skip to main content

edifact_rs/
model.rs

1//! The EDIFACT data model: [`Span`], [`Element`], and [`Segment`].
2//!
3//! # One segment type, borrowed or owned
4//!
5//! [`Segment<'a>`] holds its text as [`Cow<'a, str>`], so the *same* type covers
6//! both parsing modes:
7//!
8//! - [`from_bytes`][crate::from_bytes] borrows straight out of the input and
9//!   yields `Segment<'input>` — no allocation for segment data.
10//! - [`from_reader`][crate::from_reader] cannot borrow from a stream, so it
11//!   yields `Segment<'static>`, aliased as [`OwnedSegment`].
12//!
13//! `Segment` is covariant in `'a`, so a `&[OwnedSegment]` is accepted anywhere a
14//! `&[Segment<'_>]` is expected: every API takes one shape and serves both.
15//!
16//! ```
17//! use edifact_rs::{OwnedSegment, Segment};
18//!
19//! fn count_bgm(segments: &[Segment<'_>]) -> usize {
20//!     segments.iter().filter(|s| s.tag == "BGM").count()
21//! }
22//!
23//! let borrowed: Vec<Segment<'_>> =
24//!     edifact_rs::from_bytes(b"BGM+220'").collect::<Result<_, _>>()?;
25//! let owned: Vec<OwnedSegment> =
26//!     edifact_rs::from_reader(std::io::Cursor::new(b"BGM+220'")).collect::<Result<_, _>>()?;
27//!
28//! assert_eq!(count_bgm(&borrowed), 1);
29//! assert_eq!(count_bgm(&owned), 1); // the same function, no conversion
30//! # Ok::<(), edifact_rs::EdifactError>(())
31//! ```
32
33use crate::directory_validator::{ElementPath, SegmentLayout};
34use crate::error::EdifactError;
35use smallvec::SmallVec;
36use std::borrow::Cow;
37use std::str::FromStr;
38
39/// Reject a layout whose tag does not describe `segment_tag`.
40///
41/// Resolving `"3055"` against the wrong definition would silently address a
42/// different element — the exact failure mode code-addressed access exists to
43/// eliminate — so the mismatch is an error rather than a lookup miss.
44#[inline]
45fn check_layout_tag<L: SegmentLayout + ?Sized>(
46    layout: &L,
47    segment_tag: &str,
48) -> Result<(), EdifactError> {
49    if layout.layout_tag() != segment_tag {
50        return Err(EdifactError::SegmentLayoutMismatch {
51            expected: layout.layout_tag().to_owned(),
52            actual: segment_tag.to_owned(),
53        });
54    }
55    Ok(())
56}
57
58/// A half-open byte span within an EDIFACT payload.
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
60#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
61pub struct Span {
62    /// Start byte offset (inclusive).
63    pub start: usize,
64    /// End byte offset (exclusive).
65    pub end: usize,
66}
67
68impl Span {
69    #[inline]
70    /// Construct a span from inclusive start and exclusive end offsets.
71    pub const fn new(start: usize, end: usize) -> Self {
72        Self { start, end }
73    }
74
75    #[inline]
76    /// Shift the span by `delta` bytes.
77    ///
78    /// Uses saturating addition to avoid integer overflow on malformed input.
79    pub const fn offset(self, delta: usize) -> Self {
80        Self {
81            start: self.start.saturating_add(delta),
82            end: self.end.saturating_add(delta),
83        }
84    }
85
86    /// Length of the span in bytes.
87    ///
88    /// In debug builds, asserts `end >= start` (inverted spans are a bug).
89    /// In release builds, returns 0 for inverted spans rather than panicking,
90    /// so a single corrupt span does not abort an entire validation run.
91    #[inline]
92    pub fn len(self) -> usize {
93        debug_assert!(
94            self.end >= self.start,
95            "Span::len: end ({}) < start ({})",
96            self.end,
97            self.start
98        );
99        self.end.saturating_sub(self.start)
100    }
101
102    /// Returns `true` if the span covers zero bytes.
103    #[inline]
104    pub const fn is_empty(self) -> bool {
105        self.start == self.end
106    }
107}
108
109impl std::fmt::Display for Span {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        write!(f, "{}..{}", self.start, self.end)
112    }
113}
114
115/// Components of one occurrence of a data element, each paired with its span.
116pub type Components<'a> = SmallVec<[(Cow<'a, str>, Span); 4]>;
117
118/// A [`Segment`] that owns all of its text.
119///
120/// Just `Segment<'static>` — the shape [`from_reader`][crate::from_reader]
121/// produces, because a stream has no buffer to borrow from. It is accepted
122/// anywhere a `&[Segment<'_>]` is wanted.
123pub type OwnedSegment = Segment<'static>;
124
125/// An [`Element`] that owns all of its text. See [`OwnedSegment`].
126pub type OwnedElement = Element<'static>;
127
128/// A data element, which may have one or more component values.
129///
130/// `#[non_exhaustive]`: build one with [`Element::of`] (plus
131/// [`and_repeat`][Element::and_repeat] / [`with_span`][Element::with_span])
132/// rather than a struct literal.
133///
134/// Uses [`SmallVec`] with an inline capacity of 4 to avoid heap allocation
135/// for the common case (≤ 4 components). Each entry is a `(value, span)` pair,
136/// guaranteeing that the component string and its byte span stay in sync.
137/// A value that contained a release-character sequence is stored as
138/// [`Cow::Owned`]; everything else borrows from the input.
139///
140/// # Repetition (ISO 9735-1 §8.6)
141///
142/// [`components`][Self::components] holds the **first** occurrence, which is the
143/// only one for every interchange that does not declare a repetition separator —
144/// that is, virtually all of them. Further occurrences land in
145/// [`repeats`][Self::repeats]; read them together with
146/// [`repetitions`][Self::repetitions].
147#[derive(Debug, Clone, PartialEq, Eq)]
148#[non_exhaustive]
149pub struct Element<'a> {
150    /// Span covering the whole element, including every occurrence.
151    pub span: Span,
152    /// Components of the first occurrence, in positional order.
153    pub components: Components<'a>,
154    /// Second and subsequent occurrences of this data element.
155    ///
156    /// Empty — and therefore unallocated — unless the interchange declares a
157    /// repetition separator and the element actually repeats.
158    pub repeats: Vec<Components<'a>>,
159}
160
161impl<'a> Element<'a> {
162    /// Build a data element from its component values.
163    ///
164    /// Accepts anything that converts to `Cow<'a, str>`, so both string literals
165    /// and owned `String`s work:
166    ///
167    /// ```
168    /// use edifact_rs::{Element, OwnedElement};
169    ///
170    /// let borrowed = Element::of(&["4000001000002", "", "9"]);
171    /// let owned: OwnedElement = Element::of(&[String::from("BY")]);
172    ///
173    /// assert_eq!(borrowed.get_component(2), Some("9"));
174    /// assert_eq!(owned.get_component(0), Some("BY"));
175    /// ```
176    pub fn of<S>(components: &[S]) -> Self
177    where
178        S: Into<Cow<'a, str>> + Clone,
179    {
180        Self {
181            span: Span::default(),
182            components: components
183                .iter()
184                .map(|c| (c.clone().into(), Span::default()))
185                .collect(),
186            repeats: Vec::new(),
187        }
188    }
189
190    /// Return the component at position `n` (0-indexed) of the first occurrence.
191    #[inline]
192    pub fn get_component(&self, n: usize) -> Option<&str> {
193        self.components.get(n).map(|(c, _)| c.as_ref())
194    }
195
196    /// Return the component at position `n`, or `""` if absent.
197    #[inline]
198    pub fn component_or_empty(&self, n: usize) -> &str {
199        self.get_component(n).unwrap_or("")
200    }
201
202    /// Return the byte span of the component at position `n`, if it exists.
203    #[inline]
204    pub fn component_span(&self, n: usize) -> Option<Span> {
205        self.components.get(n).map(|(_, s)| *s)
206    }
207
208    /// Iterate over the component values of the first occurrence.
209    #[inline]
210    pub fn components(&self) -> impl Iterator<Item = &str> {
211        self.components.iter().map(|(c, _)| c.as_ref())
212    }
213
214    /// Number of occurrences of this data element — always at least 1.
215    #[inline]
216    pub fn repeat_count(&self) -> usize {
217        1 + self.repeats.len()
218    }
219
220    /// Components of occurrence `n` (0-indexed), if it exists.
221    #[inline]
222    pub fn repetition(&self, n: usize) -> Option<&[(Cow<'a, str>, Span)]> {
223        match n {
224            0 => Some(&self.components),
225            _ => self.repeats.get(n - 1).map(|r| r.as_slice()),
226        }
227    }
228
229    /// Iterate over every occurrence of this element, the first one included.
230    ///
231    /// # Example
232    ///
233    /// ```
234    /// // `UNA` byte 7 declares `*` as the repetition separator.
235    /// let segments: Vec<_> = edifact_rs::from_bytes(b"UNA:+.?*'RFF+ON:1*ON:2'")
236    ///     .collect::<Result<Vec<_>, _>>()?;
237    /// let rff = segments[0].get_element(0).unwrap();
238    ///
239    /// let refs: Vec<&str> = rff
240    ///     .repetitions()
241    ///     .map(|components| components[1].0.as_ref())
242    ///     .collect();
243    /// assert_eq!(refs, ["1", "2"]);
244    /// # Ok::<(), edifact_rs::EdifactError>(())
245    /// ```
246    #[inline]
247    pub fn repetitions(&self) -> impl Iterator<Item = &[(Cow<'a, str>, Span)]> {
248        std::iter::once(self.components.as_slice()).chain(self.repeats.iter().map(|r| r.as_slice()))
249    }
250
251    /// Read component `n` from every occurrence of this element (ISO 9735-1 §8.6).
252    ///
253    /// One item per occurrence, using `""` where an occurrence omits the
254    /// component: §8.7.3 makes occurrence position significant, so dropping the
255    /// empty ones would shift every later value into the wrong slot.
256    #[inline]
257    pub fn repeated_component(&self, n: usize) -> impl Iterator<Item = &str> {
258        self.repetitions()
259            .map(move |occurrence| occurrence.get(n).map_or("", |(c, _)| c.as_ref()))
260    }
261
262    /// Set the span covering this element.
263    ///
264    /// Parsed elements carry real spans; hand-built ones default to
265    /// [`Span::default`] and only need this when the caller is synthesising
266    /// input for diagnostics.
267    #[must_use]
268    pub fn with_span(mut self, span: Span) -> Self {
269        self.span = span;
270        self
271    }
272
273    /// Append a further occurrence of this data element (ISO 9735-1 §8.6).
274    ///
275    /// Useful when building segments for
276    /// [`Writer::write_segment`][crate::Writer::write_segment]; the writer joins
277    /// occurrences with the active repetition separator.
278    #[must_use]
279    pub fn and_repeat<S>(mut self, components: &[S]) -> Self
280    where
281        S: Into<Cow<'a, str>> + Clone,
282    {
283        self.repeats.push(
284            components
285                .iter()
286                .map(|c| (c.clone().into(), Span::default()))
287                .collect(),
288        );
289        self
290    }
291
292    /// Shift every stored span by `delta` bytes, in place.
293    ///
294    /// Every occurrence is shifted, not just the first: the reader parses each
295    /// segment from a zero-based slice and then rebases it onto the stream, so
296    /// an occurrence left unshifted points into a different segment entirely.
297    #[inline]
298    pub fn offset_in_place(&mut self, delta: usize) {
299        self.span = self.span.offset(delta);
300        for (_, span) in &mut self.components {
301            *span = span.offset(delta);
302        }
303        for repeat in &mut self.repeats {
304            for (_, span) in repeat {
305                *span = span.offset(delta);
306            }
307        }
308    }
309
310    /// Detach this element from the input buffer, cloning any borrowed text.
311    #[must_use]
312    pub fn into_owned(self) -> OwnedElement {
313        fn own(components: Components<'_>) -> Components<'static> {
314            components
315                .into_iter()
316                .map(|(c, s)| (Cow::Owned(c.into_owned()), s))
317                .collect()
318        }
319        Element {
320            span: self.span,
321            components: own(self.components),
322            repeats: self.repeats.into_iter().map(own).collect(),
323        }
324    }
325}
326
327/// A single EDIFACT segment.
328///
329/// Borrows its text from the parsed input where it can, and owns it where it
330/// cannot: [`from_bytes`][crate::from_bytes] yields `Segment<'input>`, while
331/// [`from_reader`][crate::from_reader] yields `Segment<'static>` — aliased as
332/// [`OwnedSegment`]. Covariance in `'a` means one is accepted wherever the other
333/// is, so every API in this crate takes a single shape.
334///
335/// `#[non_exhaustive]`: build one with [`Segment::new`] rather than a struct
336/// literal, so a future field stays additive. The fields stay public, so reading
337/// and `..` destructuring are unaffected.
338#[derive(Debug, Clone, PartialEq, Eq)]
339#[non_exhaustive]
340pub struct Segment<'a> {
341    /// Segment tag — three ASCII uppercase letters for anything this crate parses.
342    pub tag: Cow<'a, str>,
343    /// Span covering the whole segment payload.
344    pub span: Span,
345    /// Span covering only the segment tag.
346    pub tag_span: Span,
347    /// Segment elements in positional order.
348    pub elements: Vec<Element<'a>>,
349}
350
351impl<'a> Segment<'a> {
352    /// Build a segment from a tag and its data elements.
353    ///
354    /// Spans default to [`Span::default`], which is what a segment synthesised
355    /// from a non-EDIFACT source should carry — there is no input to point at.
356    /// Use [`with_spans`][Self::with_spans] when there is.
357    ///
358    /// The tag is not checked here; it is checked when the segment is written.
359    /// A tag is emitted verbatim, so one the parser would reject is refused as
360    /// [`EdifactError::InvalidSegmentTag`] by
361    /// [`Writer::write_segment`][crate::Writer::write_segment] rather than
362    /// written out as bytes that do not read back.
363    ///
364    /// # Example
365    ///
366    /// ```
367    /// use edifact_rs::{Element, Segment, segments_to_bytes};
368    ///
369    /// let segment = Segment::new("BGM", vec![Element::of(&["220"])]);
370    /// assert_eq!(segments_to_bytes(&[segment])?, b"BGM+220'".to_vec());
371    /// # Ok::<(), edifact_rs::EdifactError>(())
372    /// ```
373    #[inline]
374    pub fn new(tag: impl Into<Cow<'a, str>>, elements: Vec<Element<'a>>) -> Self {
375        Self {
376            tag: tag.into(),
377            span: Span::default(),
378            tag_span: Span::default(),
379            elements,
380        }
381    }
382
383    /// Set the segment and tag spans.
384    #[must_use]
385    pub fn with_spans(mut self, span: Span, tag_span: Span) -> Self {
386        self.span = span;
387        self.tag_span = tag_span;
388        self
389    }
390
391    /// The segment tag as a plain `&str`.
392    ///
393    /// `segment.tag` compares directly against a string literal
394    /// (`segment.tag == "BGM"`); this is for the places that need a `&str`, such
395    /// as a `match`.
396    #[inline]
397    pub fn tag(&self) -> &str {
398        self.tag.as_ref()
399    }
400
401    /// Return the element at position `n` (0-indexed), if it exists.
402    #[inline]
403    pub fn get_element(&self, n: usize) -> Option<&Element<'a>> {
404        self.elements.get(n)
405    }
406
407    /// Shorthand: component 0 of element `n` — the most common access pattern.
408    #[inline]
409    pub fn element_str(&self, n: usize) -> Option<&str> {
410        self.elements.get(n)?.get_component(0)
411    }
412
413    /// Get component `comp` of element `elem` (both 0-based), or `None` if absent.
414    #[inline]
415    pub fn component_str(&self, elem: usize, comp: usize) -> Option<&str> {
416        self.elements.get(elem)?.get_component(comp)
417    }
418
419    /// Return the byte span of the element at position `n`, if it exists.
420    #[inline]
421    pub fn element_span(&self, n: usize) -> Option<Span> {
422        Some(self.elements.get(n)?.span)
423    }
424
425    /// Read component `component` from every occurrence of element `element`.
426    ///
427    /// Yields nothing when the element is absent. See
428    /// [`Element::repeated_component`].
429    ///
430    /// # Example
431    ///
432    /// ```
433    /// // `UNA` position 050 declares `*` as the repetition separator.
434    /// let segments: Vec<_> = edifact_rs::from_bytes(b"UNA:+.?*'RFF+ON:1*ON:2*ON:3'")
435    ///     .collect::<Result<Vec<_>, _>>()?;
436    ///
437    /// let references: Vec<&str> = segments[0].repeated_component(0, 1).collect();
438    /// assert_eq!(references, ["1", "2", "3"]);
439    /// # Ok::<(), edifact_rs::EdifactError>(())
440    /// ```
441    #[inline]
442    pub fn repeated_component(
443        &self,
444        element: usize,
445        component: usize,
446    ) -> impl Iterator<Item = &str> {
447        self.elements
448            .get(element)
449            .into_iter()
450            .flat_map(move |elem| elem.repeated_component(component))
451    }
452
453    /// Shift every stored span by `delta` bytes.
454    #[inline]
455    #[must_use]
456    pub fn offset(mut self, delta: usize) -> Self {
457        self.span = self.span.offset(delta);
458        self.tag_span = self.tag_span.offset(delta);
459        for element in &mut self.elements {
460            element.offset_in_place(delta);
461        }
462        self
463    }
464
465    /// Detach this segment from the input buffer, cloning any borrowed text.
466    ///
467    /// Use it to keep a segment alive past the buffer it was parsed from.
468    #[must_use]
469    pub fn into_owned(self) -> OwnedSegment {
470        Segment {
471            tag: Cow::Owned(self.tag.into_owned()),
472            span: self.span,
473            tag_span: self.tag_span,
474            elements: self.elements.into_iter().map(Element::into_owned).collect(),
475        }
476    }
477
478    // ── checked field access ──────────────────────────────────────────────────
479
480    /// Read element `idx`, treating an empty value as absent.
481    ///
482    /// EDIFACT lets an element be syntactically present but empty (`SEG++'`).
483    /// A mandatory data element must carry a value, so this reports
484    /// [`EdifactError::MissingRequiredElement`] for both cases.
485    ///
486    /// # Errors
487    ///
488    /// [`EdifactError::MissingRequiredElement`] when the element is absent or empty.
489    pub fn required_element(&self, idx: usize) -> Result<&str, EdifactError> {
490        self.optional_element(idx)
491            .ok_or_else(|| EdifactError::MissingRequiredElement {
492                tag: self.tag.clone().into_owned(),
493                element_index: idx,
494            })
495    }
496
497    /// Read element `idx`, treating an empty value as absent.
498    #[inline]
499    pub fn optional_element(&self, idx: usize) -> Option<&str> {
500        self.element_str(idx).filter(|s| !s.is_empty())
501    }
502
503    /// Read component `comp` of element `elem`, treating an empty value as absent.
504    ///
505    /// # Errors
506    ///
507    /// [`EdifactError::MissingRequiredElement`] when the element itself is
508    /// absent, and [`EdifactError::MissingRequiredComponent`] when the element is
509    /// present but the component is absent or empty. The distinction matters:
510    /// the first says the segment is too short, the second that one composite is
511    /// incomplete.
512    pub fn required_component(&self, elem: usize, comp: usize) -> Result<&str, EdifactError> {
513        let element =
514            self.elements
515                .get(elem)
516                .ok_or_else(|| EdifactError::MissingRequiredElement {
517                    tag: self.tag.clone().into_owned(),
518                    element_index: elem,
519                })?;
520        element
521            .get_component(comp)
522            .filter(|s| !s.is_empty())
523            .ok_or_else(|| EdifactError::MissingRequiredComponent {
524                tag: self.tag.clone().into_owned(),
525                element_index: elem,
526                component_index: comp,
527            })
528    }
529
530    /// Read component `comp` of element `elem`, treating an empty value as absent.
531    #[inline]
532    pub fn optional_component(&self, elem: usize, comp: usize) -> Option<&str> {
533        self.component_str(elem, comp).filter(|s| !s.is_empty())
534    }
535
536    /// Read element `idx` and parse it into `T`.
537    ///
538    /// # Errors
539    ///
540    /// As [`required_element`][Self::required_element], plus
541    /// [`EdifactError::InvalidText`] when the value does not parse.
542    pub fn parsed_element<T: FromStr>(&self, idx: usize) -> Result<T, EdifactError> {
543        let raw = self.required_element(idx)?;
544        raw.parse::<T>().map_err(|_| EdifactError::InvalidText {
545            offset: self
546                .element_span(idx)
547                .map(|s| s.start)
548                .unwrap_or(self.span.start),
549        })
550    }
551
552    // ── code-addressed access ─────────────────────────────────────────────────
553
554    /// Read the value at an already-resolved [`ElementPath`].
555    ///
556    /// Use this when the same path is reused across many segments — resolve once
557    /// with [`SegmentLayout::resolve_code`], then read without repeating the
558    /// lookup.
559    #[inline]
560    pub fn value_at(&self, path: ElementPath) -> Option<&str> {
561        self.elements
562            .get(path.element)?
563            .get_component(path.component_index())
564    }
565
566    /// Byte span of the value at an already-resolved [`ElementPath`].
567    #[inline]
568    pub fn span_at(&self, path: ElementPath) -> Option<Span> {
569        let element = self.elements.get(path.element)?;
570        match path.component {
571            Some(c) => element.component_span(c),
572            None => Some(element.span),
573        }
574    }
575
576    /// Read a value by its UN/EDIFACT data element identifier.
577    ///
578    /// Positional access (`seg.element_str(4)`) fails silently when the index is
579    /// wrong: it reads a different, usually still-plausible value. Code-addressed
580    /// access cannot — a stale or mistyped identifier is a
581    /// [`EdifactError::UnknownDataElement`], checked against the directory.
582    ///
583    /// `Ok(None)` means the identifier is valid for this segment but the value is
584    /// absent from *this* instance, which is the normal state for a conditional
585    /// element.
586    ///
587    /// # Performance
588    ///
589    /// Each call scans the layout for the identifier. That is a handful of short
590    /// string comparisons and fine for one-off reads, but when pulling the same
591    /// identifier out of many segments, resolve once with
592    /// [`SegmentLayout::resolve_code`] and read with [`value_at`](Self::value_at).
593    ///
594    /// # Example
595    ///
596    /// ```rust
597    /// use edifact_rs::{ComponentRef, ElementRef, SegmentDefinition, Status};
598    ///
599    /// static C507: &[ComponentRef] = &[
600    ///     ComponentRef::new(1, "2005", Status::Mandatory),
601    ///     ComponentRef::new(2, "2380", Status::Conditional),
602    ///     ComponentRef::new(3, "2379", Status::Conditional),
603    /// ];
604    /// static DTM_ELEMENTS: &[ElementRef] =
605    ///     &[ElementRef::composite(1, "C507", Status::Mandatory, 1, C507)];
606    /// static DTM: SegmentDefinition =
607    ///     SegmentDefinition::new("DTM", "Date/time/period", DTM_ELEMENTS);
608    ///
609    /// let segments: Vec<_> = edifact_rs::from_bytes(b"DTM+137:20260101:102'")
610    ///     .collect::<Result<Vec<_>, _>>()?;
611    /// let dtm = &segments[0];
612    ///
613    /// assert_eq!(dtm.value_by_code(&DTM, "2380")?, Some("20260101"));
614    /// // A data element that this segment does not define is a hard error,
615    /// // not a wrong-but-quiet read.
616    /// assert!(dtm.value_by_code(&DTM, "3055").is_err());
617    /// # Ok::<(), edifact_rs::EdifactError>(())
618    /// ```
619    ///
620    /// # Errors
621    ///
622    /// Returns [`EdifactError::SegmentLayoutMismatch`] when `layout` describes a
623    /// different segment tag, [`EdifactError::UnknownDataElement`] when the
624    /// identifier is not in the definition, and
625    /// [`EdifactError::AmbiguousDataElement`] when it appears more than once.
626    pub fn value_by_code<L: SegmentLayout + ?Sized>(
627        &self,
628        layout: &L,
629        data_element: &str,
630    ) -> Result<Option<&str>, EdifactError> {
631        check_layout_tag(layout, &self.tag)?;
632        Ok(self.value_at(layout.resolve_code(data_element)?))
633    }
634
635    /// Byte span of a value addressed by its UN/EDIFACT data element identifier.
636    ///
637    /// Use this to attach a precise [`Span`] to a
638    /// [`ValidationIssue`][crate::ValidationIssue] without hand-counting indices.
639    ///
640    /// # Errors
641    ///
642    /// As [`value_by_code`][Self::value_by_code].
643    pub fn span_by_code<L: SegmentLayout + ?Sized>(
644        &self,
645        layout: &L,
646        data_element: &str,
647    ) -> Result<Option<Span>, EdifactError> {
648        check_layout_tag(layout, &self.tag)?;
649        Ok(self.span_at(layout.resolve_code(data_element)?))
650    }
651
652    /// Return the whole [`Element`] addressed by a data element identifier.
653    ///
654    /// When the identifier names a component inside a composite, the enclosing
655    /// composite element is returned.
656    ///
657    /// # Errors
658    ///
659    /// As [`value_by_code`][Self::value_by_code].
660    pub fn element_by_code<L: SegmentLayout + ?Sized>(
661        &self,
662        layout: &L,
663        data_element: &str,
664    ) -> Result<Option<&Element<'a>>, EdifactError> {
665        check_layout_tag(layout, &self.tag)?;
666        let path = layout.resolve_code(data_element)?;
667        Ok(self.elements.get(path.element))
668    }
669}
670
671#[cfg(test)]
672mod tests {
673    use super::*;
674
675    #[test]
676    fn owned_segments_pass_where_borrowed_ones_are_expected() {
677        fn tags<'s>(segments: &'s [Segment<'_>]) -> Vec<&'s str> {
678            segments.iter().map(Segment::tag).collect()
679        }
680
681        let owned: Vec<OwnedSegment> =
682            crate::from_reader(std::io::Cursor::new(b"BGM+220'UNT+2+1'"))
683                .collect::<Result<_, _>>()
684                .expect("reader parse");
685        // The point of the unified model: no conversion, no `_owned` twin.
686        assert_eq!(tags(&owned), ["BGM", "UNT"]);
687    }
688
689    #[test]
690    fn into_owned_outlives_the_input_buffer() {
691        let segment = {
692            let input = b"BGM+220+PO-4711'".to_vec();
693            let parsed: Vec<Segment<'_>> = crate::from_bytes(&input)
694                .collect::<Result<_, _>>()
695                .expect("parse");
696            parsed.into_iter().next().unwrap().into_owned()
697        };
698        assert_eq!(segment.element_str(1), Some("PO-4711"));
699    }
700
701    #[test]
702    fn required_accessors_treat_empty_as_absent() {
703        let segments: Vec<Segment<'_>> = crate::from_bytes(b"NAD++::'")
704            .collect::<Result<_, _>>()
705            .expect("parse");
706        let nad = &segments[0];
707        assert!(matches!(
708            nad.required_element(0),
709            Err(EdifactError::MissingRequiredElement { .. })
710        ));
711        // Element 1 exists but its components are empty …
712        assert!(matches!(
713            nad.required_component(1, 0),
714            Err(EdifactError::MissingRequiredComponent { .. })
715        ));
716        // … while element 5 does not exist at all.
717        assert!(matches!(
718            nad.required_component(5, 0),
719            Err(EdifactError::MissingRequiredElement { .. })
720        ));
721    }
722}