Skip to main content

core_invoice/
bt.rs

1use std::fmt;
2
3/// Business term id `BT-151`.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub struct BtId(pub u16);
6
7impl fmt::Display for BtId {
8    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
9        write!(f, "BT-{}", self.0)
10    }
11}
12
13/// Repeating or distinguishable groups. Index in [`Path`] is **0-based**;
14/// `BG-25[2]/BT-151` is the third line.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16#[non_exhaustive]
17pub enum Group {
18    Document,
19    Seller,
20    Buyer,
21    Payee,
22    TaxRepresentative,
23    Delivery,
24    Payment,
25    DocumentAllowance,
26    DocumentCharge,
27    Totals,
28    /// Tax breakdown (BG-23 / IBG-23), not VAT-only.
29    TaxBreakdown,
30    Attachment,
31    Line,
32}
33
34impl Group {
35    pub fn bg_id(self) -> Option<u16> {
36        Some(match self {
37            Self::Document => return None,
38            Self::Seller => 4,
39            Self::Buyer => 7,
40            Self::Payee => 10,
41            Self::TaxRepresentative => 11,
42            Self::Delivery => 13,
43            Self::Payment => 16,
44            Self::DocumentAllowance => 20,
45            Self::DocumentCharge => 21,
46            Self::Totals => 22,
47            Self::TaxBreakdown => 23,
48            Self::Attachment => 24,
49            Self::Line => 25,
50        })
51    }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
55pub struct Path {
56    pub group: Group,
57    pub index: Option<usize>,
58    pub term: Option<BtId>,
59}
60
61impl Path {
62    pub fn term(term: BtId) -> Self {
63        Self {
64            group: Group::Document,
65            index: None,
66            term: Some(term),
67        }
68    }
69
70    pub fn at_term(group: Group, index: usize, term: BtId) -> Self {
71        Self {
72            group,
73            index: Some(index),
74            term: Some(term),
75        }
76    }
77
78    pub fn group(group: Group) -> Self {
79        Self {
80            group,
81            index: None,
82            term: None,
83        }
84    }
85
86    pub fn group_term(group: Group, term: BtId) -> Self {
87        Self {
88            group,
89            index: None,
90            term: Some(term),
91        }
92    }
93}
94
95impl fmt::Display for Path {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        match (self.group.bg_id(), self.index, self.term) {
98            (None, _, Some(t)) => write!(f, "{t}"),
99            (None, _, None) => write!(f, "Invoice"),
100            (Some(bg), None, None) => write!(f, "BG-{bg}"),
101            (Some(bg), Some(i), None) => write!(f, "BG-{bg}[{i}]"),
102            (Some(bg), None, Some(t)) => write!(f, "BG-{bg}/{t}"),
103            (Some(bg), Some(i), Some(t)) => write!(f, "BG-{bg}[{i}]/{t}"),
104        }
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn display_shapes() {
114        assert_eq!(Path::term(BtId(1)).to_string(), "BT-1");
115        assert_eq!(Path::group(Group::Totals).to_string(), "BG-22");
116        assert_eq!(
117            Path {
118                group: Group::Totals,
119                index: None,
120                term: Some(BtId(109))
121            }
122            .to_string(),
123            "BG-22/BT-109"
124        );
125        assert_eq!(Path::group(Group::Line).to_string(), "BG-25");
126        assert_eq!(
127            Path::at_term(Group::Line, 2, BtId(151)).to_string(),
128            "BG-25[2]/BT-151"
129        );
130    }
131}