use crate::disassembler::DisassembledSegment;
use edifact_primitives::EdifactDelimiters;
pub fn render_edifact(segments: &[DisassembledSegment], delimiters: &EdifactDelimiters) -> String {
let mut out = String::new();
for seg in segments {
render_segment(seg, delimiters, &mut out);
}
out
}
fn render_segment(seg: &DisassembledSegment, delimiters: &EdifactDelimiters, out: &mut String) {
let elem_sep = delimiters.element as char;
let comp_sep = delimiters.component as char;
let seg_term = delimiters.segment as char;
out.push_str(&seg.tag);
for element in &seg.elements {
out.push(elem_sep);
for (j, component) in element.iter().enumerate() {
if j > 0 {
out.push(comp_sep);
}
escape_component(component, delimiters, out);
}
}
out.push(seg_term);
}
fn escape_component(value: &str, delimiters: &EdifactDelimiters, out: &mut String) {
let release = delimiters.release;
let special = [
delimiters.element,
delimiters.component,
delimiters.segment,
delimiters.release,
];
let needs_escape = value.bytes().any(|b| special.contains(&b));
if !needs_escape {
out.push_str(value);
return;
}
for b in value.bytes() {
if special.contains(&b) {
out.push(release as char);
}
out.push(b as char);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_render_segments_to_edifact() {
let segments = vec![
DisassembledSegment {
tag: "UNH".to_string(),
elements: vec![
vec!["1".to_string()],
vec!["UTILMD".to_string(), "D".to_string(), "11A".to_string()],
],
},
DisassembledSegment {
tag: "BGM".to_string(),
elements: vec![vec!["E01".to_string()]],
},
];
let delimiters = EdifactDelimiters::default();
let rendered = render_edifact(&segments, &delimiters);
assert_eq!(rendered, "UNH+1+UTILMD:D:11A'BGM+E01'");
}
#[test]
fn test_render_empty_segments() {
let delimiters = EdifactDelimiters::default();
let rendered = render_edifact(&[], &delimiters);
assert_eq!(rendered, "");
}
#[test]
fn test_render_segment_with_empty_components() {
let segments = vec![DisassembledSegment {
tag: "CAV".to_string(),
elements: vec![vec![
"SA".to_string(),
String::new(),
String::new(),
String::new(),
]],
}];
let delimiters = EdifactDelimiters::default();
let rendered = render_edifact(&segments, &delimiters);
assert_eq!(rendered, "CAV+SA:::'");
}
#[test]
fn test_render_multiple_elements() {
let segments = vec![DisassembledSegment {
tag: "DTM".to_string(),
elements: vec![vec![
"137".to_string(),
"20250101".to_string(),
"102".to_string(),
]],
}];
let delimiters = EdifactDelimiters::default();
let rendered = render_edifact(&segments, &delimiters);
assert_eq!(rendered, "DTM+137:20250101:102'");
}
#[test]
fn test_render_escapes_delimiter_chars_in_values() {
let segments = vec![DisassembledSegment {
tag: "DTM".to_string(),
elements: vec![vec![
"137".to_string(),
"202603021433+00".to_string(),
"303".to_string(),
]],
}];
let delimiters = EdifactDelimiters::default();
let rendered = render_edifact(&segments, &delimiters);
assert_eq!(rendered, "DTM+137:202603021433?+00:303'");
}
#[test]
fn test_render_escapes_multiple_special_chars() {
let segments = vec![DisassembledSegment {
tag: "FTX".to_string(),
elements: vec![
vec!["ABO".to_string()],
vec![],
vec![],
vec!["hello?+world:test".to_string()],
],
}];
let delimiters = EdifactDelimiters::default();
let rendered = render_edifact(&segments, &delimiters);
assert_eq!(rendered, "FTX+ABO+++hello???+world?:test'");
}
#[test]
fn test_render_no_escape_needed_for_plain_values() {
let segments = vec![DisassembledSegment {
tag: "BGM".to_string(),
elements: vec![vec!["312".to_string()], vec!["DOC001".to_string()]],
}];
let delimiters = EdifactDelimiters::default();
let rendered = render_edifact(&segments, &delimiters);
assert_eq!(rendered, "BGM+312+DOC001'");
}
}