brk_types/
dateindex.rs

1use std::{
2    fmt,
3    ops::{Add, Rem},
4};
5
6use brk_error::Error;
7use jiff::Span;
8use schemars::JsonSchema;
9use serde::{Deserialize, Serialize};
10use vecdb::{CheckedSub, Formattable, FromCoarserIndex, Pco, PrintableIndex};
11
12use crate::{DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, WeekIndex, YearIndex};
13
14use super::Date;
15
16#[derive(
17    Debug,
18    Default,
19    Clone,
20    Copy,
21    PartialEq,
22    Eq,
23    PartialOrd,
24    Ord,
25    Serialize,
26    Deserialize,
27    Pco,
28    JsonSchema,
29)]
30pub struct DateIndex(u16);
31
32impl DateIndex {
33    pub const BYTES: usize = size_of::<Self>();
34}
35
36impl From<DateIndex> for usize {
37    #[inline]
38    fn from(value: DateIndex) -> Self {
39        value.0 as usize
40    }
41}
42
43impl From<DateIndex> for u64 {
44    #[inline]
45    fn from(value: DateIndex) -> Self {
46        value.0 as u64
47    }
48}
49
50impl From<usize> for DateIndex {
51    #[inline]
52    fn from(value: usize) -> Self {
53        Self(value as u16)
54    }
55}
56
57impl From<DateIndex> for i64 {
58    #[inline]
59    fn from(value: DateIndex) -> Self {
60        value.0 as i64
61    }
62}
63
64impl Add<usize> for DateIndex {
65    type Output = Self;
66    fn add(self, rhs: usize) -> Self::Output {
67        Self(self.0 + rhs as u16)
68    }
69}
70
71impl TryFrom<Date> for DateIndex {
72    type Error = Error;
73    fn try_from(value: Date) -> Result<Self, Self::Error> {
74        let value_ = jiff::civil::Date::from(value);
75        if value_ < Date::INDEX_ZERO_ {
76            Err(Error::UnindexableDate)
77        } else if value == Date::INDEX_ZERO {
78            Ok(Self(0))
79        } else if value_ < Date::INDEX_ONE_ {
80            Err(Error::UnindexableDate)
81        } else if value == Date::INDEX_ONE {
82            Ok(Self(1))
83        } else {
84            Ok(Self(Date::INDEX_ONE_.until(value_)?.get_days() as u16 + 1))
85        }
86    }
87}
88
89impl CheckedSub for DateIndex {
90    fn checked_sub(self, rhs: Self) -> Option<Self> {
91        self.0.checked_sub(rhs.0).map(Self)
92    }
93}
94
95impl Rem<usize> for DateIndex {
96    type Output = Self;
97    fn rem(self, rhs: usize) -> Self::Output {
98        Self(self.0 % rhs as u16)
99    }
100}
101
102impl FromCoarserIndex<WeekIndex> for DateIndex {
103    fn min_from(coarser: WeekIndex) -> usize {
104        let coarser = usize::from(coarser);
105        if coarser == 0 {
106            0
107        } else if coarser == 1 {
108            1
109        } else {
110            4 + (coarser - 2) * 7
111        }
112    }
113
114    fn max_from_(coarser: WeekIndex) -> usize {
115        let coarser = usize::from(coarser);
116        if coarser == 0 {
117            0
118        } else if coarser == 1 {
119            3
120        } else {
121            3 + (coarser - 1) * 7
122        }
123    }
124}
125
126impl FromCoarserIndex<MonthIndex> for DateIndex {
127    fn min_from(coarser: MonthIndex) -> usize {
128        let coarser = u16::from(coarser);
129        if coarser == 0 {
130            0
131        } else {
132            let d = Date::new(2009, 1, 1)
133                .into_jiff()
134                .checked_add(Span::new().months(coarser))
135                .unwrap();
136            DateIndex::try_from(Date::from(d)).unwrap().into()
137        }
138    }
139
140    fn max_from_(coarser: MonthIndex) -> usize {
141        let d = Date::new(2009, 1, 31)
142            .into_jiff()
143            .checked_add(Span::new().months(u16::from(coarser)))
144            .unwrap();
145        DateIndex::try_from(Date::from(d)).unwrap().into()
146    }
147}
148
149impl FromCoarserIndex<QuarterIndex> for DateIndex {
150    fn min_from(coarser: QuarterIndex) -> usize {
151        let coarser = u16::from(coarser);
152        if coarser == 0 {
153            0
154        } else {
155            let d = Date::new(2009, 1, 1)
156                .into_jiff()
157                .checked_add(Span::new().months(3 * coarser))
158                .unwrap();
159            DateIndex::try_from(Date::from(d)).unwrap().into()
160        }
161    }
162
163    fn max_from_(coarser: QuarterIndex) -> usize {
164        let d = Date::new(2009, 3, 31)
165            .into_jiff()
166            .checked_add(Span::new().months(3 * u16::from(coarser)))
167            .unwrap();
168        DateIndex::try_from(Date::from(d)).unwrap().into()
169    }
170}
171
172impl FromCoarserIndex<SemesterIndex> for DateIndex {
173    fn min_from(coarser: SemesterIndex) -> usize {
174        let coarser = u16::from(coarser);
175        if coarser == 0 {
176            0
177        } else {
178            let d = Date::new(2009, 1, 1)
179                .into_jiff()
180                .checked_add(Span::new().months(6 * coarser))
181                .unwrap();
182            DateIndex::try_from(Date::from(d)).unwrap().into()
183        }
184    }
185
186    fn max_from_(coarser: SemesterIndex) -> usize {
187        let d = Date::new(2009, 5, 31)
188            .into_jiff()
189            .checked_add(Span::new().months(1 + 6 * u16::from(coarser)))
190            .unwrap();
191        DateIndex::try_from(Date::from(d)).unwrap().into()
192    }
193}
194
195impl FromCoarserIndex<YearIndex> for DateIndex {
196    fn min_from(coarser: YearIndex) -> usize {
197        let coarser = u16::from(coarser);
198        if coarser == 0 {
199            0
200        } else {
201            Self::try_from(Date::new(2009 + coarser, 1, 1))
202                .unwrap()
203                .into()
204        }
205    }
206
207    fn max_from_(coarser: YearIndex) -> usize {
208        Self::try_from(Date::new(2009 + u16::from(coarser), 12, 31))
209            .unwrap()
210            .into()
211    }
212}
213
214impl FromCoarserIndex<DecadeIndex> for DateIndex {
215    fn min_from(coarser: DecadeIndex) -> usize {
216        let coarser = u16::from(coarser);
217        if coarser == 0 {
218            0
219        } else {
220            Self::try_from(Date::new(2000 + 10 * coarser, 1, 1))
221                .unwrap()
222                .into()
223        }
224    }
225
226    fn max_from_(coarser: DecadeIndex) -> usize {
227        let coarser = u16::from(coarser);
228        Self::try_from(Date::new(2009 + (10 * coarser), 12, 31))
229            .unwrap()
230            .into()
231    }
232}
233
234impl PrintableIndex for DateIndex {
235    fn to_string() -> &'static str {
236        "dateindex"
237    }
238
239    fn to_possible_strings() -> &'static [&'static str] {
240        &["d", "date", "dateindex"]
241    }
242}
243
244impl fmt::Display for DateIndex {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        let mut buf = itoa::Buffer::new();
247        let str = buf.format(self.0);
248        f.write_str(str)
249    }
250}
251
252impl Formattable for DateIndex {
253    #[inline(always)]
254    fn may_need_escaping() -> bool {
255        false
256    }
257}