grid_tariffs/
country.rs

1use core::fmt;
2use std::str::FromStr;
3
4use chrono::{NaiveDate, Utc};
5use serde::{Deserialize, Serialize};
6
7use crate::{Money, SE_TAX_REDUCTIONS, SE_TAXES, Tax, TaxAppliedBy, TaxReduction, helpers::date};
8
9#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
10#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
11pub enum Country {
12    SE,
13}
14
15impl Country {
16    pub const fn all() -> &'static [Self] {
17        &[Country::SE]
18    }
19
20    pub const fn taxes(&self) -> &'static [Tax] {
21        match self {
22            Country::SE => SE_TAXES,
23        }
24    }
25
26    pub const fn tax_reductions(&self) -> &'static [TaxReduction] {
27        match self {
28            Country::SE => SE_TAX_REDUCTIONS,
29        }
30    }
31
32    pub fn current_taxes(&self, today: NaiveDate) -> Vec<Tax> {
33        self.taxes()
34            .iter()
35            .filter(|tax| tax.valid_for(today))
36            .copied()
37            .collect()
38    }
39
40    /// Tax total for the given date
41    pub fn kwh_tax_total(&self, today: NaiveDate) -> Money {
42        self.current_taxes(today)
43            .into_iter()
44            .filter(|tax| tax.is_kwh_based())
45            .map(|tax| tax.amount())
46            .sum()
47    }
48
49    pub fn current_tax_reductions(&self, today: NaiveDate) -> Vec<TaxReduction> {
50        self.tax_reductions()
51            .iter()
52            .filter(|tax| tax.valid_for(today))
53            .copied()
54            .collect()
55    }
56
57    /// Tax reduction total for the given date
58    pub fn kwh_tax_reduction_total(&self, today: NaiveDate) -> Money {
59        self.current_tax_reductions(today)
60            .into_iter()
61            .filter(|tr| tr.is_kwh_based())
62            .map(|tr| tr.amount())
63            .sum()
64    }
65
66    pub(crate) const fn vat_rate(&self) -> f64 {
67        match self {
68            Country::SE => 1.25,
69        }
70    }
71
72    pub(crate) const fn add_vat(&self, value: f64) -> f64 {
73        value * self.vat_rate()
74    }
75
76    pub(crate) fn is_holiday(&self, date_naive: NaiveDate) -> bool {
77        SE_HOLIDAYS.contains(&date_naive)
78    }
79}
80
81impl Country {
82    pub fn code(&self) -> &'static str {
83        match self {
84            Country::SE => "SE",
85        }
86    }
87
88    pub fn english_name(&self) -> &'static str {
89        match self {
90            Country::SE => "Sweden",
91        }
92    }
93}
94
95impl FromStr for Country {
96    type Err = &'static str;
97
98    fn from_str(s: &str) -> Result<Self, Self::Err> {
99        match s.to_ascii_uppercase().as_ref() {
100            "SE" => Ok(Country::SE),
101            _ => Err("no such country"),
102        }
103    }
104}
105
106impl fmt::Display for Country {
107    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108        f.write_str(match self {
109            Country::SE => "SE",
110        })
111    }
112}
113
114#[derive(Debug, Serialize)]
115pub struct CountryInfo {
116    country: Country,
117    /// Taxes applied in this country
118    taxes: Vec<Tax>,
119    /// Tax reductions applied in this country
120    tax_reductions: Vec<TaxReduction>,
121}
122
123impl CountryInfo {
124    pub fn current(country: Country) -> Self {
125        let today = Utc::now().date_naive();
126        Self {
127            country,
128            taxes: country.current_taxes(today),
129            tax_reductions: country.current_tax_reductions(today),
130        }
131    }
132}
133
134impl From<Country> for CountryInfo {
135    fn from(country: Country) -> Self {
136        let taxes = country.taxes().to_vec();
137        let tax_reductions = country.tax_reductions().to_vec();
138        Self {
139            country,
140            taxes,
141            tax_reductions,
142        }
143    }
144}
145
146static SE_HOLIDAYS: &[NaiveDate] = &[
147    date(2025, 1, 1),
148    date(2025, 1, 6),
149    date(2025, 4, 18),
150    date(2025, 4, 20),
151    date(2025, 4, 21),
152    date(2025, 5, 1),
153    date(2025, 5, 29),
154    date(2025, 6, 6),
155    date(2025, 6, 8),
156    date(2025, 6, 21),
157    date(2025, 11, 1),
158    date(2025, 12, 24),
159    date(2025, 12, 25),
160    date(2025, 12, 26),
161    date(2025, 12, 31),
162    date(2026, 1, 1),
163    date(2026, 1, 6),
164    date(2026, 4, 3),
165    date(2026, 4, 5),
166    date(2026, 4, 6),
167    date(2026, 5, 1),
168    date(2026, 5, 14),
169    date(2026, 5, 24),
170    date(2026, 6, 6),
171    date(2026, 6, 20),
172    date(2026, 10, 31),
173    date(2026, 12, 24),
174    date(2026, 12, 25),
175    date(2026, 12, 26),
176    date(2026, 12, 31),
177    date(2027, 1, 1),
178    date(2027, 1, 6),
179    date(2027, 3, 26),
180    date(2027, 3, 28),
181    date(2027, 3, 29),
182    date(2027, 5, 1),
183    date(2027, 5, 6),
184    date(2027, 5, 16),
185    date(2027, 6, 6),
186    date(2027, 6, 26),
187    date(2027, 11, 6),
188    date(2027, 12, 24),
189    date(2027, 12, 25),
190    date(2027, 12, 26),
191    date(2027, 12, 31),
192    date(2028, 1, 1),
193    date(2028, 1, 6),
194    date(2028, 4, 14),
195    date(2028, 4, 16),
196    date(2028, 4, 17),
197    date(2028, 5, 1),
198    date(2028, 5, 25),
199    date(2028, 6, 4),
200    date(2028, 6, 6),
201    date(2028, 6, 24),
202    date(2028, 11, 4),
203    date(2028, 12, 24),
204    date(2028, 12, 25),
205    date(2028, 12, 26),
206    date(2028, 12, 31),
207];