Skip to main content

compact_calendar/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::collections::VecDeque;
4use std::{fmt, io};
5
6use chrono::{Datelike, NaiveDate};
7
8/// A compact representation of included days in a range of years, using u32-based bit arrays.
9#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
10pub struct CompactCalendar {
11    first_year: i32,
12    calendar: VecDeque<CompactYear>,
13}
14
15impl CompactCalendar {
16    /// Get a reference to the year containing give date.
17    ///
18    /// ```
19    /// use compact_calendar::CompactCalendar;
20    /// use chrono::NaiveDate;
21    ///
22    /// let day1 = NaiveDate::from_ymd_opt(2013, 11, 3).unwrap();
23    /// let day2 = NaiveDate::from_ymd_opt(2055, 3, 5).unwrap();
24    ///
25    /// let mut cal = CompactCalendar::default();
26    /// cal.insert(day1);
27    ///
28    /// assert!(cal.year_for(day1).unwrap().contains(11, 3));
29    /// assert!(cal.year_for(day2).is_none());
30    /// ```
31    pub fn year_for(&self, date: NaiveDate) -> Option<&CompactYear> {
32        let year0 = usize::try_from(date.year() - self.first_year).ok()?;
33        self.calendar.get(year0)
34    }
35
36    /// Get a mutable reference to the year containing give date.
37    ///
38    /// ```
39    /// use compact_calendar::CompactCalendar;
40    /// use chrono::NaiveDate;
41    ///
42    /// let day1 = NaiveDate::from_ymd_opt(2013, 11, 3).unwrap();
43    /// let day2 = NaiveDate::from_ymd_opt(2022, 3, 5).unwrap();
44    /// let day3 = NaiveDate::from_ymd_opt(2055, 7, 5).unwrap();
45    ///
46    /// let mut cal = CompactCalendar::default();
47    /// assert!(cal.year_for_mut(day3).is_none());
48    ///
49    /// cal.insert(day1);
50    /// assert!(cal.year_for_mut(day1).unwrap().contains(11, 3));
51    /// ```
52    pub fn year_for_mut(&mut self, date: NaiveDate) -> Option<&mut CompactYear> {
53        let year0 = usize::try_from(date.year() - self.first_year).ok()?;
54        self.calendar.get_mut(year0)
55    }
56
57    /// Include a day in this calendar. Return `false` if the day already
58    /// belonged to the calendar.
59    ///
60    /// ```
61    /// use compact_calendar::CompactCalendar;
62    /// use chrono::NaiveDate;
63    ///
64    /// let day1 = NaiveDate::from_ymd_opt(2013, 11, 3).unwrap();
65    /// let day2 = NaiveDate::from_ymd_opt(2022, 3, 5).unwrap();
66    /// let day3 = NaiveDate::from_ymd_opt(2055, 9, 7).unwrap();
67    ///
68    /// let mut cal = CompactCalendar::default();
69    /// assert!(cal.insert(day1));
70    /// assert!(cal.insert(day2));
71    /// assert!(cal.insert(day3));
72    /// assert!(!cal.insert(day1));
73    /// assert_eq!(cal.count(), 3);
74    /// ```
75    pub fn insert(&mut self, date: NaiveDate) -> bool {
76        let year = {
77            if let Some(year) = self.year_for_mut(date) {
78                year
79            } else if self.calendar.is_empty() {
80                self.first_year = date.year();
81                self.calendar.push_back_mut(CompactYear::default())
82            } else if date.year() < self.first_year {
83                for _ in date.year()..self.first_year {
84                    self.calendar.push_front(CompactYear::default());
85                }
86
87                self.first_year = date.year();
88
89                #[allow(clippy::unwrap_used)]
90                // self.calendar is not empty as checked in second if branch
91                self.calendar.front_mut().unwrap()
92            } else {
93                let last_year = self.first_year
94                    + i32::try_from(self.calendar.len()).expect("calendar is too large")
95                    - 1;
96
97                for _ in last_year..date.year() {
98                    self.calendar.push_back(CompactYear::default());
99                }
100
101                #[allow(clippy::unwrap_used)]
102                // self.calendar is not empty as checked in second if branch
103                self.calendar.back_mut().unwrap()
104            }
105        };
106
107        year.insert(date.month(), date.day())
108    }
109
110    /// Check if this calendar includes the given day.
111    ///
112    /// ```
113    /// use compact_calendar::CompactCalendar;
114    /// use chrono::NaiveDate;
115    ///
116    /// let day1 = NaiveDate::from_ymd_opt(2013, 11, 3).unwrap();
117    /// let day2 = NaiveDate::from_ymd_opt(2022, 3, 5).unwrap();
118    /// let day3 = NaiveDate::from_ymd_opt(2022, 8, 12).unwrap();
119    ///
120    /// let mut cal = CompactCalendar::default();
121    /// cal.insert(day1);
122    /// cal.insert(day2);
123    ///
124    /// assert!(cal.contains(day1));
125    /// assert!(cal.contains(day2));
126    /// assert!(!cal.contains(day3));
127    /// ```
128    pub fn contains(&self, date: NaiveDate) -> bool {
129        if let Some(year) = self.year_for(date) {
130            year.contains(date.month(), date.day())
131        } else {
132            false
133        }
134    }
135
136    /// Iterate over the days included in this calendar.
137    ///
138    /// ```
139    /// use compact_calendar::CompactCalendar;
140    /// use chrono::NaiveDate;
141    ///
142    /// let day1 = NaiveDate::from_ymd_opt(2013, 11, 3).unwrap();
143    /// let day2 = NaiveDate::from_ymd_opt(2022, 3, 5).unwrap();
144    /// let day3 = NaiveDate::from_ymd_opt(2022, 8, 12).unwrap();
145    ///
146    /// let mut cal = CompactCalendar::default();
147    /// cal.insert(day3);
148    /// cal.insert(day1);
149    /// cal.insert(day2);
150    ///
151    /// let days: Vec<_> = cal.iter().collect();
152    /// assert_eq!(days, [day1, day2, day3])
153    /// ```
154    pub fn iter(&self) -> impl Iterator<Item = NaiveDate> + Send + Sync + '_ {
155        (self.first_year..)
156            .zip(self.calendar.iter())
157            .flat_map(|(year_i, year)| {
158                year.iter().map(move |(month, day)| {
159                    NaiveDate::from_ymd_opt(year_i, month, day)
160                        .expect("invalid date loaded from calendar")
161                })
162            })
163    }
164
165    /// Get the first day included in this calendar that follows the input day, if such a day
166    /// exists.
167    ///
168    /// ```
169    /// use compact_calendar::CompactCalendar;
170    /// use chrono::NaiveDate;
171    ///
172    /// let day0 = NaiveDate::from_ymd_opt(2010, 1, 1).unwrap();
173    /// let day1 = NaiveDate::from_ymd_opt(2013, 11, 3).unwrap();
174    /// let day2 = NaiveDate::from_ymd_opt(2022, 3, 5).unwrap();
175    /// let day3 = NaiveDate::from_ymd_opt(2022, 8, 12).unwrap();
176    ///
177    /// let mut cal = CompactCalendar::default();
178    /// cal.insert(day1);
179    /// cal.insert(day2);
180    /// cal.insert(day3);
181    ///
182    /// assert_eq!(cal.first_after(day0), Some(day1));
183    /// assert_eq!(cal.first_after(day2), Some(day3));
184    /// assert_eq!(cal.first_after(day3), None);
185    /// ```
186    pub fn first_after(&self, date: NaiveDate) -> Option<NaiveDate> {
187        if let Some(year) = self.year_for(date) {
188            let from_first_year = year
189                .first_after(date.month(), date.day())
190                .map(|(month, day)| {
191                    NaiveDate::from_ymd_opt(date.year(), month, day)
192                        .expect("invalid date loaded from calendar")
193                });
194
195            from_first_year.or_else(|| {
196                let year0 = usize::try_from(date.year() - self.first_year).ok()?;
197
198                (date.year() + 1..)
199                    .zip(self.calendar.iter().skip(year0 + 1))
200                    .find_map(|(year_i, year)| {
201                        let (month, day) = year.first()?;
202                        Some(
203                            NaiveDate::from_ymd_opt(year_i, month, day)
204                                .expect("invalid date loaded from calendar"),
205                        )
206                    })
207            })
208        } else if date.year() < self.first_year {
209            self.iter().next()
210        } else {
211            None
212        }
213    }
214
215    /// Count number of days included for this calendar.
216    ///
217    /// ```
218    /// use compact_calendar::CompactCalendar;
219    /// use chrono::NaiveDate;
220    ///
221    /// let mut cal = CompactCalendar::default();
222    /// cal.insert(NaiveDate::from_ymd_opt(2022, 8, 12).unwrap());
223    /// cal.insert(NaiveDate::from_ymd_opt(2022, 3, 5).unwrap());
224    /// assert_eq!(cal.count(), 2);
225    /// ```
226    pub fn count(&self) -> u32 {
227        self.calendar.iter().map(CompactYear::count).sum()
228    }
229
230    /// Serialize this calendar into a writer.
231    ///
232    /// ```
233    /// use compact_calendar::CompactCalendar;
234    /// use chrono::NaiveDate;
235    ///
236    /// let mut cal = CompactCalendar::default();
237    ///
238    /// let mut buf1 = Vec::new();
239    /// cal.insert(NaiveDate::from_ymd_opt(2022, 8, 12).unwrap());
240    /// cal.serialize(&mut buf1).unwrap();
241    ///
242    /// let mut buf2 = Vec::new();
243    /// cal.insert(NaiveDate::from_ymd_opt(2022, 3, 5).unwrap());
244    /// cal.serialize(&mut buf2).unwrap();
245    ///
246    /// assert_ne!(buf1, buf2);
247    /// ```
248    pub fn serialize(&self, mut writer: impl io::Write) -> io::Result<()> {
249        let len = self.calendar.len();
250
251        let len32 = u32::try_from(len).map_err(|_| {
252            io::Error::other(format!(
253                "compact-calendar contains {len} years but serializing only supports {}",
254                u32::MAX
255            ))
256        })?;
257
258        writer.write_all(&self.first_year.to_le_bytes())?;
259        writer.write_all(&len32.to_le_bytes())?;
260
261        for year in &self.calendar {
262            year.serialize(&mut writer)?;
263        }
264
265        Ok(())
266    }
267
268    /// Deserialize a calendar from a reader.
269    ///
270    /// ```
271    /// use compact_calendar::CompactCalendar;
272    /// use chrono::NaiveDate;
273    ///
274    /// let mut cal1 = CompactCalendar::default();
275    /// cal1.insert(NaiveDate::from_ymd_opt(2022, 8, 12).unwrap());
276    /// cal1.insert(NaiveDate::from_ymd_opt(2022, 3, 5).unwrap());
277    ///
278    /// let mut buf = Vec::new();
279    /// cal1.serialize(&mut buf).unwrap();
280    ///
281    /// let cal2 = CompactCalendar::deserialize(buf.as_slice()).unwrap();
282    /// assert_eq!(cal1, cal2);
283    /// ```
284    pub fn deserialize(mut reader: impl io::Read) -> io::Result<Self> {
285        let first_year = {
286            let mut buf = [0; std::mem::size_of::<i32>()];
287            reader.read_exact(&mut buf)?;
288            i32::from_le_bytes(buf)
289        };
290
291        let length = {
292            let mut buf = [0; std::mem::size_of::<u32>()];
293            reader.read_exact(&mut buf)?;
294            let len = u32::from_le_bytes(buf);
295
296            if isize::try_from(len).is_err() {
297                return Err(io::Error::other(format!(
298                    "compact-calendar contains {len} years but this platform only supports {}",
299                    isize::MAX
300                )));
301            };
302
303            len
304        };
305
306        let calendar = (0..length)
307            .map(|_| CompactYear::deserialize(&mut reader))
308            .collect::<Result<_, _>>()?;
309
310        Ok(Self { first_year, calendar })
311    }
312}
313
314impl Default for CompactCalendar {
315    /// Create a new year that does not include any day.
316    ///
317    /// ```
318    /// use compact_calendar::CompactCalendar;
319    /// use chrono::NaiveDate;
320    ///
321    /// let mut cal = CompactCalendar::default();
322    /// assert_eq!(cal.count(), 0);
323    /// ```
324    fn default() -> Self {
325        Self { first_year: 0, calendar: VecDeque::default() }
326    }
327}
328
329impl FromIterator<NaiveDate> for CompactCalendar {
330    /// Create a calendar from a list of dates.
331    ///
332    /// ```
333    /// use compact_calendar::CompactCalendar;
334    /// use chrono::NaiveDate;
335    ///
336    /// let dates = [
337    ///     NaiveDate::from_ymd_opt(2013, 11, 3).unwrap(),
338    ///     NaiveDate::from_ymd_opt(2022, 3, 5).unwrap(),
339    ///     NaiveDate::from_ymd_opt(2055, 7, 5).unwrap(),
340    ///     NaiveDate::from_ymd_opt(2013, 11, 3).unwrap(),
341    /// ];
342    ///
343    /// let cal: CompactCalendar = dates.iter().copied().collect();
344    /// assert_eq!(cal.count(), 3);
345    /// assert!(cal.contains(dates[0]));
346    /// ```
347    fn from_iter<T: IntoIterator<Item = NaiveDate>>(iter: T) -> Self {
348        let mut calendar = CompactCalendar::default();
349
350        for date in iter {
351            calendar.insert(date);
352        }
353
354        calendar.calendar.make_contiguous();
355        calendar.calendar.shrink_to_fit();
356        calendar
357    }
358}
359
360impl fmt::Debug for CompactCalendar {
361    /// ```
362    /// use compact_calendar::CompactCalendar;
363    /// use chrono::NaiveDate;
364    ///
365    /// let mut cal = CompactCalendar::default();
366    /// cal.insert(NaiveDate::from_ymd_opt(2022, 8, 12).unwrap());
367    /// cal.insert(NaiveDate::from_ymd_opt(2022, 3, 5).unwrap());
368    /// assert_eq!(format!("{cal:?}"), "CompactCalendar({2022-03-05, 2022-08-12})");
369    /// ```
370    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371        struct DebugCalendar<'a>(&'a CompactCalendar);
372
373        impl fmt::Debug for DebugCalendar<'_> {
374            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375                f.debug_set().entries(self.0.iter()).finish()
376            }
377        }
378
379        f.debug_tuple("CompactCalendar")
380            .field(&DebugCalendar(self))
381            .finish()
382    }
383}
384
385/// A compact representation of included days for a year, using a collection of u32-based bit
386/// array.
387#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
388pub struct CompactYear([CompactMonth; 12]);
389
390impl CompactYear {
391    /// Include a day in this year. Return `false` if the day was already
392    /// included.
393    ///
394    /// ```
395    /// use compact_calendar::CompactYear;
396    ///
397    /// let mut year = CompactYear::default();
398    /// year.insert(11, 3);
399    /// year.insert(11, 3);
400    /// year.insert(1, 25);
401    /// assert_eq!(year.count(), 2);
402    /// ```
403    pub fn insert(&mut self, month: u32, day: u32) -> bool {
404        assert!((1..=12).contains(&month));
405        assert!((1..=31).contains(&day));
406        self.0[(month - 1) as usize].insert(day)
407    }
408
409    /// Check if this year includes the given day.
410    ///
411    /// ```
412    /// use compact_calendar::CompactYear;
413    ///
414    /// let mut year = CompactYear::default();
415    /// year.insert(3, 1);
416    /// year.insert(9, 5);
417    ///
418    /// assert!(year.contains(3, 1));
419    /// assert!(year.contains(9, 5));
420    /// assert!(!year.contains(7, 14));
421    /// ```
422    pub fn contains(&self, month: u32, day: u32) -> bool {
423        assert!((1..=12).contains(&month));
424        assert!((1..=31).contains(&day));
425        self.0[(month - 1) as usize].contains(day)
426    }
427
428    /// Iterate over the days included in this year.
429    ///
430    /// ```
431    /// use compact_calendar::CompactYear;
432    ///
433    /// let mut year = CompactYear::default();
434    /// year.insert(9, 5);
435    /// year.insert(3, 1);
436    ///
437    /// let days: Vec<_> = year.iter().collect();
438    /// assert_eq!(days, [(3, 1), (9, 5)])
439    /// ```
440    pub fn iter(&self) -> impl Iterator<Item = (u32, u32)> + '_ {
441        ((1..=12).zip(&self.0))
442            .flat_map(|(month_i, month)| month.iter().map(move |day| (month_i, day)))
443    }
444
445    /// Get the first day included in this year if it is not empty.
446    ///
447    /// ```
448    /// use compact_calendar::CompactYear;
449    ///
450    /// let mut year = CompactYear::default();
451    /// assert_eq!(year.first(), None);
452    ///
453    /// year.insert(12, 31);
454    /// assert_eq!(year.first(), Some((12, 31)));
455    ///
456    /// year.insert(5, 8);
457    /// assert_eq!(year.first(), Some((5, 8)));
458    /// ```
459    pub fn first(&self) -> Option<(u32, u32)> {
460        self.0.iter().enumerate().find_map(|(i, month)| {
461            let res_month = (i + 1) as u32;
462            Some((res_month, month.first()?))
463        })
464    }
465
466    /// Get the first day included in this year that follows the input day, if such a day exists.
467    ///
468    /// ```
469    /// use compact_calendar::CompactYear;
470    ///
471    /// let mut year = CompactYear::default();
472    /// year.insert(3, 15);
473    /// year.insert(10, 9);
474    /// year.insert(2, 7);
475    ///
476    /// assert_eq!(year.first_after(2, 2), Some((2, 7)));
477    /// assert_eq!(year.first_after(2, 7), Some((3, 15)));
478    /// assert_eq!(year.first_after(11, 1), None);
479    /// ```
480    pub fn first_after(&self, month: u32, day: u32) -> Option<(u32, u32)> {
481        assert!((1..=12).contains(&month));
482        assert!((1..=31).contains(&day));
483        let month0: usize = (month - 1) as usize;
484
485        if let Some(res) = self.0[month0].first_after(day) {
486            Some((month, res))
487        } else {
488            self.0[month0 + 1..]
489                .iter()
490                .enumerate()
491                .find_map(|(i, month)| {
492                    let res_month = (i + month0 + 2) as u32;
493                    Some((res_month, month.first()?))
494                })
495        }
496    }
497
498    /// Count number of days included for this year.
499    ///
500    /// ```
501    /// use compact_calendar::CompactYear;
502    ///
503    /// let mut year = CompactYear::default();
504    /// year.insert(11, 3);
505    /// year.insert(4, 28);
506    /// assert_eq!(year.count(), 2);
507    /// ```
508    pub fn count(&self) -> u32 {
509        self.0.iter().copied().map(CompactMonth::count).sum()
510    }
511
512    /// Serialize this year into a writer.
513    ///
514    /// ```
515    /// use compact_calendar::CompactYear;
516    ///
517    /// let mut year = CompactYear::default();
518    ///
519    /// let mut buf1 = Vec::new();
520    /// year.insert(11, 3);
521    /// year.serialize(&mut buf1).unwrap();
522    ///
523    /// let mut buf2 = Vec::new();
524    /// year.insert(4, 28);
525    /// year.serialize(&mut buf2).unwrap();
526    ///
527    /// assert_ne!(buf1, buf2);
528    /// ```
529    pub fn serialize(&self, mut writer: impl io::Write) -> io::Result<()> {
530        for month in self.0 {
531            month.serialize(&mut writer)?;
532        }
533
534        Ok(())
535    }
536
537    /// Deserialize a year from a reader.
538    ///
539    /// ```
540    /// use compact_calendar::CompactYear;
541    ///
542    /// let mut year1 = CompactYear::default();
543    /// year1.insert(11, 3);
544    /// year1.insert(4, 28);
545    ///
546    /// let mut buf = Vec::new();
547    /// year1.serialize(&mut buf).unwrap();
548    ///
549    /// let year2 = CompactYear::deserialize(buf.as_slice()).unwrap();
550    /// assert_eq!(year1, year2);
551    /// ```
552    pub fn deserialize(mut reader: impl io::Read) -> io::Result<Self> {
553        // NOTE: could use `try_from_fn` when stabilized:
554        //       https://doc.rust-lang.org/std/array/fn.try_from_fn.html
555        let mut res = Self::default();
556
557        for month in &mut res.0 {
558            *month = CompactMonth::deserialize(&mut reader)?;
559        }
560
561        Ok(res)
562    }
563}
564
565impl Default for CompactYear {
566    /// Create a new year that does not include any day.
567    ///
568    /// ```
569    /// use compact_calendar::CompactYear;
570    ///
571    /// let year = CompactYear::default();
572    /// assert_eq!(year.count(), 0);
573    /// ```
574    fn default() -> Self {
575        Self([CompactMonth::default(); 12])
576    }
577}
578
579impl fmt::Debug for CompactYear {
580    /// ```
581    /// use compact_calendar::CompactYear;
582    ///
583    /// let mut year = CompactYear::default();
584    /// year.insert(11, 3);
585    /// year.insert(4, 28);
586    /// assert_eq!(format!("{year:?}"), "{04-28, 11-03}");
587    /// ```
588    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
589        struct DebugMonthDay {
590            month: u32,
591            day: u32,
592        }
593
594        impl fmt::Debug for DebugMonthDay {
595            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
596                write!(f, "{:02}-{:02}", self.month, self.day)
597            }
598        }
599
600        f.debug_set()
601            .entries(self.iter().map(|(month, day)| DebugMonthDay { month, day }))
602            .finish()
603    }
604}
605
606/// A compact representation of included days for a month, using a u32-based bit array.
607#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
608pub struct CompactMonth(u32);
609
610impl CompactMonth {
611    /// Include a day in this month. Return `false` if it was already included.
612    ///
613    /// ```
614    /// use compact_calendar::CompactMonth;
615    ///
616    /// let mut month = CompactMonth::default();
617    /// month.insert(2);
618    /// month.insert(2);
619    /// month.insert(19);
620    /// assert_eq!(month.count(), 2);
621    /// ```
622    pub fn insert(&mut self, day: u32) -> bool {
623        assert!((1..=31).contains(&day));
624
625        if self.contains(day) {
626            false
627        } else {
628            self.0 |= 1 << (day - 1);
629            true
630        }
631    }
632
633    /// Check if this month includes the given day.
634    ///
635    /// ```
636    /// use compact_calendar::CompactMonth;
637    ///
638    /// let mut month = CompactMonth::default();
639    /// month.insert(1);
640    /// month.insert(18);
641    ///
642    /// assert!(month.contains(1));
643    /// assert!(month.contains(18));
644    /// assert!(!month.contains(5));
645    /// ```
646    pub fn contains(self, day: u32) -> bool {
647        assert!((1..=31).contains(&day));
648        self.0 & (1 << (day - 1)) != 0
649    }
650
651    /// Iterate over the days included in this month.
652    ///
653    /// ```
654    /// use compact_calendar::CompactMonth;
655    ///
656    /// let mut month = CompactMonth::default();
657    /// month.insert(18);
658    /// month.insert(1);
659    ///
660    /// let days: Vec<u32> = month.iter().collect();
661    /// assert_eq!(days, [1, 18])
662    /// ```
663    pub fn iter(self) -> impl Iterator<Item = u32> {
664        let mut val = self.0;
665
666        std::iter::from_fn(move || {
667            if val != 0 {
668                let day0 = val.trailing_zeros();
669                val ^= 1 << day0;
670                Some(day0 + 1)
671            } else {
672                None
673            }
674        })
675    }
676
677    /// Get the first day included in this month if it is not empty.
678    ///
679    /// ```
680    /// use compact_calendar::CompactMonth;
681    ///
682    /// let mut month = CompactMonth::default();
683    /// assert_eq!(month.first(), None);
684    ///
685    /// month.insert(31);
686    /// assert_eq!(month.first(), Some(31));
687    ///
688    /// month.insert(8);
689    /// assert_eq!(month.first(), Some(8));
690    /// ```
691    pub fn first(self) -> Option<u32> {
692        if self.0 == 0 {
693            None
694        } else {
695            Some(self.0.trailing_zeros() + 1)
696        }
697    }
698
699    /// Get the first day included in this month that follows the input day, if such a day exists.
700    ///
701    /// ```
702    /// use compact_calendar::CompactMonth;
703    ///
704    /// let mut month = CompactMonth::default();
705    /// month.insert(4);
706    /// month.insert(17);
707    ///
708    /// assert_eq!(month.first_after(2), Some(4));
709    /// assert_eq!(month.first_after(4), Some(17));
710    /// assert_eq!(month.first_after(17), None);
711    /// ```
712    pub fn first_after(self, day: u32) -> Option<u32> {
713        assert!((1..=31).contains(&day));
714        let shifted = self.0 >> day;
715
716        if shifted == 0 {
717            None
718        } else {
719            Some(day + shifted.trailing_zeros() + 1)
720        }
721    }
722
723    /// Count number of days included for this month.
724    ///
725    /// ```
726    /// use compact_calendar::CompactMonth;
727    ///
728    /// let mut month = CompactMonth::default();
729    /// month.insert(26);
730    /// month.insert(3);
731    /// assert_eq!(month.count(), 2);
732    /// ```
733    pub fn count(self) -> u32 {
734        self.0.count_ones()
735    }
736
737    /// Serialize this month into a writer.
738    ///
739    /// ```
740    /// use compact_calendar::CompactMonth;
741    ///
742    /// let mut month = CompactMonth::default();
743    ///
744    /// let mut buf1 = Vec::new();
745    /// month.insert(31);
746    /// month.serialize(&mut buf1).unwrap();
747    ///
748    /// let mut buf2 = Vec::new();
749    /// month.insert(1);
750    /// month.serialize(&mut buf2).unwrap();
751    ///
752    /// assert_ne!(buf1, buf2);
753    /// ```
754    pub fn serialize(self, mut writer: impl io::Write) -> io::Result<()> {
755        writer.write_all(&self.0.to_le_bytes())
756    }
757
758    /// Deserialize a month from a reader.
759    ///
760    /// ```
761    /// use compact_calendar::CompactMonth;
762    ///
763    /// let mut month1 = CompactMonth::default();
764    /// month1.insert(30);
765    /// month1.insert(2);
766    ///
767    /// let mut buf = Vec::new();
768    /// month1.serialize(&mut buf).unwrap();
769    ///
770    /// let month2 = CompactMonth::deserialize(buf.as_slice()).unwrap();
771    /// assert_eq!(month1, month2);
772    /// ```
773    pub fn deserialize(mut reader: impl io::Read) -> io::Result<Self> {
774        let mut buf = [0; std::mem::size_of::<u32>()];
775        reader.read_exact(&mut buf)?;
776        Ok(Self(u32::from_le_bytes(buf)))
777    }
778}
779
780impl Default for CompactMonth {
781    /// Create a new month that does not include any day.
782    ///
783    /// ```
784    /// use compact_calendar::CompactMonth;
785    ///
786    /// let month = CompactMonth::default();
787    /// assert_eq!(month.count(), 0);
788    /// ```
789    fn default() -> Self {
790        Self(0)
791    }
792}
793
794impl fmt::Debug for CompactMonth {
795    /// ```
796    /// use compact_calendar::CompactMonth;
797    ///
798    /// let mut month = CompactMonth::default();
799    /// month.insert(26);
800    /// month.insert(3);
801    /// assert_eq!(format!("{month:?}"), "{03, 26}");
802    /// ```
803    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
804        struct DebugDay(u32);
805
806        impl fmt::Debug for DebugDay {
807            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
808                write!(f, "{:02}", self.0)
809            }
810        }
811
812        f.debug_set().entries(self.iter().map(DebugDay)).finish()
813    }
814}