Skip to main content

mig_types/schema/
common.rs

1use serde::{Deserialize, Serialize};
2
3/// Cardinality of a segment or group in the MIG.
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
5pub enum Cardinality {
6    /// Mandatory (M) — must appear exactly once or as specified.
7    Mandatory,
8    /// Required (R) — must appear.
9    Required,
10    /// Dependent (D) — depends on other segments.
11    Dependent,
12    /// Optional (O) — may appear.
13    Optional,
14    /// Not used (N) — must not appear.
15    NotUsed,
16    /// Conditional (C) — conditional on context.
17    Conditional,
18}
19
20impl Cardinality {
21    /// Parse from a status string (e.g., "M", "C", "R", "D", "O", "N").
22    pub fn from_status(status: &str) -> Self {
23        match status.trim() {
24            "M" => Cardinality::Mandatory,
25            "R" => Cardinality::Required,
26            "D" => Cardinality::Dependent,
27            "O" => Cardinality::Optional,
28            "N" => Cardinality::NotUsed,
29            "C" => Cardinality::Conditional,
30            _ => Cardinality::Conditional, // Default for unknown
31        }
32    }
33
34    /// Whether this cardinality means the element is required.
35    pub fn is_required(&self) -> bool {
36        matches!(self, Cardinality::Mandatory | Cardinality::Required)
37    }
38}
39
40/// An allowed code value for a data element.
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42pub struct CodeDefinition {
43    /// The code value (e.g., "ORDERS", "E40").
44    pub value: String,
45    /// Human-readable name of the code.
46    pub name: String,
47    /// Optional description.
48    pub description: Option<String>,
49}
50
51/// EDIFACT data element format type.
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53pub enum EdifactDataType {
54    Alphabetic,
55    Numeric,
56    Alphanumeric,
57}
58
59/// Parsed EDIFACT format specification (e.g., "an..35", "n13").
60#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
61pub struct EdifactFormat {
62    pub data_type: EdifactDataType,
63    /// Minimum length (None for variable-length formats like "an..35").
64    pub min_length: Option<usize>,
65    /// Maximum length.
66    pub max_length: usize,
67}
68
69impl EdifactFormat {
70    /// Parse an EDIFACT format string (e.g., "an..35", "n13", "a3").
71    pub fn parse(format: &str) -> Option<Self> {
72        let format = format.trim();
73        if format.is_empty() {
74            return None;
75        }
76
77        // Regex-free parsing: extract type prefix, optional "..", and length
78        let (type_str, rest) = if let Some(rest) = format.strip_prefix("an") {
79            ("an", rest)
80        } else if let Some(rest) = format.strip_prefix('a') {
81            ("a", rest)
82        } else if let Some(rest) = format.strip_prefix('n') {
83            ("n", rest)
84        } else {
85            return None;
86        };
87
88        let data_type = match type_str {
89            "a" => EdifactDataType::Alphabetic,
90            "n" => EdifactDataType::Numeric,
91            "an" => EdifactDataType::Alphanumeric,
92            _ => return None,
93        };
94
95        let (is_variable, length_str) = if let Some(stripped) = rest.strip_prefix("..") {
96            (true, stripped)
97        } else {
98            (false, rest)
99        };
100
101        let max_length: usize = length_str.parse().ok()?;
102        let min_length = if is_variable { None } else { Some(max_length) };
103
104        Some(EdifactFormat {
105            data_type,
106            min_length,
107            max_length,
108        })
109    }
110}