Skip to main content

automapper_validation/eval/
codelist.rs

1//! BDEW Artikel-ID / Artikelnummer codelist provider.
2//!
3//! Resolves UTILMD/PRICAT/INVOIC conditions that reference the
4//! "Codeliste der Artikelnummern und Artikel-ID" — conditions like
5//! [73] ("Wenn … ein Code aus der Codeliste … in der Spalte
6//! UTILMD/Preisangabe mit X gekennzeichnet ist").
7
8use serde::Deserialize;
9use std::collections::HashMap;
10use std::sync::OnceLock;
11
12const EMBEDDED_JSON: &str = include_str!("../../data/artikel_id_codelist.json");
13
14#[derive(Debug, Deserialize)]
15struct RawFile {
16    version: String,
17    codes: HashMap<String, RawEntry>,
18}
19
20#[derive(Debug, Deserialize)]
21struct RawEntry {
22    description: String,
23    einheit: String,
24    utilmd: MessageFlags,
25    pricat: MessageFlags,
26    invoic: MessageFlags,
27}
28
29#[derive(Debug, Clone, Copy, Deserialize)]
30pub struct MessageFlags {
31    pub codeverwendung: bool,
32    pub preisangabe: bool,
33}
34
35#[derive(Debug, Clone)]
36pub struct ArtikelIdEntry {
37    pub description: String,
38    pub einheit: String,
39    pub utilmd: MessageFlags,
40    pub pricat: MessageFlags,
41    pub invoic: MessageFlags,
42}
43
44#[derive(Debug, Clone, Copy)]
45pub enum CodelistColumn {
46    UtilmdCodeverwendung,
47    UtilmdPreisangabe,
48    PricatCodeverwendung,
49    PricatPreisangabe,
50    InvoicCodeverwendung,
51    InvoicPreisangabe,
52}
53
54#[derive(Debug)]
55pub struct ArtikelIdCodelist {
56    version: String,
57    codes: HashMap<String, ArtikelIdEntry>,
58}
59
60impl ArtikelIdCodelist {
61    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
62        let raw: RawFile = serde_json::from_str(json)?;
63        let codes = raw
64            .codes
65            .into_iter()
66            .map(|(k, v)| {
67                (
68                    k,
69                    ArtikelIdEntry {
70                        description: v.description,
71                        einheit: v.einheit,
72                        utilmd: v.utilmd,
73                        pricat: v.pricat,
74                        invoic: v.invoic,
75                    },
76                )
77            })
78            .collect();
79        Ok(Self {
80            version: raw.version,
81            codes,
82        })
83    }
84
85    pub fn embedded() -> &'static Self {
86        static CELL: OnceLock<ArtikelIdCodelist> = OnceLock::new();
87        CELL.get_or_init(|| {
88            ArtikelIdCodelist::from_json(EMBEDDED_JSON)
89                .expect("embedded artikel_id_codelist.json is malformed")
90        })
91    }
92
93    pub fn version(&self) -> &str {
94        &self.version
95    }
96
97    pub fn flag(&self, code: &str, column: CodelistColumn) -> Option<bool> {
98        let entry = self.codes.get(code)?;
99        use CodelistColumn::*;
100        Some(match column {
101            UtilmdCodeverwendung => entry.utilmd.codeverwendung,
102            UtilmdPreisangabe => entry.utilmd.preisangabe,
103            PricatCodeverwendung => entry.pricat.codeverwendung,
104            PricatPreisangabe => entry.pricat.preisangabe,
105            InvoicCodeverwendung => entry.invoic.codeverwendung,
106            InvoicPreisangabe => entry.invoic.preisangabe,
107        })
108    }
109
110    pub fn contains(&self, code: &str) -> bool {
111        self.codes.contains_key(code)
112    }
113}