1use chrono::{Local, NaiveDate};
2use serde::Serialize;
3
4use crate::{Money, minivec::MiniVec};
5
6#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)]
7#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
8pub enum TaxAppliedBy {
9 KwhConsumed,
10}
11
12type TaxesInner = MiniVec<&'static Tax, 2>;
14
15#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)]
16#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
17pub struct Taxes(TaxesInner);
18
19pub(crate) struct TaxesBuilder {
20 t: TaxesInner,
21}
22
23impl TaxesBuilder {
24 pub(crate) const fn new() -> Self {
25 Self {
26 t: TaxesInner::new(),
27 }
28 }
29
30 pub(crate) const fn add(mut self, t: &'static Tax) -> Self {
31 self.t.push(t);
32 self
33 }
34
35 pub(crate) const fn build(self) -> Taxes {
36 Taxes(self.t)
37 }
38}
39
40impl Taxes {
41 pub fn for_date(&self, today: NaiveDate) -> Vec<Tax> {
43 self.0
44 .iter()
45 .filter(|tax| tax.valid_for(today))
46 .copied()
47 .collect()
48 }
49
50 pub fn kwh_total(&self, today: NaiveDate) -> Money {
52 self.for_date(today)
53 .into_iter()
54 .filter(|tax| tax.is_kwh_based())
55 .map(|tax| tax.amount())
56 .sum()
57 }
58
59 pub fn with_current_only(mut self) -> Taxes {
60 let today = Local::now().date_naive();
61 self.0 = self.0.iter().filter(|tax| tax.valid_for(today)).collect();
62 self
63 }
64}
65
66#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)]
67#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
68pub struct Tax {
69 description: &'static str,
70 amount: Money,
71 applied_by: TaxAppliedBy,
72 from_date: NaiveDate,
73 to_date: Option<NaiveDate>,
75}
76
77impl Tax {
78 pub(crate) const fn new(
79 description: &'static str,
80 amount: Money,
81 applied_by: TaxAppliedBy,
82 from_date: NaiveDate,
83 to_date: Option<NaiveDate>,
84 ) -> Self {
85 Self {
86 description,
87 amount,
88 applied_by,
89 from_date,
90 to_date,
91 }
92 }
93
94 pub fn description(&self) -> &'static str {
95 self.description
96 }
97
98 pub fn amount(&self) -> Money {
99 self.amount
100 }
101
102 pub(crate) fn valid_for(&self, today: NaiveDate) -> bool {
103 today >= self.from_date && self.to_date.is_none_or(|to_date| today <= to_date)
104 }
105
106 pub(crate) fn is_kwh_based(&self) -> bool {
107 matches!(self.applied_by, TaxAppliedBy::KwhConsumed)
108 }
109
110 pub(crate) const fn reduce_by(mut self, reduce_amount: Money) -> Self {
112 self.amount = self.amount.reduce_by(reduce_amount);
113 self
114 }
115}