use crate::directory_validator::{ElementPath, SegmentLayout};
use crate::error::EdifactError;
use smallvec::SmallVec;
use std::borrow::Cow;
use std::str::FromStr;
#[inline]
fn check_layout_tag<L: SegmentLayout + ?Sized>(
layout: &L,
segment_tag: &str,
) -> Result<(), EdifactError> {
if layout.layout_tag() != segment_tag {
return Err(EdifactError::SegmentLayoutMismatch {
expected: layout.layout_tag().to_owned(),
actual: segment_tag.to_owned(),
});
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
#[inline]
pub const fn new(start: usize, end: usize) -> Self {
Self { start, end }
}
#[inline]
pub const fn offset(self, delta: usize) -> Self {
Self {
start: self.start.saturating_add(delta),
end: self.end.saturating_add(delta),
}
}
#[inline]
pub fn len(self) -> usize {
debug_assert!(
self.end >= self.start,
"Span::len: end ({}) < start ({})",
self.end,
self.start
);
self.end.saturating_sub(self.start)
}
#[inline]
pub const fn is_empty(self) -> bool {
self.start == self.end
}
}
impl std::fmt::Display for Span {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}..{}", self.start, self.end)
}
}
pub type Components<'a> = SmallVec<[(Cow<'a, str>, Span); 4]>;
pub type OwnedSegment = Segment<'static>;
pub type OwnedElement = Element<'static>;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct Element<'a> {
pub span: Span,
pub components: Components<'a>,
pub repeats: Vec<Components<'a>>,
}
impl<'a> Element<'a> {
pub fn of<S>(components: &[S]) -> Self
where
S: Into<Cow<'a, str>> + Clone,
{
Self {
span: Span::default(),
components: components
.iter()
.map(|c| (c.clone().into(), Span::default()))
.collect(),
repeats: Vec::new(),
}
}
#[inline]
pub fn get_component(&self, n: usize) -> Option<&str> {
self.components.get(n).map(|(c, _)| c.as_ref())
}
#[inline]
pub fn component_or_empty(&self, n: usize) -> &str {
self.get_component(n).unwrap_or("")
}
#[inline]
pub fn component_span(&self, n: usize) -> Option<Span> {
self.components.get(n).map(|(_, s)| *s)
}
#[inline]
pub fn components(&self) -> impl Iterator<Item = &str> {
self.components.iter().map(|(c, _)| c.as_ref())
}
#[inline]
pub fn repeat_count(&self) -> usize {
1 + self.repeats.len()
}
#[inline]
pub fn repetition(&self, n: usize) -> Option<&[(Cow<'a, str>, Span)]> {
match n {
0 => Some(&self.components),
_ => self.repeats.get(n - 1).map(|r| r.as_slice()),
}
}
#[inline]
pub fn repetitions(&self) -> impl Iterator<Item = &[(Cow<'a, str>, Span)]> {
std::iter::once(self.components.as_slice()).chain(self.repeats.iter().map(|r| r.as_slice()))
}
#[inline]
pub fn repeated_component(&self, n: usize) -> impl Iterator<Item = &str> {
self.repetitions()
.map(move |occurrence| occurrence.get(n).map_or("", |(c, _)| c.as_ref()))
}
#[must_use]
pub fn with_span(mut self, span: Span) -> Self {
self.span = span;
self
}
#[must_use]
pub fn and_repeat<S>(mut self, components: &[S]) -> Self
where
S: Into<Cow<'a, str>> + Clone,
{
self.repeats.push(
components
.iter()
.map(|c| (c.clone().into(), Span::default()))
.collect(),
);
self
}
#[inline]
pub fn offset_in_place(&mut self, delta: usize) {
self.span = self.span.offset(delta);
for (_, span) in &mut self.components {
*span = span.offset(delta);
}
for repeat in &mut self.repeats {
for (_, span) in repeat {
*span = span.offset(delta);
}
}
}
#[must_use]
pub fn into_owned(self) -> OwnedElement {
fn own(components: Components<'_>) -> Components<'static> {
components
.into_iter()
.map(|(c, s)| (Cow::Owned(c.into_owned()), s))
.collect()
}
Element {
span: self.span,
components: own(self.components),
repeats: self.repeats.into_iter().map(own).collect(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct Segment<'a> {
pub tag: Cow<'a, str>,
pub span: Span,
pub tag_span: Span,
pub elements: Vec<Element<'a>>,
}
impl<'a> Segment<'a> {
#[inline]
pub fn new(tag: impl Into<Cow<'a, str>>, elements: Vec<Element<'a>>) -> Self {
Self {
tag: tag.into(),
span: Span::default(),
tag_span: Span::default(),
elements,
}
}
#[must_use]
pub fn with_spans(mut self, span: Span, tag_span: Span) -> Self {
self.span = span;
self.tag_span = tag_span;
self
}
#[inline]
pub fn tag(&self) -> &str {
self.tag.as_ref()
}
#[inline]
pub fn get_element(&self, n: usize) -> Option<&Element<'a>> {
self.elements.get(n)
}
#[inline]
pub fn element_str(&self, n: usize) -> Option<&str> {
self.elements.get(n)?.get_component(0)
}
#[inline]
pub fn component_str(&self, elem: usize, comp: usize) -> Option<&str> {
self.elements.get(elem)?.get_component(comp)
}
#[inline]
pub fn element_span(&self, n: usize) -> Option<Span> {
Some(self.elements.get(n)?.span)
}
#[inline]
pub fn repeated_component(
&self,
element: usize,
component: usize,
) -> impl Iterator<Item = &str> {
self.elements
.get(element)
.into_iter()
.flat_map(move |elem| elem.repeated_component(component))
}
#[inline]
#[must_use]
pub fn offset(mut self, delta: usize) -> Self {
self.span = self.span.offset(delta);
self.tag_span = self.tag_span.offset(delta);
for element in &mut self.elements {
element.offset_in_place(delta);
}
self
}
#[must_use]
pub fn into_owned(self) -> OwnedSegment {
Segment {
tag: Cow::Owned(self.tag.into_owned()),
span: self.span,
tag_span: self.tag_span,
elements: self.elements.into_iter().map(Element::into_owned).collect(),
}
}
pub fn required_element(&self, idx: usize) -> Result<&str, EdifactError> {
self.optional_element(idx)
.ok_or_else(|| EdifactError::MissingRequiredElement {
tag: self.tag.clone().into_owned(),
element_index: idx,
})
}
#[inline]
pub fn optional_element(&self, idx: usize) -> Option<&str> {
self.element_str(idx).filter(|s| !s.is_empty())
}
pub fn required_component(&self, elem: usize, comp: usize) -> Result<&str, EdifactError> {
let element =
self.elements
.get(elem)
.ok_or_else(|| EdifactError::MissingRequiredElement {
tag: self.tag.clone().into_owned(),
element_index: elem,
})?;
element
.get_component(comp)
.filter(|s| !s.is_empty())
.ok_or_else(|| EdifactError::MissingRequiredComponent {
tag: self.tag.clone().into_owned(),
element_index: elem,
component_index: comp,
})
}
#[inline]
pub fn optional_component(&self, elem: usize, comp: usize) -> Option<&str> {
self.component_str(elem, comp).filter(|s| !s.is_empty())
}
pub fn parsed_element<T: FromStr>(&self, idx: usize) -> Result<T, EdifactError> {
let raw = self.required_element(idx)?;
raw.parse::<T>().map_err(|_| EdifactError::InvalidText {
offset: self
.element_span(idx)
.map(|s| s.start)
.unwrap_or(self.span.start),
})
}
#[inline]
pub fn value_at(&self, path: ElementPath) -> Option<&str> {
self.elements
.get(path.element)?
.get_component(path.component_index())
}
#[inline]
pub fn span_at(&self, path: ElementPath) -> Option<Span> {
let element = self.elements.get(path.element)?;
match path.component {
Some(c) => element.component_span(c),
None => Some(element.span),
}
}
pub fn value_by_code<L: SegmentLayout + ?Sized>(
&self,
layout: &L,
data_element: &str,
) -> Result<Option<&str>, EdifactError> {
check_layout_tag(layout, &self.tag)?;
Ok(self.value_at(layout.resolve_code(data_element)?))
}
pub fn span_by_code<L: SegmentLayout + ?Sized>(
&self,
layout: &L,
data_element: &str,
) -> Result<Option<Span>, EdifactError> {
check_layout_tag(layout, &self.tag)?;
Ok(self.span_at(layout.resolve_code(data_element)?))
}
pub fn element_by_code<L: SegmentLayout + ?Sized>(
&self,
layout: &L,
data_element: &str,
) -> Result<Option<&Element<'a>>, EdifactError> {
check_layout_tag(layout, &self.tag)?;
let path = layout.resolve_code(data_element)?;
Ok(self.elements.get(path.element))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn owned_segments_pass_where_borrowed_ones_are_expected() {
fn tags<'s>(segments: &'s [Segment<'_>]) -> Vec<&'s str> {
segments.iter().map(Segment::tag).collect()
}
let owned: Vec<OwnedSegment> =
crate::from_reader(std::io::Cursor::new(b"BGM+220'UNT+2+1'"))
.collect::<Result<_, _>>()
.expect("reader parse");
assert_eq!(tags(&owned), ["BGM", "UNT"]);
}
#[test]
fn into_owned_outlives_the_input_buffer() {
let segment = {
let input = b"BGM+220+PO-4711'".to_vec();
let parsed: Vec<Segment<'_>> = crate::from_bytes(&input)
.collect::<Result<_, _>>()
.expect("parse");
parsed.into_iter().next().unwrap().into_owned()
};
assert_eq!(segment.element_str(1), Some("PO-4711"));
}
#[test]
fn required_accessors_treat_empty_as_absent() {
let segments: Vec<Segment<'_>> = crate::from_bytes(b"NAD++::'")
.collect::<Result<_, _>>()
.expect("parse");
let nad = &segments[0];
assert!(matches!(
nad.required_element(0),
Err(EdifactError::MissingRequiredElement { .. })
));
assert!(matches!(
nad.required_component(1, 0),
Err(EdifactError::MissingRequiredComponent { .. })
));
assert!(matches!(
nad.required_component(5, 0),
Err(EdifactError::MissingRequiredElement { .. })
));
}
}