use std::path::PathBuf;
use edifact_mapper::{DataDir, EdifactParty, InterchangeEnvelope, InterchangeMessage, Mapper};
use serde_json::Value;
type Dynamic = mig_bo4e::model::Interchange<Value, Value>;
const FV: &str = "FV2504";
const VARIANT: &str = "UTILMD_Strom";
const PID: &str = "55629";
fn repo_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.and_then(|p| p.parent())
.expect("crate is two levels under the workspace root")
.to_path_buf()
}
fn setup() -> Option<(Mapper, String)> {
let root = repo_root();
let fixture = root.join(format!(
"fixtures/generated/{}/utilmd/{PID}.edi",
FV.to_lowercase()
));
if !root.join(format!("dist/edifact-data-{FV}.bin")).exists() || !fixture.exists() {
eprintln!("skipping: regenerate the {FV} bundle + fixture first");
return None;
}
let mapper =
Mapper::from_data_dir(DataDir::path(root.join("dist")).eager(&[FV])).expect("load bundle");
Some((
mapper,
std::fs::read_to_string(&fixture).expect("read fixture"),
))
}
fn render(mapper: &Mapper, ic: &Dynamic) -> String {
let msg = &ic.nachrichten[0];
mapper
.to_edifact_interchange(
&InterchangeEnvelope {
sender: EdifactParty::bdew("9900000000001"),
receiver: EdifactParty::bdew("9900000000002"),
interchange_ref: "1".to_string(),
},
&[InterchangeMessage {
message_ref: "1".to_string(),
msg_stammdaten: msg.stammdaten.clone(),
tx_stammdaten: msg.transaktionen.clone(),
fv: FV.to_string(),
variant: VARIANT.to_string(),
pid: PID.to_string(),
}],
)
.expect("render")
}
fn segments(edifact: &str) -> Vec<String> {
edifact
.split('\'')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(str::to_string)
.collect()
}
fn entities_mut(tx: &mut Value) -> &mut Value {
if tx.get("stammdaten").is_some() {
&mut tx["stammdaten"]
} else {
tx
}
}
fn technische_ressource_mut(tx: &mut Value) -> &mut Value {
let entity = entities_mut(tx)
.get_mut("technischeRessourceDaten")
.expect("the fixture has a technischeRessourceDaten");
match entity {
Value::Array(reps) => reps
.iter_mut()
.find(|rep| rep.get("steuerbareRessourceRef").is_some())
.expect("a repetition carrying the SG8 references"),
other => other,
}
}
#[test]
fn two_reference_qualifiers_keep_their_own_values_through_a_reverse() {
let Some((mapper, edifact)) = setup() else {
return;
};
let mut ic: Dynamic = mapper
.from_edifact(&edifact, FV, VARIANT, PID)
.expect("parse fixture");
{
let tr = technische_ressource_mut(&mut ic.nachrichten[0].transaktionen[0]);
assert!(
tr.get("steuerbareRessourceRef").is_some() && tr.get("netzlokationRef").is_some(),
"RFF+Z38 and RFF+Z32 must each have their own field, got: {tr:#}"
);
}
const STEUERBARE_RESSOURCE: &str = "SR99999999991";
const NETZLOKATION: &str = "NL99999999992";
{
let tr = technische_ressource_mut(&mut ic.nachrichten[0].transaktionen[0]);
tr["steuerbareRessourceRef"] = Value::String(STEUERBARE_RESSOURCE.to_string());
tr["netzlokationRef"] = Value::String(NETZLOKATION.to_string());
}
let rendered = render(&mapper, &ic);
let segs = segments(&rendered);
assert!(
segs.contains(&format!("RFF+Z38:{STEUERBARE_RESSOURCE}")),
"the Steuerbare Ressource reference is missing or carries the wrong \
value:\n{rendered}"
);
assert!(
segs.contains(&format!("RFF+Z32:{NETZLOKATION}")),
"the Netzlokation reference is missing or carries the wrong \
value:\n{rendered}"
);
let mut reparsed: Dynamic = mapper
.from_edifact(&rendered, FV, VARIANT, PID)
.expect("reparse rendered message");
let tr = technische_ressource_mut(&mut reparsed.nachrichten[0].transaktionen[0]);
assert_eq!(tr["steuerbareRessourceRef"], STEUERBARE_RESSOURCE);
assert_eq!(tr["netzlokationRef"], NETZLOKATION);
}