pub struct EvaluationContext<'a> {
pub pruefidentifikator: &'a str,
pub external: &'a dyn ExternalConditionProvider,
pub segments: &'a [OwnedSegment],
pub navigator: Option<&'a dyn GroupNavigator>,
pub resolved_value: Option<&'a str>,
pub resolved_segment: Option<&'a [Vec<String>]>,
pub codelist: &'a ArtikelIdCodelist,
pub ebd_clusters: &'a EbdClusterLookup,
pub scope: Option<GroupScope<'a>>,
}Expand description
Context passed to condition evaluators during evaluation.
Carries references to the transaction data and external condition provider needed to evaluate AHB conditions.
Fields§
§pruefidentifikator: &'a strThe Pruefidentifikator (e.g., “11001”, “55001”) that identifies the specific AHB workflow being validated against.
external: &'a dyn ExternalConditionProviderProvider for external conditions that depend on business context outside the EDIFACT message.
segments: &'a [OwnedSegment]Parsed EDIFACT segments for direct segment inspection by condition evaluators. Conditions often need to check specific segment values.
Optional group navigator for group-scoped condition queries. When None, group-scoped methods return empty / false / 0.
resolved_value: Option<&'a str>The resolved value of the data element currently being validated.
Set per-field during tree-based validation from AhbNode.value.
Format/value conditions (e.g., [931] “ZZZ=+00”, [932] “HHMM=2200”)
check this first instead of searching message-wide.
When None, conditions fall back to message-wide segment searches.
resolved_segment: Option<&'a [Vec<String>]>The full segment elements for the segment being validated.
Allows cross-element access within the same segment (e.g., check qualifier in element 0 while validating value in element 1). When None, conditions fall back to message-wide segment searches.
codelist: &'a ArtikelIdCodelistBDEW Artikel-ID / Artikelnummer codelist, used by conditions like
[73] and [284] that reference “Codeliste der Gruppenartikel- und
Artikel-ID”. Defaults to ArtikelIdCodelist::embedded.
ebd_clusters: &'a EbdClusterLookupPer-EBD answer-code cluster lookup, used by conditions like
[359] / [360] / [366] / [368] that restrict STS response
codes to the Zustimmung or Ablehnung cluster of a specific EBD.
Defaults to EbdClusterLookup::embedded.
scope: Option<GroupScope<'a>>Optional group scope — when Some, scope-aware helpers like
scoped_find_segments narrow lookups to that group instance.
When None, scoped helpers fall back to message-wide semantics.
Implementations§
Source§impl<'a> EvaluationContext<'a>
impl<'a> EvaluationContext<'a>
Sourcepub fn new(
pruefidentifikator: &'a str,
external: &'a dyn ExternalConditionProvider,
segments: &'a [OwnedSegment],
) -> Self
pub fn new( pruefidentifikator: &'a str, external: &'a dyn ExternalConditionProvider, segments: &'a [OwnedSegment], ) -> Self
Create a new evaluation context (without group navigator).
Create a new evaluation context with a group navigator.
Sourcepub fn with_codelist(self, codelist: &'a ArtikelIdCodelist) -> Self
pub fn with_codelist(self, codelist: &'a ArtikelIdCodelist) -> Self
Override the codelist (tests only — defaults to embedded).
Sourcepub fn with_ebd_clusters(self, ebd_clusters: &'a EbdClusterLookup) -> Self
pub fn with_ebd_clusters(self, ebd_clusters: &'a EbdClusterLookup) -> Self
Override the EBD cluster lookup (tests only — defaults to embedded).
Sourcepub fn with_scope(&self, scope: GroupScope<'a>) -> Self
pub fn with_scope(&self, scope: GroupScope<'a>) -> Self
Attach a group scope; returns a fresh context without consuming self.
Mirrors with_resolved’s &self shape so validate_tree can build
a scoped context inside a loop without moving the outer one.
Sourcepub fn with_resolved(
&self,
value: Option<&'a str>,
segment: Option<&'a [Vec<String>]>,
) -> Self
pub fn with_resolved( &self, value: Option<&'a str>, segment: Option<&'a [Vec<String>]>, ) -> Self
Create a context with a resolved field value for tree-based validation.
The resolved value comes from the ValidatedTree — the specific data
element value at the current tree position. Conditions check this first.
Get the group navigator, if one is set.
Sourcepub fn find_segment(&self, segment_id: &str) -> Option<&'a OwnedSegment>
pub fn find_segment(&self, segment_id: &str) -> Option<&'a OwnedSegment>
Find the first segment with the given ID.
Sourcepub fn find_segments(&self, segment_id: &str) -> Vec<&'a OwnedSegment>
pub fn find_segments(&self, segment_id: &str) -> Vec<&'a OwnedSegment>
Find all segments with the given ID.
Sourcepub fn find_segments_with_qualifier(
&self,
segment_id: &str,
element_index: usize,
qualifier: &str,
) -> Vec<&'a OwnedSegment>
pub fn find_segments_with_qualifier( &self, segment_id: &str, element_index: usize, qualifier: &str, ) -> Vec<&'a OwnedSegment>
Find segments with a specific qualifier value on a given element.
Sourcepub fn scoped_find_segments(&self, tag: &str) -> Vec<OwnedSegment>
pub fn scoped_find_segments(&self, tag: &str) -> Vec<OwnedSegment>
Find segments tag within the current scope, or message-wide if no
scope is set. Scope + navigator required for per-instance narrowing;
falls back to find_segments when either is absent.
Returns owned OwnedSegment values because
GroupNavigator::find_segments_in_group returns owned values — the
navigator may synthesise segments from an assembled tree rather than
borrowing from ctx.segments. Callers that only need a &OwnedSegment
should use find_segments directly when they know message-wide is correct.
Sourcepub fn scoped_find_segments_with_qualifier(
&self,
tag: &str,
element_index: usize,
qualifier: &str,
) -> Vec<OwnedSegment>
pub fn scoped_find_segments_with_qualifier( &self, tag: &str, element_index: usize, qualifier: &str, ) -> Vec<OwnedSegment>
Find segments tag with a qualifier value on element element_index,
narrowed to the current scope or message-wide.
Sourcepub fn format_check(
&self,
tag: &str,
elem: usize,
comp: usize,
validate: impl FnOnce(&str) -> ConditionResult,
) -> ConditionResult
pub fn format_check( &self, tag: &str, elem: usize, comp: usize, validate: impl FnOnce(&str) -> ConditionResult, ) -> ConditionResult
Validate a format condition on the current field value.
Prefers resolved_value (exact field from tree-based validation).
Falls back to searching segments by tag and extracting elements[elem][comp],
but only when exactly one segment of that tag exists — otherwise the
fallback would pick an arbitrary occurrence and validate the wrong field.
Returns ConditionResult::Unknown when no unambiguous value is available.
Usage: ctx.format_check("DTM", 0, 1, validate_timezone_utc)
Sourcepub fn format_check_qualified(
&self,
tag: &str,
qual_elem: usize,
qualifier: &str,
val_elem: usize,
val_comp: usize,
validate: impl FnOnce(&str) -> ConditionResult,
) -> ConditionResult
pub fn format_check_qualified( &self, tag: &str, qual_elem: usize, qualifier: &str, val_elem: usize, val_comp: usize, validate: impl FnOnce(&str) -> ConditionResult, ) -> ConditionResult
Validate a format condition with qualifier-filtered segment fallback.
Like format_check, but the fallback searches for segments matching
tag where elements[qual_elem][0] == qualifier, then extracts
elements[val_elem][val_comp]. Returns Unknown when the qualifier
filter doesn’t narrow down to exactly one segment.
Usage: ctx.format_check_qualified("DTM", 0, "163", 0, 1, |v| validate_hhmm_equals(v, "2200"))
Sourcepub fn self_segment_value(
&self,
tag: &str,
elem: usize,
comp: usize,
) -> Option<&str>
pub fn self_segment_value( &self, tag: &str, elem: usize, comp: usize, ) -> Option<&str>
Read a value at elements[elem][comp] of the segment currently being
validated (“im selben Segment” conditions).
Prefers resolved_segment (the exact segment the field lives in,
bound per-field during tree-based validation). Falls back to the
first message-wide segment matching tag when no tree context is
available — matches the prior find_segments(tag).first() behavior.
Conditions phrased “Wenn im selben Segment im DE2379 der Code 303
vorhanden ist” should use this instead of iterating every tag
segment in the message, which finds spurious matches elsewhere.
Sourcepub fn self_segment_value_equals(
&self,
tag: &str,
elem: usize,
comp: usize,
expected: &str,
) -> bool
pub fn self_segment_value_equals( &self, tag: &str, elem: usize, comp: usize, expected: &str, ) -> bool
Convenience wrapper — returns true when self_segment_value equals
expected. The common shape for “im selben Segment” code-equality
conditions.
Sourcepub fn has_segment(&self, segment_id: &str) -> bool
pub fn has_segment(&self, segment_id: &str) -> bool
Check if a segment with the given ID exists.
Sourcepub fn count_sg8_obis_matches<F>(&self, obis_pred: F) -> usize
pub fn count_sg8_obis_matches<F>(&self, obis_pred: F) -> usize
Count SG8 instances whose PIA+5 OBIS code satisfies obis_pred,
restricted to SG8 instances with:
- SEQ qualifier (DE1229) == current SEQ’s qualifier, AND
- SEQ DE1050 (Zeitraum-ID in C286) == current SEQ’s DE1050.
“Current” is taken from resolved_segment (tree-based validation),
which must point at a SEQ segment with a qualifier in
{Z02, ZA1, ZA2} (OBIS-Daten der Marktlokation). Returns 0 when the
context isn’t anchored to such a SEQ.
Used by UTILMD_Strom conditions [413]/[414]/[415]/[416] which ask “does another SG8 with matching Zeitraum-ID + qualifier have a Wirkarbeit kumuliert / 1/4 Stunde OBIS?”
Implementation: linear scan of self.segments. Each encountered SEQ
defines the “current group’s” matching state; subsequent PIA+5
segments (before the next SEQ) belong to that SG8 instance and are
tested against obis_pred when the SEQ matches current’s qualifier +
Zeitraum-ID. This relies on the standard EDIFACT group layout where
SEQ is SG8’s entry segment and child segments (SG9 PIA …) appear
consecutively after it.
Sourcepub fn find_segments_in_group(
&self,
segment_id: &str,
group_path: &[&str],
instance_index: usize,
) -> Vec<OwnedSegment>
pub fn find_segments_in_group( &self, segment_id: &str, group_path: &[&str], instance_index: usize, ) -> Vec<OwnedSegment>
Find all segments with the given tag within a specific group instance. Returns empty if no navigator is set.
Sourcepub fn find_segments_with_qualifier_in_group(
&self,
segment_id: &str,
element_index: usize,
qualifier: &str,
group_path: &[&str],
instance_index: usize,
) -> Vec<OwnedSegment>
pub fn find_segments_with_qualifier_in_group( &self, segment_id: &str, element_index: usize, qualifier: &str, group_path: &[&str], instance_index: usize, ) -> Vec<OwnedSegment>
Find segments matching a tag + qualifier within a group instance. Returns empty if no navigator is set.
Sourcepub fn has_segment_in_group(
&self,
segment_id: &str,
group_path: &[&str],
instance_index: usize,
) -> bool
pub fn has_segment_in_group( &self, segment_id: &str, group_path: &[&str], instance_index: usize, ) -> bool
Check if a segment exists in a specific group instance. Returns false if no navigator is set.
Sourcepub fn group_instance_count(&self, group_path: &[&str]) -> usize
pub fn group_instance_count(&self, group_path: &[&str]) -> usize
Count repetitions of a group at the given path. Returns 0 if no navigator is set.
Sourcepub fn child_group_instance_count(
&self,
parent_path: &[&str],
parent_instance: usize,
child_group_id: &str,
) -> usize
pub fn child_group_instance_count( &self, parent_path: &[&str], parent_instance: usize, child_group_id: &str, ) -> usize
Count child group repetitions within a specific parent group instance. Returns 0 if no navigator is set.
Sourcepub fn find_segments_in_child_group(
&self,
segment_id: &str,
parent_path: &[&str],
parent_instance: usize,
child_group_id: &str,
child_instance: usize,
) -> Vec<OwnedSegment>
pub fn find_segments_in_child_group( &self, segment_id: &str, parent_path: &[&str], parent_instance: usize, child_group_id: &str, child_instance: usize, ) -> Vec<OwnedSegment>
Find segments in a child group within a specific parent group instance. Returns empty if no navigator is set.
Sourcepub fn extract_value_in_group(
&self,
segment_id: &str,
element_index: usize,
component_index: usize,
group_path: &[&str],
instance_index: usize,
) -> Option<String>
pub fn extract_value_in_group( &self, segment_id: &str, element_index: usize, component_index: usize, group_path: &[&str], instance_index: usize, ) -> Option<String>
Extract a single value from the first matching segment in a group instance. Returns None if no navigator is set or value not found.
Sourcepub fn has_qualifier(
&self,
tag: &str,
element_index: usize,
qualifier: &str,
) -> ConditionResult
pub fn has_qualifier( &self, tag: &str, element_index: usize, qualifier: &str, ) -> ConditionResult
Check if any segment with the given tag + qualifier exists (message-wide).
Returns True if found, False if not.
Sourcepub fn lacks_qualifier(
&self,
tag: &str,
element_index: usize,
qualifier: &str,
) -> ConditionResult
pub fn lacks_qualifier( &self, tag: &str, element_index: usize, qualifier: &str, ) -> ConditionResult
Check if a segment with given tag + qualifier does NOT exist (message-wide).
Returns True if absent, False if present.
Sourcepub fn has_qualified_value(
&self,
tag: &str,
qual_elem: usize,
qualifier: &str,
value_elem: usize,
value_comp: usize,
values: &[&str],
) -> ConditionResult
pub fn has_qualified_value( &self, tag: &str, qual_elem: usize, qualifier: &str, value_elem: usize, value_comp: usize, values: &[&str], ) -> ConditionResult
Check if any segment with the given tag + qualifier has a specific sub-element value.
Finds segments matching tag with elements[qual_elem][0] == qualifier,
then checks if elements[value_elem][value_comp] matches any of values.
Sourcepub fn any_group_has_qualifier(
&self,
tag: &str,
element_index: usize,
qualifier: &str,
group_path: &[&str],
) -> ConditionResult
pub fn any_group_has_qualifier( &self, tag: &str, element_index: usize, qualifier: &str, group_path: &[&str], ) -> ConditionResult
Group-scoped qualifier existence check with message-wide fallback.
Checks if any group instance at group_path contains a segment matching
tag with elements[element_index][0] == qualifier. Falls back to
message-wide search if no group navigator is available.
Sourcepub fn any_group_has_any_qualifier(
&self,
tag: &str,
element_index: usize,
qualifiers: &[&str],
group_path: &[&str],
) -> ConditionResult
pub fn any_group_has_any_qualifier( &self, tag: &str, element_index: usize, qualifiers: &[&str], group_path: &[&str], ) -> ConditionResult
Group-scoped segment existence check (any tag match, no qualifier).
Checks if any group instance at group_path contains a segment with
elements[element_index][0] matching any of qualifiers. Falls back
to message-wide search if no group navigator is available.
Sourcepub fn any_group_has_qualified_value(
&self,
tag: &str,
qual_elem: usize,
qualifier: &str,
value_elem: usize,
value_comp: usize,
values: &[&str],
group_path: &[&str],
) -> ConditionResult
pub fn any_group_has_qualified_value( &self, tag: &str, qual_elem: usize, qualifier: &str, value_elem: usize, value_comp: usize, values: &[&str], group_path: &[&str], ) -> ConditionResult
Group-scoped check for sub-element value within qualified segments.
For each group instance at group_path, checks if a segment matching
tag with elements[qual_elem][0] == qualifier has
elements[value_elem][value_comp] in values. Falls back to message-wide.
Sourcepub fn filtered_parent_child_has_qualifier(
&self,
parent_path: &[&str],
parent_tag: &str,
parent_elem: usize,
parent_qual: &str,
child_group_id: &str,
child_tag: &str,
child_elem: usize,
child_qual: &str,
) -> ConditionResult
pub fn filtered_parent_child_has_qualifier( &self, parent_path: &[&str], parent_tag: &str, parent_elem: usize, parent_qual: &str, child_group_id: &str, child_tag: &str, child_elem: usize, child_qual: &str, ) -> ConditionResult
Pattern A: Check if parent group instances matching a qualifier have a child group containing a specific qualifier.
Example: “In the SG8 with SEQ+Z98, does its SG10 child have CCI+Z23?”
Falls back to message-wide search if no navigator is available.
Sourcepub fn any_group_has_qualifier_without(
&self,
present_tag: &str,
present_elem: usize,
present_qual: &str,
absent_tag: &str,
absent_elem: usize,
absent_qual: &str,
group_path: &[&str],
) -> ConditionResult
pub fn any_group_has_qualifier_without( &self, present_tag: &str, present_elem: usize, present_qual: &str, absent_tag: &str, absent_elem: usize, absent_qual: &str, group_path: &[&str], ) -> ConditionResult
Pattern B: Check if any group instance has one qualifier present but another absent.
Example: “In any SG8, SEQ+Z59 is present but CCI+11 is absent”
Falls back to message-wide search if no navigator is available.
Sourcepub fn collect_group_values(
&self,
tag: &str,
elem: usize,
comp: usize,
group_path: &[&str],
) -> Vec<(usize, String)>
pub fn collect_group_values( &self, tag: &str, elem: usize, comp: usize, group_path: &[&str], ) -> Vec<(usize, String)>
Pattern C helper: Collect all values at a specific element+component across group instances.
Returns (instance_index, value) pairs for non-empty values.
Pattern C: Check if a value from one group path matches a value in another group path.
Example: “Zeitraum-ID in SG6 RFF+Z49 matches reference in SG8 SEQ.c286”
Finds qualified segments in source_path, extracts their value, then checks if any instance in target_path has the same value at the target position.
Falls back to message-wide search if no navigator is available.
Sourcepub fn any_group_has_co_occurrence(
&self,
tag_a: &str,
elem_a: usize,
quals_a: &[&str],
tag_b: &str,
elem_b: usize,
comp_b: usize,
vals_b: &[&str],
group_path: &[&str],
) -> ConditionResult
pub fn any_group_has_co_occurrence( &self, tag_a: &str, elem_a: usize, quals_a: &[&str], tag_b: &str, elem_b: usize, comp_b: usize, vals_b: &[&str], group_path: &[&str], ) -> ConditionResult
Group-scoped co-occurrence check: two segment conditions must both be true in the same group instance.
For each group instance, checks that:
- A segment with
tag_ahaselements[elem_a][0]inquals_a - A segment with
tag_bhaselements[elem_b][comp_b]invals_b
Falls back to message-wide search.
Sourcepub fn has_segment_matching(
&self,
tag: &str,
checks: &[(usize, usize, &str)],
) -> ConditionResult
pub fn has_segment_matching( &self, tag: &str, checks: &[(usize, usize, &str)], ) -> ConditionResult
Check if any segment with the given tag has ALL specified element/component values.
Each check is (element_index, component_index, expected_value).
Returns True if a matching segment exists, False if segments exist but none match,
Unknown if no segments with the tag exist.
Example: ctx.has_segment_matching("STS", &[(0, 0, "Z20"), (1, 0, "Z32"), (2, 0, "A99")])
Sourcepub fn has_segment_matching_in_group(
&self,
tag: &str,
checks: &[(usize, usize, &str)],
group_path: &[&str],
) -> ConditionResult
pub fn has_segment_matching_in_group( &self, tag: &str, checks: &[(usize, usize, &str)], group_path: &[&str], ) -> ConditionResult
Group-scoped multi-element match with message-wide fallback.
Checks if any group instance at group_path contains a segment with tag
where ALL element/component checks match.
Sourcepub fn dtm_ge(&self, qualifier: &str, threshold: &str) -> ConditionResult
pub fn dtm_ge(&self, qualifier: &str, threshold: &str) -> ConditionResult
Check if a DTM segment with the given qualifier has a value >= threshold.
Both the DTM value and threshold should be in EDIFACT format 303 (CCYYMMDDHHMM).
Returns Unknown if no DTM with the qualifier exists.
Sourcepub fn dtm_lt(&self, qualifier: &str, threshold: &str) -> ConditionResult
pub fn dtm_lt(&self, qualifier: &str, threshold: &str) -> ConditionResult
Check if a DTM segment with the given qualifier has a value < threshold.
Sourcepub fn dtm_le(&self, qualifier: &str, threshold: &str) -> ConditionResult
pub fn dtm_le(&self, qualifier: &str, threshold: &str) -> ConditionResult
Check if a DTM segment with the given qualifier has a value <= threshold.
Sourcepub fn count_qualified_in_group(
&self,
tag: &str,
element_index: usize,
qualifier: &str,
group_path: &[&str],
) -> usize
pub fn count_qualified_in_group( &self, tag: &str, element_index: usize, qualifier: &str, group_path: &[&str], ) -> usize
Count segments matching tag + qualifier within a group path (across all instances).
Returns the total count across all group instances. Falls back to message-wide count if no navigator.
Sourcepub fn count_in_group(&self, tag: &str, group_path: &[&str]) -> usize
pub fn count_in_group(&self, tag: &str, group_path: &[&str]) -> usize
Count segments matching tag (any qualifier) within a group path.