Skip to main content

mig_bo4e/
segment_structure.rs

1//! MIG-aware segment structure lookup.
2//!
3//! Provides the expected element count for each segment tag, derived from the MIG schema.
4//! Used by `MappingEngine::map_reverse` to pad trailing empty elements so that
5//! reconstructed EDIFACT segments match the original structure.
6
7use std::collections::HashMap;
8
9use mig_types::schema::mig::{MigSchema, MigSegment, MigSegmentGroup};
10
11/// Maps segment tags to their expected element count from the MIG schema.
12///
13/// Element count = `data_elements.len() + composites.len()` for each segment.
14/// This matches the EDIFACT convention where each data element or composite
15/// occupies one element position separated by `+`.
16#[derive(Clone, serde::Serialize, serde::Deserialize)]
17pub struct SegmentStructure {
18    pub(crate) element_counts: HashMap<String, usize>,
19}
20
21impl SegmentStructure {
22    /// Build from a MIG schema by walking all segments (top-level and within groups).
23    ///
24    /// First occurrence of a segment tag wins — the same tag always has the same
25    /// element structure in EDIFACT.
26    pub fn from_mig(mig: &MigSchema) -> Self {
27        let mut element_counts = HashMap::new();
28
29        // Top-level segments
30        for seg in &mig.segments {
31            Self::register_segment(&mut element_counts, seg);
32        }
33
34        // Segments within groups (recursive)
35        for group in &mig.segment_groups {
36            Self::walk_group(&mut element_counts, group);
37        }
38
39        Self { element_counts }
40    }
41
42    /// Look up the expected element count for a segment tag.
43    pub fn element_count(&self, tag: &str) -> Option<usize> {
44        self.element_counts.get(&tag.to_uppercase()).copied()
45    }
46
47    fn register_segment(counts: &mut HashMap<String, usize>, seg: &MigSegment) {
48        let tag = seg.id.to_uppercase();
49        counts
50            .entry(tag)
51            .or_insert_with(|| seg.data_elements.len() + seg.composites.len());
52    }
53
54    fn walk_group(counts: &mut HashMap<String, usize>, group: &MigSegmentGroup) {
55        for seg in &group.segments {
56            Self::register_segment(counts, seg);
57        }
58        for nested in &group.nested_groups {
59            Self::walk_group(counts, nested);
60        }
61    }
62}