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}