pub struct Mapper { /* private fields */ }Implementations§
Source§impl Mapper
impl Mapper
Sourcepub fn from_data_dir(data_dir: DataDir) -> Result<Self, MapperError>
pub fn from_data_dir(data_dir: DataDir) -> Result<Self, MapperError>
Sourcepub fn conversion_service(
&self,
fv: &str,
variant: &str,
) -> Result<ConversionService, MapperError>
pub fn conversion_service( &self, fv: &str, variant: &str, ) -> Result<ConversionService, MapperError>
Get a ConversionService for the given format version and variant.
The service can tokenize EDIFACT input and assemble it into a MIG tree.
Sourcepub fn engine(
&self,
fv: &str,
variant: &str,
pid: &str,
) -> Result<MappingEngine, MapperError>
pub fn engine( &self, fv: &str, variant: &str, pid: &str, ) -> Result<MappingEngine, MapperError>
Get a MappingEngine for a specific PID within a format version and variant.
The engine can convert between assembled MIG trees and BO4E JSON.
Sourcepub fn pid_requirements(
&self,
fv: &str,
variant: &str,
pid: &str,
) -> Result<PidRequirements, MapperError>
pub fn pid_requirements( &self, fv: &str, variant: &str, pid: &str, ) -> Result<PidRequirements, MapperError>
Return the [PidRequirements] for a specific PID within a format version and variant.
Requirements describe every entity and field the PID expects, including AHB status, cardinality, valid code values, and message vs transaction scope.
Sourcepub fn bo4e_catalog(&self, fv: &str) -> Result<Bo4eCatalog, MapperError>
pub fn bo4e_catalog(&self, fv: &str) -> Result<Bo4eCatalog, MapperError>
Return the PID-agnostic [Bo4eCatalog] for a format version.
The catalog contains one entry per BO4E type (BO, COM, Enum) parsed from
bo4e-german source at compile-mappings time. Used by Stammdatenaufbau in
downstream services.
Sourcepub fn list_pids(&self) -> Result<Vec<PidListEntry>, MapperError>
pub fn list_pids(&self) -> Result<Vec<PidListEntry>, MapperError>
List all PIDs available across all format versions found in the data directory.
Scans for edifact-data-{FV}.bin files, loads each bundle, and returns
one entry per PID per variant. Results are sorted by PID.
Sourcepub fn validate_pid(
&self,
json: &Value,
fv: &str,
variant: &str,
pid: &str,
) -> Result<Vec<PidValidationError>, MapperError>
pub fn validate_pid( &self, json: &Value, fv: &str, variant: &str, pid: &str, ) -> Result<Vec<PidValidationError>, MapperError>
Validate a BO4E JSON object against PID requirements.
Returns a list of validation errors. Empty list = valid.
The json should be the transaction-level stammdaten (the entity map).
Sourcepub fn validate_pid_struct(
&self,
value: &impl Serialize,
fv: &str,
variant: &str,
pid: &str,
) -> Result<Vec<PidValidationError>, MapperError>
pub fn validate_pid_struct( &self, value: &impl Serialize, fv: &str, variant: &str, pid: &str, ) -> Result<Vec<PidValidationError>, MapperError>
Validate a typed BO4E struct against PID requirements.
Convenience wrapper that serializes the struct to JSON first.
Works with any Pid*Interchange or Pid*MessageStammdaten type.
§Example
let interchange = build_55001_interchange();
let errors = mapper.validate_pid_struct(&interchange, "FV2504", "UTILMD_Strom", "55001")?;
assert!(errors.is_empty(), "Errors:\n{}", ValidationReport(errors));Sourcepub fn validate_pid_with_conditions(
&self,
json: &Value,
fv: &str,
variant: &str,
pid: &str,
) -> Result<Vec<PidValidationError>, MapperError>
pub fn validate_pid_with_conditions( &self, json: &Value, fv: &str, variant: &str, pid: &str, ) -> Result<Vec<PidValidationError>, MapperError>
Validate with AHB condition awareness.
Reverse-maps the JSON to EDIFACT segments, evaluates AHB conditions, and reports fields as required/optional based on the actual data present.
Falls back to basic validation (without conditions) if no condition evaluator is available for the given variant/format version combination.
Sourcepub fn to_edifact(
&self,
msg_stammdaten: &Value,
tx_stammdaten: &[Value],
fv: &str,
variant: &str,
pid: &str,
) -> Result<String, MapperError>
pub fn to_edifact( &self, msg_stammdaten: &Value, tx_stammdaten: &[Value], fv: &str, variant: &str, pid: &str, ) -> Result<String, MapperError>
Convert BO4E JSON back to an EDIFACT string.
Takes message-level stammdaten, a slice of per-transaction stammdaten, and produces an EDIFACT message body (UNH through UNT content segments, without UNB/UNZ interchange envelope).
§Arguments
msg_stammdaten— message-level entities (e.g., Marktteilnehmer from SG2)tx_stammdaten— per-transaction entities (one per transaction/SG4 instance)fv— format version (e.g., “FV2504”)variant— message variant (e.g., “UTILMD_Strom”)pid— Pruefidentifikator (e.g., “55001”)
§Round-tripping output of from_edifact
msg_stammdaten is only half of what the forward direction produced.
The message header — nachrichtentyp, nachrichtennummer,
erstellungsdatum, i.e. the wire’s BGM and DTM+137 — is in
nachrichtendaten, not in stammdaten, so passing stammdaten alone
renders a body without its header and reports nothing (issue #158).
Use to_edifact_nachricht, which takes
both halves.
§Example
let edifact = mapper.to_edifact(
&msg_json,
&[tx_json],
"FV2504",
"UTILMD_Strom",
"55001",
)?;§Errors
Besides lookup failures, returns MapperError::MissingGroupEntrySegment
when the BO4E fills some of a segment group’s fields but not the one its
entry segment is built from — e.g. a zaehler with geraeteNummer but no
zaehlertypMerkmal, which would render SG10 CAV without CCI. Such a
message cannot be parsed back; its group content would be lost.
Sourcepub fn to_edifact_nachricht(
&self,
nachricht: &Nachricht<Value, Value>,
fv: &str,
variant: &str,
pid: &str,
) -> Result<String, MapperError>
pub fn to_edifact_nachricht( &self, nachricht: &Nachricht<Value, Value>, fv: &str, variant: &str, pid: &str, ) -> Result<String, MapperError>
Render one message body from a Nachricht as from_edifact produced it.
The forward direction splits a message in two: the business objects go to
stammdaten, and the message header — nachrichtentyp,
nachrichtennummer, erstellungsdatum, which are the BGM and
DTM+137 of the wire — goes to nachrichtendaten beside it.
to_edifact takes only the first half, so handing it stammdaten
alone renders a body without its header and says nothing (issue #158).
This takes both, so a caller can give back what it was given:
let interchange = mapper.from_edifact::<Value, Value>(&edifact, fv, variant, pid)?;
let body = mapper.to_edifact_nachricht(&interchange.nachrichten[0], fv, variant, pid)?;Only the body: the UNB/UNH/UNT/UNZ envelope is
to_edifact_interchange’s job.
§Errors
As to_edifact.
Sourcepub fn to_edifact_struct(
&self,
nachricht: &impl Serialize,
fv: &str,
variant: &str,
pid: &str,
) -> Result<String, MapperError>
pub fn to_edifact_struct( &self, nachricht: &impl Serialize, fv: &str, variant: &str, pid: &str, ) -> Result<String, MapperError>
Convert a typed BO4E struct to an EDIFACT string.
Convenience wrapper that serializes the struct to JSON first.
The struct should serialize to the Nachricht shape:
{ "stammdaten": {...}, "transaktionen": [{...}] }
Sourcepub fn from_edifact<M, T>(
&self,
edifact: &str,
fv: &str,
variant: &str,
pid: &str,
) -> Result<Interchange<M, T>, MapperError>where
M: DeserializeOwned,
T: DeserializeOwned,
pub fn from_edifact<M, T>(
&self,
edifact: &str,
fv: &str,
variant: &str,
pid: &str,
) -> Result<Interchange<M, T>, MapperError>where
M: DeserializeOwned,
T: DeserializeOwned,
Parse an EDIFACT interchange string into a typed PID interchange struct.
Runs the full pipeline: tokenize → split messages → assemble → forward-map → deserialize.
The type parameters M and T are the message-level and transaction-level
stammdaten types from the generated PID module.
§Example
use bo4e_edifact_types::generated::fv2504::utilmd::pids::pid_55001::*;
let interchange: Interchange<Pid55001MsgStammdaten, Pid55001TxStammdaten> =
mapper.from_edifact(edifact_str, "FV2504", "UTILMD_Strom", "55001")?;
let tx = &interchange.nachrichten[0].transaktionen[0];
println!("Vorgang: {}", tx.prozessdaten.vorgang_id);Mapping is lossy for content the assembler cannot place: segments the
PID’s AHB does not cover, and segments whose group lacks its entry segment
(e.g. SG10 CAV without CCI). They have no BO4E representation and are
dropped. The conversion still succeeds, so that everything else in the
message is available; each dropped segment is logged as a tracing
warning. Use from_edifact_with_diagnostics to inspect them in code
(e.g. to reject such messages).
Sourcepub fn from_edifact_with_diagnostics<M, T>(
&self,
edifact: &str,
fv: &str,
variant: &str,
pid: &str,
) -> Result<(Interchange<M, T>, Vec<StructureDiagnostic>), MapperError>where
M: DeserializeOwned,
T: DeserializeOwned,
pub fn from_edifact_with_diagnostics<M, T>(
&self,
edifact: &str,
fv: &str,
variant: &str,
pid: &str,
) -> Result<(Interchange<M, T>, Vec<StructureDiagnostic>), MapperError>where
M: DeserializeOwned,
T: DeserializeOwned,
from_edifact, plus the structure diagnostics raised while assembling.
A non-empty diagnostic list does not mean the conversion failed — it means
the BO4E result does not represent everything the EDIFACT carried. In
particular SkippedUnknownSegment marks a segment outside the PID’s AHB
that the assembler advanced past, and OrphanedGroupSegment a segment
the MIG defines but whose group’s entry segment is missing; in both cases
its content is absent from the result.
Sourcepub fn detect_pid(&self, edifact: &str) -> Result<String, MapperError>
pub fn detect_pid(&self, edifact: &str) -> Result<String, MapperError>
Detect the PID (Pruefidentifikator) from a raw EDIFACT interchange.
Tokenizes the input, splits into messages, and extracts the PID from the first message using the RFF+Z13 segment (primary) or BGM+STS fallback.
This enables inbound message processing where the PID is not known upfront:
let pid = mapper.detect_pid(edifact_str)?;
let interchange: MyType = mapper.from_edifact(edifact_str, "FV2504", "UTILMD_Strom", &pid)?;Sourcepub fn validate_edifact(
&self,
edifact: &str,
fv: &str,
level: ValidationLevel,
) -> Result<ValidationReport, MapperError>
pub fn validate_edifact( &self, edifact: &str, fv: &str, level: ValidationLevel, ) -> Result<ValidationReport, MapperError>
Validate raw EDIFACT against its AHB rules.
This is the same pipeline as the v2 API’s POST /api/v2/validate
(run_validation) — both call validate_edifact_message — exposed here
as a library call so consumers (e.g. mako.hive) get full raw-EDIFACT
validation without running the API server. Detects the PID, resolves the
owning variant + its pre-built AhbWorkflow from the loaded bundle,
assembles the message, and runs the shared validation core.
Requires the bundle for fv to carry pid_ahb_workflows (baked in at
compile-mappings). Returns MapperError::PidNotFound if no loaded variant
has a workflow for the detected PID.
Sourcepub fn validate_edifact_for_pid(
&self,
edifact: &str,
fv: &str,
variant: &str,
pid: &str,
level: ValidationLevel,
) -> Result<ValidationReport, MapperError>
pub fn validate_edifact_for_pid( &self, edifact: &str, fv: &str, variant: &str, pid: &str, level: ValidationLevel, ) -> Result<ValidationReport, MapperError>
validate_edifact, but validating against a PID the caller already knows.
Use this when the PID comes from somewhere other than the message — a form,
a route, a job definition. It skips PID detection, which only works for
message types that carry the Prüfidentifikator in RFF+Z13 (UTILMD); for
ORDERS, MSCONS, IFTSTA and the rest, detection cannot recover a PID that the
caller already has.
Sourcepub fn validate_bo4e(
&self,
msg_stammdaten: &Value,
tx_stammdaten: &[Value],
fv: &str,
variant: &str,
pid: &str,
envelope: Option<&InterchangeEnvelope>,
level: ValidationLevel,
) -> Result<ValidationReport, MapperError>
pub fn validate_bo4e( &self, msg_stammdaten: &Value, tx_stammdaten: &[Value], fv: &str, variant: &str, pid: &str, envelope: Option<&InterchangeEnvelope>, level: ValidationLevel, ) -> Result<ValidationReport, MapperError>
Validate BO4E JSON against the AHB rules of its Prüfidentifikator.
This is validate_edifact with a reverse-mapping front end: the BO4E
input is rendered to a complete EDIFACT interchange
(to_edifact_interchange) and that interchange is validated. Because it
is literally the same call, the findings are the ones the EDIFACT
validation reports for the message this BO4E describes — including the
bo4e_path enrichment that points each finding back at the BO4E field it
came from. Callers working in BO4E (forms, assistants) therefore do not
need their own EDIFACT-path-to-BO4E-path translation.
envelope fills UNB/UNZ. Pass None unless the message type’s MIG covers
the interchange envelope (e.g. MSCONS) — for the others the envelope is
outside the AHB and a neutral placeholder is used.
Two classes of finding cannot appear here, because the BO4E input has no counterpart for them: the UNT segment-count check (the trailer is regenerated) and skipped-unknown-segment diagnostics (segments outside the AHB have no BO4E representation).
Sourcepub fn association_code(
&self,
fv: &str,
variant: &str,
) -> Result<String, MapperError>
pub fn association_code( &self, fv: &str, variant: &str, ) -> Result<String, MapperError>
Sourcepub fn message_metadata(
&self,
fv: &str,
variant: &str,
) -> Result<MessageMetadata, MapperError>
pub fn message_metadata( &self, fv: &str, variant: &str, ) -> Result<MessageMetadata, MapperError>
Get full message metadata for a variant, including the UNH S009 components.
Returns the message type, UN/EDIFACT release code, and association code needed to construct UNH segments.
Sourcepub fn to_edifact_interchange(
&self,
envelope: &InterchangeEnvelope,
messages: &[InterchangeMessage],
) -> Result<String, MapperError>
pub fn to_edifact_interchange( &self, envelope: &InterchangeEnvelope, messages: &[InterchangeMessage], ) -> Result<String, MapperError>
Convert BO4E JSON to a complete EDIFACT interchange with envelope segments.
Produces a full interchange including UNA, UNB, UNH, message body, UNT, and UNZ.
§The envelope is regenerated, not reproduced
This always emits a UNA service string advice and stamps the UNB
date and time from the clock, so a render is never byte-identical to the
interchange it came from: an input carrying no UNA gains one, and its
interchange date becomes today (issue #161). That is right for a
re-send, and wrong for a caller checking that a conversion did not
change the message.
Two ways to check that instead:
- compare message bodies, which
to_edifact_nachrichtrenders without any envelope; - or reproduce the envelope with
to_edifact_interchange_withandEnvelopeOptions, which take theUNAdecision and theUNBdate and time from the caller.
Neither reproduces non-default delimiters: the whole render uses
EdifactDelimiters::default.
§Example
let edifact = mapper.to_edifact_interchange(
&InterchangeEnvelope {
sender: EdifactParty::bdew("9900000000003"),
receiver: EdifactParty::bdew("9900000000001"),
interchange_ref: "REF001".to_string(),
},
&[InterchangeMessage {
message_ref: "MSG001".to_string(),
msg_stammdaten: serde_json::json!({"marktteilnehmer": []}),
tx_stammdaten: vec![serde_json::json!({"prozessdaten": {"pruefidentifikator": "55001"}})],
fv: "FV2604".to_string(),
variant: "UTILMD_Strom".to_string(),
pid: "55001".to_string(),
}],
)?;
assert!(edifact.starts_with("UNA:+.? '"));§Errors
Fails like to_edifact, including
MapperError::MissingGroupEntrySegment for a group that would be
rendered without its entry segment.
Sourcepub fn to_edifact_interchange_with(
&self,
envelope: &InterchangeEnvelope,
messages: &[InterchangeMessage],
options: &EnvelopeOptions,
) -> Result<String, MapperError>
pub fn to_edifact_interchange_with( &self, envelope: &InterchangeEnvelope, messages: &[InterchangeMessage], options: &EnvelopeOptions, ) -> Result<String, MapperError>
Like to_edifact_interchange, with
control over how the envelope is built.
The default regenerates it — a fresh UNA and a UNB timestamped from
the clock — which is right for a re-send but means a render can never
equal its input. EnvelopeOptions lets a caller that has the original
ask for it back instead (issue #161).
§Errors
Sourcepub fn loaded_format_versions(&self) -> Vec<String>
pub fn loaded_format_versions(&self) -> Vec<String>
List all format versions currently loaded in memory.