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}