Skip to main content

edi_energy/
agency_code.rs

1/// EDIFACT DE 3055 — code list responsible agency for market participant identification.
2///
3/// Used in NAD segments (C082 component 2) and IDE segments to indicate which body
4/// issued the party identifier code. Choosing the wrong agency code produces
5/// non-conformant EDIFACT that receiving parties may reject.
6///
7/// In the **German energy market (BDEW `MaKo` / EDI@Energy)** the dominant code is
8/// [`AgencyCode::Bdew`] (`"293"`). Nearly all supplier, DSO, and MSB codes are
9/// issued and administered by BDEW and carry this agency qualifier — even when the
10/// 13-digit number is also a valid GS1 GLN (BDEW is a GS1 member prefix holder).
11///
12/// # Wire format
13///
14/// The agency code appears as the third component of the NAD C082 composite:
15///
16/// ```text
17/// NAD+MS+{party_id}::{agency_code}'
18/// ```
19///
20/// The middle component (code list id, C082/1154) is always empty in EDI@Energy.
21///
22/// # Example
23///
24/// ```rust
25/// use edi_energy::AgencyCode;
26///
27/// assert_eq!(AgencyCode::Bdew.as_str(), "293");
28/// assert_eq!(AgencyCode::Gs1.as_str(),  "9");
29/// assert_eq!(AgencyCode::Entso.as_str(), "305");
30///
31/// // Parse from a raw NAD segment agency string.
32/// assert_eq!(AgencyCode::parse("293"), Some(AgencyCode::Bdew));
33/// assert_eq!(AgencyCode::parse("999"), None);
34/// ```
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36#[non_exhaustive]
37pub enum AgencyCode {
38    /// `"293"` — BDEW (Bundesverband der Energie- und Wasserwirtschaft).
39    ///
40    /// Used for all BDEW-issued market participant codes in the German electricity
41    /// and gas markets. This is the correct agency code for suppliers (LFN),
42    /// distribution system operators (NB/VNB), metering point operators (MSB),
43    /// and balance responsible parties (BKV/BRK) registered in the BDEW
44    /// Marktteilnehmerverzeichnis.
45    ///
46    /// # NAD wire form
47    ///
48    /// ```text
49    /// NAD+MS+9900123456789::293'
50    /// ```
51    Bdew,
52
53    /// `"9"` — GS1 (formerly EAN International).
54    ///
55    /// Used when a 13-digit GLN is issued directly under GS1's global prefix
56    /// scheme rather than through BDEW. Rare in German `MaKo` practice — most
57    /// operators use [`AgencyCode::Bdew`] even for GS1-compatible numbers.
58    Gs1,
59
60    /// `"305"` — ECOD (ENTSO-E Coding Scheme / European network operator).
61    ///
62    /// Used for 16-character EIC (Energy Identification Codes) issued by
63    /// ENTSO-E. Required for transmission grid operators (ÜNB/TSO), balance
64    /// zones (Regelzonen), and cross-border market participants.
65    ///
66    /// # NAD wire form
67    ///
68    /// ```text
69    /// NAD+MS+10XDE-EON-NETZ--I::305'
70    /// ```
71    Entso,
72
73    /// `"332"` — DVGW (Deutscher Verein des Gas- und Wasserfaches).
74    ///
75    /// Occasionally used for gas-sector participants registered in the DVGW
76    /// system before the BDEW merger. Modern gas market messages prefer
77    /// [`AgencyCode::Bdew`]; this variant is kept for parsing legacy messages.
78    Dvgw,
79}
80
81impl AgencyCode {
82    /// Default agency code for new outbound EDI@Energy messages.
83    ///
84    /// `293` (BDEW) is the correct default for all standard German market
85    /// participants. Change to [`AgencyCode::Entso`] only for TSO/ÜNB parties
86    /// that carry an EIC code.
87    pub const DEFAULT: Self = Self::Bdew;
88
89    /// Return the wire-format string for the DE 3055 component.
90    #[must_use]
91    pub fn as_str(self) -> &'static str {
92        match self {
93            Self::Bdew => "293",
94            Self::Gs1 => "9",
95            Self::Entso => "305",
96            Self::Dvgw => "332",
97        }
98    }
99
100    /// Parse a DE 3055 agency code string.
101    ///
102    /// Returns `None` for unrecognised codes; callers may fall back to
103    /// treating the raw string as an opaque pass-through.
104    #[must_use]
105    pub fn parse(s: &str) -> Option<Self> {
106        match s {
107            "293" => Some(Self::Bdew),
108            "9" => Some(Self::Gs1),
109            "305" => Some(Self::Entso),
110            "332" => Some(Self::Dvgw),
111            _ => None,
112        }
113    }
114}
115
116impl Default for AgencyCode {
117    fn default() -> Self {
118        Self::DEFAULT
119    }
120}
121
122impl std::fmt::Display for AgencyCode {
123    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124        f.write_str(self.as_str())
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn bdew_is_default() {
134        assert_eq!(AgencyCode::default(), AgencyCode::Bdew);
135        assert_eq!(AgencyCode::DEFAULT.as_str(), "293");
136    }
137
138    #[test]
139    fn round_trip_from_str() {
140        for (code, variant) in [
141            ("293", AgencyCode::Bdew),
142            ("9", AgencyCode::Gs1),
143            ("305", AgencyCode::Entso),
144            ("332", AgencyCode::Dvgw),
145        ] {
146            assert_eq!(AgencyCode::parse(code), Some(variant));
147            assert_eq!(variant.as_str(), code);
148        }
149        assert_eq!(AgencyCode::parse("999"), None);
150    }
151}