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}