use crate::SegmentPosition;
#[derive(Debug, Clone)]
pub struct RawSegment<'a> {
pub id: &'a str,
pub elements: Vec<Vec<&'a str>>,
pub position: SegmentPosition,
}
impl<'a> RawSegment<'a> {
pub fn new(id: &'a str, elements: Vec<Vec<&'a str>>, position: SegmentPosition) -> Self {
Self {
id,
elements,
position,
}
}
pub fn element_count(&self) -> usize {
self.elements.len()
}
pub fn get_element(&self, index: usize) -> &str {
self.elements
.get(index)
.and_then(|e| e.first())
.copied()
.unwrap_or("")
}
pub fn get_component(&self, element_index: usize, component_index: usize) -> &str {
self.elements
.get(element_index)
.and_then(|e| e.get(component_index))
.copied()
.unwrap_or("")
}
pub fn get_components(&self, element_index: usize) -> &[&'a str] {
self.elements
.get(element_index)
.map_or(&[], |e| e.as_slice())
}
pub fn is(&self, segment_id: &str) -> bool {
self.id.eq_ignore_ascii_case(segment_id)
}
pub fn to_raw_string(&self, delimiters: &crate::EdifactDelimiters) -> String {
let elem_sep = delimiters.element as char;
let comp_sep = delimiters.component as char;
let mut result = self.id.to_string();
for element in &self.elements {
result.push(elem_sep);
for (j, component) in element.iter().enumerate() {
if j > 0 {
result.push(comp_sep);
}
result.push_str(component);
}
}
while result.ends_with(elem_sep) {
result.pop();
}
result
}
}
impl<'a> std::fmt::Display for RawSegment<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.id)?;
for element in &self.elements {
write!(f, "+")?;
for (j, component) in element.iter().enumerate() {
if j > 0 {
write!(f, ":")?;
}
write!(f, "{component}")?;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_position() -> SegmentPosition {
SegmentPosition::new(1, 0, 1)
}
#[test]
fn test_raw_segment_simple() {
let seg = RawSegment::new(
"UNH",
vec![vec!["00001"], vec!["UTILMD", "D", "11A", "UN", "S2.1"]],
make_position(),
);
assert_eq!(seg.id, "UNH");
assert_eq!(seg.element_count(), 2);
assert_eq!(seg.get_element(0), "00001");
assert_eq!(seg.get_component(1, 0), "UTILMD");
assert_eq!(seg.get_component(1, 4), "S2.1");
}
#[test]
fn test_raw_segment_get_element_out_of_bounds() {
let seg = RawSegment::new("BGM", vec![vec!["E03"]], make_position());
assert_eq!(seg.get_element(0), "E03");
assert_eq!(seg.get_element(1), "");
assert_eq!(seg.get_element(99), "");
}
#[test]
fn test_raw_segment_get_component_out_of_bounds() {
let seg = RawSegment::new("NAD", vec![vec!["Z04", "123"]], make_position());
assert_eq!(seg.get_component(0, 0), "Z04");
assert_eq!(seg.get_component(0, 1), "123");
assert_eq!(seg.get_component(0, 2), "");
assert_eq!(seg.get_component(1, 0), "");
}
#[test]
fn test_raw_segment_display() {
let seg = RawSegment::new(
"NAD",
vec![vec!["Z04"], vec!["9900123000002", "500"]],
make_position(),
);
assert_eq!(seg.to_string(), "NAD+Z04+9900123000002:500");
}
#[test]
fn test_raw_segment_display_no_elements() {
let seg = RawSegment::new("UNA", vec![], make_position());
assert_eq!(seg.to_string(), "UNA");
}
#[test]
fn test_raw_segment_is_case_insensitive() {
let seg = RawSegment::new("NAD", vec![], make_position());
assert!(seg.is("NAD"));
assert!(seg.is("nad"));
assert!(seg.is("Nad"));
assert!(!seg.is("LOC"));
}
#[test]
fn test_raw_segment_get_components() {
let seg = RawSegment::new(
"DTM",
vec![vec!["137", "202501010000+01", "303"]],
make_position(),
);
let components = seg.get_components(0);
assert_eq!(components, &["137", "202501010000+01", "303"]);
assert!(seg.get_components(1).is_empty());
}
#[test]
fn test_raw_segment_zero_copy_lifetime() {
let input = String::from("NAD+Z04+9900123000002:500");
let seg = RawSegment::new(
&input[0..3],
vec![vec![&input[4..7]], vec![&input[8..21], &input[22..25]]],
make_position(),
);
assert_eq!(seg.id, "NAD");
assert_eq!(seg.get_element(0), "Z04");
assert_eq!(seg.get_component(1, 0), "9900123000002");
assert_eq!(seg.get_component(1, 1), "500");
}
#[test]
fn test_raw_segment_clone() {
let seg = RawSegment::new("LOC", vec![vec!["Z16", "DE00014545768"]], make_position());
let cloned = seg.clone();
assert_eq!(seg.id, cloned.id);
assert_eq!(seg.elements, cloned.elements);
assert_eq!(seg.position, cloned.position);
}
#[test]
fn test_raw_segment_to_raw_string() {
let seg = RawSegment::new(
"LOC",
vec![vec!["Z16"], vec!["DE00014545768S0000000000000003054"]],
make_position(),
);
let delimiters = crate::EdifactDelimiters::default();
assert_eq!(
seg.to_raw_string(&delimiters),
"LOC+Z16+DE00014545768S0000000000000003054"
);
}
#[test]
fn test_raw_segment_to_raw_string_composite() {
let seg = RawSegment::new(
"DTM",
vec![vec!["137", "202507011330", "303"]],
make_position(),
);
let delimiters = crate::EdifactDelimiters::default();
assert_eq!(seg.to_raw_string(&delimiters), "DTM+137:202507011330:303");
}
#[test]
fn test_raw_segment_to_raw_string_no_elements() {
let seg = RawSegment::new("UNA", vec![], make_position());
let delimiters = crate::EdifactDelimiters::default();
assert_eq!(seg.to_raw_string(&delimiters), "UNA");
}
#[test]
fn test_raw_segment_to_raw_string_trailing_empty_components() {
let seg = RawSegment::new(
"CCI",
vec![vec!["Z30"], vec![""], vec!["Z07"]],
make_position(),
);
let delimiters = crate::EdifactDelimiters::default();
assert_eq!(seg.to_raw_string(&delimiters), "CCI+Z30++Z07");
}
#[test]
fn test_raw_segment_to_raw_string_trailing_empty_elements() {
let seg = RawSegment::new(
"BGM",
vec![vec!["E03"], vec![""], vec![""]],
make_position(),
);
let delimiters = crate::EdifactDelimiters::default();
assert_eq!(seg.to_raw_string(&delimiters), "BGM+E03");
}
}