use mig_types::segment::OwnedSegment;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub use bo4e_edifact_types::{
DynamicInterchange, DynamicNachricht, Interchange, Interchangedaten, Nachricht,
Nachrichtendaten,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MappedTransaktion {
pub stammdaten: serde_json::Value,
#[serde(skip)]
pub nesting_info: HashMap<String, Vec<usize>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MappedMessage {
pub stammdaten: serde_json::Value,
pub transaktionen: Vec<MappedTransaktion>,
#[serde(skip)]
pub nesting_info: HashMap<String, Vec<usize>>,
}
impl MappedMessage {
pub fn into_dynamic_nachricht(self, nachrichtendaten: Nachrichtendaten) -> DynamicNachricht {
Nachricht {
nachrichtendaten,
stammdaten: self.stammdaten,
transaktionen: self
.transaktionen
.into_iter()
.map(|t| t.stammdaten)
.collect(),
}
}
}
pub fn extract_unh_fields(unh: &OwnedSegment) -> (String, String) {
let referenz = unh.get_element(0).to_string();
let typ = unh.get_component(1, 0).to_string();
(referenz, typ)
}
pub fn extract_interchangedaten(envelope: &[OwnedSegment]) -> Interchangedaten {
let mut result = Interchangedaten::default();
for seg in envelope {
if seg.is("UNB") {
let val = |s: &str| {
if s.is_empty() {
None
} else {
Some(s.to_string())
}
};
result.syntax_kennung = val(seg.get_component(0, 0));
result.absender_code = val(seg.get_component(1, 0));
result.empfaenger_code = val(seg.get_component(2, 0));
result.datum = val(seg.get_component(3, 0));
result.zeit = val(seg.get_component(3, 1));
result.interchange_ref = val(seg.get_element(4));
}
}
result
}
pub fn extract_nachrichtendaten(envelope: &[OwnedSegment]) -> serde_json::Value {
let data = extract_interchangedaten(envelope);
serde_json::to_value(&data).unwrap_or_default()
}
pub fn rebuild_unb_from_interchangedaten(data: &Interchangedaten) -> OwnedSegment {
let syntax = data.syntax_kennung.as_deref().unwrap_or("UNOC");
let sender = data.absender_code.as_deref().unwrap_or("");
let receiver = data.empfaenger_code.as_deref().unwrap_or("");
let datum = data.datum.as_deref().unwrap_or("");
let zeit = data.zeit.as_deref().unwrap_or("");
let interchange_ref = data.interchange_ref.as_deref().unwrap_or("00000");
OwnedSegment {
id: "UNB".to_string(),
elements: vec![
vec![syntax.to_string(), "3".to_string()],
vec![sender.to_string(), "500".to_string()],
vec![receiver.to_string(), "500".to_string()],
vec![datum.to_string(), zeit.to_string()],
vec![interchange_ref.to_string()],
],
segment_number: 0,
}
}
pub fn rebuild_unb(nachrichtendaten: &serde_json::Value) -> OwnedSegment {
let syntax = nachrichtendaten
.get("syntaxKennung")
.and_then(|v| v.as_str())
.unwrap_or("UNOC");
let sender = nachrichtendaten
.get("absenderCode")
.and_then(|v| v.as_str())
.unwrap_or("");
let receiver = nachrichtendaten
.get("empfaengerCode")
.and_then(|v| v.as_str())
.unwrap_or("");
let datum = nachrichtendaten
.get("datum")
.and_then(|v| v.as_str())
.unwrap_or("");
let zeit = nachrichtendaten
.get("zeit")
.and_then(|v| v.as_str())
.unwrap_or("");
let interchange_ref = nachrichtendaten
.get("interchangeRef")
.and_then(|v| v.as_str())
.unwrap_or("00000");
OwnedSegment {
id: "UNB".to_string(),
elements: vec![
vec![syntax.to_string(), "3".to_string()],
vec![sender.to_string(), "500".to_string()],
vec![receiver.to_string(), "500".to_string()],
vec![datum.to_string(), zeit.to_string()],
vec![interchange_ref.to_string()],
],
segment_number: 0,
}
}
pub fn rebuild_unh(referenz: &str, nachrichten_typ: &str) -> OwnedSegment {
OwnedSegment {
id: "UNH".to_string(),
elements: vec![
vec![referenz.to_string()],
vec![
nachrichten_typ.to_string(),
"D".to_string(),
"11A".to_string(),
"UN".to_string(),
"S2.1".to_string(),
],
],
segment_number: 0,
}
}
pub fn rebuild_unt(segment_count: usize, referenz: &str) -> OwnedSegment {
OwnedSegment {
id: "UNT".to_string(),
elements: vec![vec![segment_count.to_string()], vec![referenz.to_string()]],
segment_number: 0,
}
}
pub fn rebuild_unz(message_count: usize, interchange_ref: &str) -> OwnedSegment {
OwnedSegment {
id: "UNZ".to_string(),
elements: vec![
vec![message_count.to_string()],
vec![interchange_ref.to_string()],
],
segment_number: 0,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mapped_transaktion_serde_roundtrip() {
let tx = MappedTransaktion {
stammdaten: serde_json::json!({
"prozessdaten": {
"vorgangId": "TX001",
"transaktionsgrund": "E01"
},
"marktlokation": { "marktlokationsId": "DE000111222333" }
}),
nesting_info: Default::default(),
};
let json = serde_json::to_string(&tx).unwrap();
let de: MappedTransaktion = serde_json::from_str(&json).unwrap();
assert_eq!(
de.stammdaten["prozessdaten"]["vorgangId"].as_str().unwrap(),
"TX001"
);
assert!(de.stammdaten["marktlokation"].is_object());
}
#[test]
fn test_dynamic_nachricht_serde_roundtrip() {
let msg: DynamicNachricht = Nachricht {
nachrichtendaten: Nachrichtendaten {
unh_referenz: "00001".to_string(),
nachrichten_typ: "UTILMD".to_string(),
},
stammdaten: serde_json::json!({
"marktteilnehmer": [
{ "marktrolle": "MS", "rollencodenummer": "9900123" }
]
}),
transaktionen: vec![serde_json::json!({})],
};
let json = serde_json::to_string(&msg).unwrap();
let de: DynamicNachricht = serde_json::from_str(&json).unwrap();
assert_eq!(de.nachrichtendaten.unh_referenz, "00001");
assert_eq!(de.nachrichtendaten.nachrichten_typ, "UTILMD");
assert_eq!(de.transaktionen.len(), 1);
}
#[test]
fn test_dynamic_interchange_serde_roundtrip() {
let interchange: DynamicInterchange = Interchange {
interchangedaten: Interchangedaten {
absender_code: Some("9900123456789".to_string()),
empfaenger_code: Some("9900987654321".to_string()),
..Default::default()
},
nachrichten: vec![Nachricht {
nachrichtendaten: Nachrichtendaten {
unh_referenz: "00001".to_string(),
nachrichten_typ: "UTILMD".to_string(),
},
stammdaten: serde_json::json!({}),
transaktionen: vec![],
}],
};
let json = serde_json::to_string_pretty(&interchange).unwrap();
let de: DynamicInterchange = serde_json::from_str(&json).unwrap();
assert_eq!(de.nachrichten.len(), 1);
assert_eq!(de.nachrichten[0].nachrichtendaten.unh_referenz, "00001");
}
#[test]
fn test_extract_interchangedaten_from_segments() {
let envelope = vec![OwnedSegment {
id: "UNB".to_string(),
elements: vec![
vec!["UNOC".to_string(), "3".to_string()],
vec!["9900123456789".to_string(), "500".to_string()],
vec!["9900987654321".to_string(), "500".to_string()],
vec!["210101".to_string(), "1200".to_string()],
vec!["REF001".to_string()],
],
segment_number: 0,
}];
let data = extract_interchangedaten(&envelope);
assert_eq!(data.absender_code.as_deref(), Some("9900123456789"));
assert_eq!(data.empfaenger_code.as_deref(), Some("9900987654321"));
assert_eq!(data.interchange_ref.as_deref(), Some("REF001"));
assert_eq!(data.syntax_kennung.as_deref(), Some("UNOC"));
assert_eq!(data.datum.as_deref(), Some("210101"));
assert_eq!(data.zeit.as_deref(), Some("1200"));
}
#[test]
fn test_extract_envelope_from_segments_json() {
let envelope = vec![OwnedSegment {
id: "UNB".to_string(),
elements: vec![
vec!["UNOC".to_string(), "3".to_string()],
vec!["9900123456789".to_string(), "500".to_string()],
vec!["9900987654321".to_string(), "500".to_string()],
vec!["210101".to_string(), "1200".to_string()],
vec!["REF001".to_string()],
],
segment_number: 0,
}];
let nd = extract_nachrichtendaten(&envelope);
assert_eq!(nd["absenderCode"].as_str().unwrap(), "9900123456789");
assert_eq!(nd["empfaengerCode"].as_str().unwrap(), "9900987654321");
assert_eq!(nd["interchangeRef"].as_str().unwrap(), "REF001");
assert_eq!(nd["syntaxKennung"].as_str().unwrap(), "UNOC");
assert_eq!(nd["datum"].as_str().unwrap(), "210101");
assert_eq!(nd["zeit"].as_str().unwrap(), "1200");
}
#[test]
fn test_extract_unh_fields() {
let unh = OwnedSegment {
id: "UNH".to_string(),
elements: vec![
vec!["MSG001".to_string()],
vec![
"UTILMD".to_string(),
"D".to_string(),
"11A".to_string(),
"UN".to_string(),
"S2.1".to_string(),
],
],
segment_number: 0,
};
let (referenz, typ) = extract_unh_fields(&unh);
assert_eq!(referenz, "MSG001");
assert_eq!(typ, "UTILMD");
}
#[test]
fn test_rebuild_unb_from_interchangedaten_typed() {
let data = Interchangedaten {
syntax_kennung: Some("UNOC".to_string()),
absender_code: Some("9900123456789".to_string()),
empfaenger_code: Some("9900987654321".to_string()),
datum: Some("210101".to_string()),
zeit: Some("1200".to_string()),
interchange_ref: Some("REF001".to_string()),
};
let unb = rebuild_unb_from_interchangedaten(&data);
assert_eq!(unb.id, "UNB");
assert_eq!(unb.elements[0], vec!["UNOC", "3"]);
assert_eq!(unb.elements[1][0], "9900123456789");
assert_eq!(unb.elements[2][0], "9900987654321");
assert_eq!(unb.elements[3], vec!["210101", "1200"]);
assert_eq!(unb.elements[4], vec!["REF001"]);
}
#[test]
fn test_rebuild_unb_from_nachrichtendaten() {
let nd = serde_json::json!({
"syntaxKennung": "UNOC",
"absenderCode": "9900123456789",
"empfaengerCode": "9900987654321",
"datum": "210101",
"zeit": "1200",
"interchangeRef": "REF001"
});
let unb = rebuild_unb(&nd);
assert_eq!(unb.id, "UNB");
assert_eq!(unb.elements[0], vec!["UNOC", "3"]);
assert_eq!(unb.elements[1][0], "9900123456789");
assert_eq!(unb.elements[2][0], "9900987654321");
assert_eq!(unb.elements[3], vec!["210101", "1200"]);
assert_eq!(unb.elements[4], vec!["REF001"]);
}
#[test]
fn test_rebuild_unb_defaults() {
let nd = serde_json::json!({});
let unb = rebuild_unb(&nd);
assert_eq!(unb.id, "UNB");
assert_eq!(unb.elements[0], vec!["UNOC", "3"]);
}
#[test]
fn test_rebuild_unh() {
let unh = rebuild_unh("00001", "UTILMD");
assert_eq!(unh.id, "UNH");
assert_eq!(unh.elements[0], vec!["00001"]);
assert_eq!(unh.elements[1][0], "UTILMD");
assert_eq!(unh.elements[1][1], "D");
assert_eq!(unh.elements[1][2], "11A");
assert_eq!(unh.elements[1][3], "UN");
assert_eq!(unh.elements[1][4], "S2.1");
}
#[test]
fn test_rebuild_unt() {
let unt = rebuild_unt(25, "00001");
assert_eq!(unt.id, "UNT");
assert_eq!(unt.elements[0], vec!["25"]);
assert_eq!(unt.elements[1], vec!["00001"]);
}
#[test]
fn test_rebuild_unz() {
let unz = rebuild_unz(1, "REF001");
assert_eq!(unz.id, "UNZ");
assert_eq!(unz.elements[0], vec!["1"]);
assert_eq!(unz.elements[1], vec!["REF001"]);
}
#[test]
fn test_roundtrip_interchangedaten_rebuild() {
let original = OwnedSegment {
id: "UNB".to_string(),
elements: vec![
vec!["UNOC".to_string(), "3".to_string()],
vec!["9900123456789".to_string(), "500".to_string()],
vec!["9900987654321".to_string(), "500".to_string()],
vec!["210101".to_string(), "1200".to_string()],
vec!["REF001".to_string()],
],
segment_number: 0,
};
let data = extract_interchangedaten(&[original]);
let rebuilt = rebuild_unb_from_interchangedaten(&data);
assert_eq!(rebuilt.elements[0], vec!["UNOC", "3"]);
assert_eq!(rebuilt.elements[1][0], "9900123456789");
assert_eq!(rebuilt.elements[2][0], "9900987654321");
assert_eq!(rebuilt.elements[3], vec!["210101", "1200"]);
assert_eq!(rebuilt.elements[4], vec!["REF001"]);
}
#[test]
fn test_roundtrip_nachrichtendaten_rebuild() {
let original = OwnedSegment {
id: "UNB".to_string(),
elements: vec![
vec!["UNOC".to_string(), "3".to_string()],
vec!["9900123456789".to_string(), "500".to_string()],
vec!["9900987654321".to_string(), "500".to_string()],
vec!["210101".to_string(), "1200".to_string()],
vec!["REF001".to_string()],
],
segment_number: 0,
};
let nd = extract_nachrichtendaten(&[original]);
let rebuilt = rebuild_unb(&nd);
assert_eq!(rebuilt.elements[0], vec!["UNOC", "3"]);
assert_eq!(rebuilt.elements[1][0], "9900123456789");
assert_eq!(rebuilt.elements[2][0], "9900987654321");
assert_eq!(rebuilt.elements[3], vec!["210101", "1200"]);
assert_eq!(rebuilt.elements[4], vec!["REF001"]);
}
#[test]
fn test_into_dynamic_nachricht() {
let mapped = MappedMessage {
stammdaten: serde_json::json!({"marktteilnehmer": []}),
transaktionen: vec![MappedTransaktion {
stammdaten: serde_json::json!({"prozessdaten": {"id": "1"}}),
nesting_info: Default::default(),
}],
nesting_info: Default::default(),
};
let nd = Nachrichtendaten {
unh_referenz: "00001".to_string(),
nachrichten_typ: "UTILMD".to_string(),
};
let nachricht = mapped.into_dynamic_nachricht(nd);
assert_eq!(nachricht.nachrichtendaten.unh_referenz, "00001");
assert_eq!(nachricht.transaktionen.len(), 1);
assert!(nachricht.transaktionen[0]["prozessdaten"].is_object());
}
}