use crate::tokenize::OwnedSegment;
use crate::AssemblyError;
pub fn detect_pid(segments: &[OwnedSegment]) -> Result<String, AssemblyError> {
for seg in segments {
if seg.is("RFF") {
let qualifier = seg.get_component(0, 0);
let reference = seg.get_component(0, 1);
if qualifier == "Z13" && !reference.is_empty() {
return Ok(reference.to_string());
}
}
}
let msg_type = segments
.iter()
.find(|s| s.is("UNH"))
.map(|s| s.get_component(1, 0).to_string())
.unwrap_or_default();
match msg_type.as_str() {
"APERAK" => resolve_aperak_pid(segments),
"CONTRL" => Ok(CONTRL_PID.to_string()),
"UTILMD" => resolve_utilmd_pid_fallback(segments),
_ => Err(AssemblyError::PidDetectionFailed),
}
}
fn resolve_utilmd_pid_fallback(segments: &[OwnedSegment]) -> Result<String, AssemblyError> {
let bgm = segments.iter().find(|s| s.is("BGM"));
let sts = segments.iter().find(|s| s.is("STS"));
match (bgm, sts) {
(Some(bgm_seg), Some(sts_seg)) => {
let doc_code = bgm_seg.get_element(0);
let reason = sts_seg.get_component(1, 0);
resolve_utilmd_pid(doc_code, reason)
}
(Some(bgm_seg), None) => {
let doc_code = bgm_seg.get_element(0);
resolve_utilmd_pid_from_bgm(doc_code)
}
_ => Err(AssemblyError::PidDetectionFailed),
}
}
fn resolve_aperak_pid(segments: &[OwnedSegment]) -> Result<String, AssemblyError> {
let bgm = segments
.iter()
.find(|s| s.is("BGM"))
.ok_or(AssemblyError::PidDetectionFailed)?;
match bgm.get_element(0) {
"313" => Ok("92001".to_string()),
"312" => Ok("92002".to_string()),
_ => Err(AssemblyError::PidDetectionFailed),
}
}
pub const CONTRL_PID: &str = "91001";
fn resolve_utilmd_pid(doc_code: &str, reason: &str) -> Result<String, AssemblyError> {
match (doc_code, reason) {
("E01", "Z33") => Ok("55001".to_string()),
("E01", "Z34") => Ok("55002".to_string()),
("E01", "Z35") => Ok("55003".to_string()),
("E02", "Z33") => Ok("55101".to_string()),
("E02", "Z34") => Ok("55102".to_string()),
("E03", "Z33") => Ok("55201".to_string()),
_ => Err(AssemblyError::PidDetectionFailed),
}
}
fn resolve_utilmd_pid_from_bgm(doc_code: &str) -> Result<String, AssemblyError> {
match doc_code {
"E01" => Ok("55001".to_string()),
"E02" => Ok("55101".to_string()),
"E03" => Ok("55201".to_string()),
_ => Err(AssemblyError::PidDetectionFailed),
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_segment(id: &str, elements: Vec<Vec<&str>>) -> OwnedSegment {
OwnedSegment {
id: id.to_string(),
elements: elements
.into_iter()
.map(|e| e.into_iter().map(|c| c.to_string()).collect())
.collect(),
segment_number: 0,
}
}
#[test]
fn test_detect_pid_from_rff_z13() {
let segments = vec![
make_segment("UNH", vec![vec!["001"]]),
make_segment("BGM", vec![vec!["E01"]]),
make_segment("RFF", vec![vec!["Z13", "55001"]]),
make_segment("UNT", vec![vec!["3", "001"]]),
];
let pid = detect_pid(&segments).unwrap();
assert_eq!(pid, "55001");
}
#[test]
fn test_detect_pid_from_bgm_and_sts() {
let segments = vec![
make_segment(
"UNH",
vec![vec!["001"], vec!["UTILMD", "D", "11A", "UN", "S2.1"]],
),
make_segment("BGM", vec![vec!["E01"]]),
make_segment("STS", vec![vec![""], vec!["Z33"]]),
make_segment("UNT", vec![vec!["3", "001"]]),
];
let pid = detect_pid(&segments).unwrap();
assert_eq!(pid, "55001");
}
#[test]
fn test_detect_pid_from_bgm_only() {
let segments = vec![
make_segment(
"UNH",
vec![vec!["001"], vec!["UTILMD", "D", "11A", "UN", "S2.1"]],
),
make_segment("BGM", vec![vec!["E03"]]),
make_segment("UNT", vec![vec!["2", "001"]]),
];
let pid = detect_pid(&segments).unwrap();
assert_eq!(pid, "55201");
}
#[test]
fn test_detect_pid_fails_no_bgm() {
let segments = vec![
make_segment("UNH", vec![vec!["001"]]),
make_segment("UNT", vec![vec!["1", "001"]]),
];
let result = detect_pid(&segments);
assert!(result.is_err());
}
#[test]
fn test_detect_pid_prefers_rff_z13_over_bgm() {
let segments = vec![
make_segment("UNH", vec![vec!["001"]]),
make_segment("BGM", vec![vec!["E01"]]),
make_segment("STS", vec![vec![""], vec!["Z33"]]),
make_segment("RFF", vec![vec!["Z13", "99999"]]),
make_segment("UNT", vec![vec!["4", "001"]]),
];
let pid = detect_pid(&segments).unwrap();
assert_eq!(pid, "99999"); }
#[test]
fn test_detect_pid_from_parsed_edifact() {
let input = b"UNA:+.? 'UNB+UNOC:3+SENDER+RECEIVER+210101:1200+REF001'UNH+MSG001+UTILMD:D:11A:UN:S2.1'BGM+E01+DOC001'RFF+Z13:55001'UNT+3+MSG001'UNZ+1+REF001'";
let segments = crate::tokenize::parse_to_segments(input).unwrap();
let pid = detect_pid(&segments).unwrap();
assert_eq!(pid, "55001");
}
#[test]
fn test_detect_aperak_fehlermeldung_from_bgm_313() {
let segments = vec![
make_segment(
"UNH",
vec![vec!["MSG"], vec!["APERAK", "D", "07B", "UN", "2.1i"]],
),
make_segment("BGM", vec![vec!["313"], vec!["MSG-BGM"]]),
make_segment("UNT", vec![vec!["3"], vec!["MSG"]]),
];
let pid = detect_pid(&segments).unwrap();
assert_eq!(pid, "92001");
}
#[test]
fn test_detect_aperak_anerkennungsmeldung_from_bgm_312() {
let segments = vec![
make_segment(
"UNH",
vec![vec!["MSG"], vec!["APERAK", "D", "07B", "UN", "2.1i"]],
),
make_segment("BGM", vec![vec!["312"], vec!["MSG-BGM"]]),
make_segment("UNT", vec![vec!["3"], vec!["MSG"]]),
];
let pid = detect_pid(&segments).unwrap();
assert_eq!(pid, "92002");
}
#[test]
fn test_detect_aperak_unknown_bgm_code_fails() {
let segments = vec![
make_segment(
"UNH",
vec![vec!["MSG"], vec!["APERAK", "D", "07B", "UN", "2.1i"]],
),
make_segment("BGM", vec![vec!["999"], vec!["MSG-BGM"]]),
make_segment("UNT", vec![vec!["3"], vec!["MSG"]]),
];
assert!(detect_pid(&segments).is_err());
}
#[test]
fn test_detect_contrl_empfangsbestaetigung() {
let input = b"UNB+UNOC:3+9903111000003:500+9900262000007:500+220628:2255+X165'\
UNH+X165+CONTRL:D:3:UN:2.0b'\
UCI+GPKELF18197957+9903111000003:500+9900262000007:500+7'\
UNT+3+X165'UNZ+1+X165'";
let segments = crate::tokenize::parse_to_segments(input).unwrap();
assert_eq!(detect_pid(&segments).unwrap(), CONTRL_PID);
}
#[test]
fn test_detect_contrl_interchange_error() {
let input = b"UNB+UNOC:3+9903111000003:500+9900269000000:500+220614:1519+X315'\
UNH+X315+CONTRL:D:3:UN:2.0b'\
UCI+10000000172UCI+9903111000003:500+9900269000000:500+4+13+UNB'\
UNT+3+X315'UNZ+1+X315'";
let segments = crate::tokenize::parse_to_segments(input).unwrap();
assert_eq!(detect_pid(&segments).unwrap(), CONTRL_PID);
}
#[test]
fn test_detect_contrl_message_error() {
let input = b"UNB+UNOC:3+9903111000003:500+9900269000000:500+220614:1519+X315'\
UNH+X315+CONTRL:D:3:UN:2.0b'\
UCI+10000000172UCI+9903111000003:500+9900269000000:500+4'\
UCM+10000000172UCM+UTILMD:D:11A:UN:5.2c+4'UCS+90'UCD+21+6:4'\
UNT+6+X315'UNZ+1+X315'";
let segments = crate::tokenize::parse_to_segments(input).unwrap();
assert_eq!(detect_pid(&segments).unwrap(), CONTRL_PID);
}
#[test]
fn test_detect_contrl_needs_only_the_message_type() {
let input = b"UNB+UNOC:3+A:500+B:500+220614:1519+X315'\
UNH+X315+CONTRL:D:3:UN:2.0b'\
UNT+2+X315'UNZ+1+X315'";
let segments = crate::tokenize::parse_to_segments(input).unwrap();
assert_eq!(detect_pid(&segments).unwrap(), CONTRL_PID);
}
#[test]
fn test_aperak_detector_not_applied_to_non_aperak() {
let segments = vec![
make_segment(
"UNH",
vec![vec!["MSG"], vec!["UTILMD", "D", "11A", "UN", "S2.1"]],
),
make_segment("BGM", vec![vec!["312"], vec!["MSG-BGM"]]),
make_segment("UNT", vec![vec!["3"], vec!["MSG"]]),
];
let result = detect_pid(&segments);
assert!(
result.is_err()
|| result
.as_ref()
.map(|s| !s.starts_with("92"))
.unwrap_or(true),
"UTILMD BGM+312 must not resolve to APERAK PID, got {:?}",
result
);
}
}