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