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        writer.write_all(&self.first_year.to_ne_bytes())?;
246        writer.write_all(&self.calendar.len().to_ne_bytes())?;
247
248        for year in &self.calendar {
249            year.serialize(&mut writer)?;
250        }
251
252        Ok(())
253    }
254
255    /// Deserialize a calendar from a reader.
256    ///
257    /// ```
258    /// use compact_calendar::CompactCalendar;
259    /// use chrono::NaiveDate;
260    ///
261    /// let mut cal1 = CompactCalendar::default();
262    /// cal1.insert(NaiveDate::from_ymd_opt(2022, 8, 12).unwrap());
263    /// cal1.insert(NaiveDate::from_ymd_opt(2022, 3, 5).unwrap());
264    ///
265    /// let mut buf = Vec::new();
266    /// cal1.serialize(&mut buf).unwrap();
267    ///
268    /// let cal2 = CompactCalendar::deserialize(buf.as_slice()).unwrap();
269    /// assert_eq!(cal1, cal2);
270    /// ```
271    pub fn deserialize(mut reader: impl io::Read) -> io::Result<Self> {
272        let first_year = {
273            let mut buf = [0; std::mem::size_of::<i32>()];
274            reader.read_exact(&mut buf)?;
275            i32::from_ne_bytes(buf)
276        };
277
278        let length = {
279            let mut buf = [0; std::mem::size_of::<usize>()];
280            reader.read_exact(&mut buf)?;
281            usize::from_ne_bytes(buf)
282        };
283
284        let calendar = (0..length)
285            .map(|_| CompactYear::deserialize(&mut reader))
286            .collect::<Result<_, _>>()?;
287
288        Ok(Self { first_year, calendar })
289    }
290}
291
292impl Default for CompactCalendar {
293    /// Create a new year that does not include any day.
294    ///
295    /// ```
296    /// use compact_calendar::CompactCalendar;
297    /// use chrono::NaiveDate;
298    ///
299    /// let mut cal = CompactCalendar::default();
300    /// assert_eq!(cal.count(), 0);
301    /// ```
302    fn default() -> Self {
303        Self { first_year: 0, calendar: VecDeque::default() }
304    }
305}
306
307impl FromIterator<NaiveDate> for CompactCalendar {
308    /// Create a calendar from a list of dates.
309    ///
310    /// ```
311    /// use compact_calendar::CompactCalendar;
312    /// use chrono::NaiveDate;
313    ///
314    /// let dates = [
315    ///     NaiveDate::from_ymd_opt(2013, 11, 3).unwrap(),
316    ///     NaiveDate::from_ymd_opt(2022, 3, 5).unwrap(),
317    ///     NaiveDate::from_ymd_opt(2055, 7, 5).unwrap(),
318    ///     NaiveDate::from_ymd_opt(2013, 11, 3).unwrap(),
319    /// ];
320    ///
321    /// let cal: CompactCalendar = dates.iter().copied().collect();
322    /// assert_eq!(cal.count(), 3);
323    /// assert!(cal.contains(dates[0]));
324    /// ```
325    fn from_iter<T: IntoIterator<Item = NaiveDate>>(iter: T) -> Self {
326        let mut calendar = CompactCalendar::default();
327
328        for date in iter {
329            calendar.insert(date);
330        }
331
332        calendar.calendar.make_contiguous();
333        calendar.calendar.shrink_to_fit();
334        calendar
335    }
336}
337
338impl fmt::Debug for CompactCalendar {
339    /// ```
340    /// use compact_calendar::CompactCalendar;
341    /// use chrono::NaiveDate;
342    ///
343    /// let mut cal = CompactCalendar::default();
344    /// cal.insert(NaiveDate::from_ymd_opt(2022, 8, 12).unwrap());
345    /// cal.insert(NaiveDate::from_ymd_opt(2022, 3, 5).unwrap());
346    /// assert_eq!(format!("{cal:?}"), "CompactCalendar({2022-03-05, 2022-08-12})");
347    /// ```
348    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349        struct DebugCalendar<'a>(&'a CompactCalendar);
350
351        impl fmt::Debug for DebugCalendar<'_> {
352            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353                f.debug_set().entries(self.0.iter()).finish()
354            }
355        }
356
357        f.debug_tuple("CompactCalendar")
358            .field(&DebugCalendar(self))
359            .finish()
360    }
361}
362
363/// A compact representation of included days for a year, using a collection of u32-based bit
364/// array.
365#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
366pub struct CompactYear([CompactMonth; 12]);
367
368impl CompactYear {
369    /// Include a day in this year. Return `false` if the day was already
370    /// included.
371    ///
372    /// ```
373    /// use compact_calendar::CompactYear;
374    ///
375    /// let mut year = CompactYear::default();
376    /// year.insert(11, 3);
377    /// year.insert(11, 3);
378    /// year.insert(1, 25);
379    /// assert_eq!(year.count(), 2);
380    /// ```
381    pub fn insert(&mut self, month: u32, day: u32) -> bool {
382        assert!((1..=12).contains(&month));
383        assert!((1..=31).contains(&day));
384        self.0[(month - 1) as usize].insert(day)
385    }
386
387    /// Check if this year includes the given day.
388    ///
389    /// ```
390    /// use compact_calendar::CompactYear;
391    ///
392    /// let mut year = CompactYear::default();
393    /// year.insert(3, 1);
394    /// year.insert(9, 5);
395    ///
396    /// assert!(year.contains(3, 1));
397    /// assert!(year.contains(9, 5));
398    /// assert!(!year.contains(7, 14));
399    /// ```
400    pub fn contains(&self, month: u32, day: u32) -> bool {
401        assert!((1..=12).contains(&month));
402        assert!((1..=31).contains(&day));
403        self.0[(month - 1) as usize].contains(day)
404    }
405
406    /// Iterate over the days included in this year.
407    ///
408    /// ```
409    /// use compact_calendar::CompactYear;
410    ///
411    /// let mut year = CompactYear::default();
412    /// year.insert(9, 5);
413    /// year.insert(3, 1);
414    ///
415    /// let days: Vec<_> = year.iter().collect();
416    /// assert_eq!(days, [(3, 1), (9, 5)])
417    /// ```
418    pub fn iter(&self) -> impl Iterator<Item = (u32, u32)> + '_ {
419        ((1..=12).zip(&self.0))
420            .flat_map(|(month_i, month)| month.iter().map(move |day| (month_i, day)))
421    }
422
423    /// Get the first day included in this year if it is not empty.
424    ///
425    /// ```
426    /// use compact_calendar::CompactYear;
427    ///
428    /// let mut year = CompactYear::default();
429    /// assert_eq!(year.first(), None);
430    ///
431    /// year.insert(12, 31);
432    /// assert_eq!(year.first(), Some((12, 31)));
433    ///
434    /// year.insert(5, 8);
435    /// assert_eq!(year.first(), Some((5, 8)));
436    /// ```
437    pub fn first(&self) -> Option<(u32, u32)> {
438        self.0.iter().enumerate().find_map(|(i, month)| {
439            let res_month = (i + 1) as u32;
440            Some((res_month, month.first()?))
441        })
442    }
443
444    /// Get the first day included in this year that follows the input day, if such a day exists.
445    ///
446    /// ```
447    /// use compact_calendar::CompactYear;
448    ///
449    /// let mut year = CompactYear::default();
450    /// year.insert(3, 15);
451    /// year.insert(10, 9);
452    /// year.insert(2, 7);
453    ///
454    /// assert_eq!(year.first_after(2, 2), Some((2, 7)));
455    /// assert_eq!(year.first_after(2, 7), Some((3, 15)));
456    /// assert_eq!(year.first_after(11, 1), None);
457    /// ```
458    pub fn first_after(&self, month: u32, day: u32) -> Option<(u32, u32)> {
459        assert!((1..=12).contains(&month));
460        assert!((1..=31).contains(&day));
461        let month0: usize = (month - 1) as usize;
462
463        if let Some(res) = self.0[month0].first_after(day) {
464            Some((month, res))
465        } else {
466            self.0[month0 + 1..]
467                .iter()
468                .enumerate()
469                .find_map(|(i, month)| {
470                    let res_month = (i + month0 + 2) as u32;
471                    Some((res_month, month.first()?))
472                })
473        }
474    }
475
476    /// Count number of days included for this year.
477    ///
478    /// ```
479    /// use compact_calendar::CompactYear;
480    ///
481    /// let mut year = CompactYear::default();
482    /// year.insert(11, 3);
483    /// year.insert(4, 28);
484    /// assert_eq!(year.count(), 2);
485    /// ```
486    pub fn count(&self) -> u32 {
487        self.0.iter().copied().map(CompactMonth::count).sum()
488    }
489
490    /// Serialize this year into a writer.
491    ///
492    /// ```
493    /// use compact_calendar::CompactYear;
494    ///
495    /// let mut year = CompactYear::default();
496    ///
497    /// let mut buf1 = Vec::new();
498    /// year.insert(11, 3);
499    /// year.serialize(&mut buf1).unwrap();
500    ///
501    /// let mut buf2 = Vec::new();
502    /// year.insert(4, 28);
503    /// year.serialize(&mut buf2).unwrap();
504    ///
505    /// assert_ne!(buf1, buf2);
506    /// ```
507    pub fn serialize(&self, mut writer: impl io::Write) -> io::Result<()> {
508        for month in self.0 {
509            month.serialize(&mut writer)?;
510        }
511
512        Ok(())
513    }
514
515    /// Deserialize a year from a reader.
516    ///
517    /// ```
518    /// use compact_calendar::CompactYear;
519    ///
520    /// let mut year1 = CompactYear::default();
521    /// year1.insert(11, 3);
522    /// year1.insert(4, 28);
523    ///
524    /// let mut buf = Vec::new();
525    /// year1.serialize(&mut buf).unwrap();
526    ///
527    /// let year2 = CompactYear::deserialize(buf.as_slice()).unwrap();
528    /// assert_eq!(year1, year2);
529    /// ```
530    pub fn deserialize(mut reader: impl io::Read) -> io::Result<Self> {
531        // NOTE: could use `try_from_fn` when stabilized:
532        //       https://doc.rust-lang.org/std/array/fn.try_from_fn.html
533        let mut res = Self::default();
534
535        for month in &mut res.0 {
536            *month = CompactMonth::deserialize(&mut reader)?;
537        }
538
539        Ok(res)
540    }
541}
542
543impl Default for CompactYear {
544    /// Create a new year that does not include any day.
545    ///
546    /// ```
547    /// use compact_calendar::CompactYear;
548    ///
549    /// let year = CompactYear::default();
550    /// assert_eq!(year.count(), 0);
551    /// ```
552    fn default() -> Self {
553        Self([CompactMonth::default(); 12])
554    }
555}
556
557impl fmt::Debug for CompactYear {
558    /// ```
559    /// use compact_calendar::CompactYear;
560    ///
561    /// let mut year = CompactYear::default();
562    /// year.insert(11, 3);
563    /// year.insert(4, 28);
564    /// assert_eq!(format!("{year:?}"), "{04-28, 11-03}");
565    /// ```
566    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
567        struct DebugMonthDay {
568            month: u32,
569            day: u32,
570        }
571
572        impl fmt::Debug for DebugMonthDay {
573            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574                write!(f, "{:02}-{:02}", self.month, self.day)
575            }
576        }
577
578        f.debug_set()
579            .entries(self.iter().map(|(month, day)| DebugMonthDay { month, day }))
580            .finish()
581    }
582}
583
584/// A compact representation of included days for a month, using a u32-based bit array.
585#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)]
586pub struct CompactMonth(u32);
587
588impl CompactMonth {
589    /// Include a day in this month. Return `false` if it was already included.
590    ///
591    /// ```
592    /// use compact_calendar::CompactMonth;
593    ///
594    /// let mut month = CompactMonth::default();
595    /// month.insert(2);
596    /// month.insert(2);
597    /// month.insert(19);
598    /// assert_eq!(month.count(), 2);
599    /// ```
600    pub fn insert(&mut self, day: u32) -> bool {
601        assert!((1..=31).contains(&day));
602
603        if self.contains(day) {
604            false
605        } else {
606            self.0 |= 1 << (day - 1);
607            true
608        }
609    }
610
611    /// Check if this month includes the given day.
612    ///
613    /// ```
614    /// use compact_calendar::CompactMonth;
615    ///
616    /// let mut month = CompactMonth::default();
617    /// month.insert(1);
618    /// month.insert(18);
619    ///
620    /// assert!(month.contains(1));
621    /// assert!(month.contains(18));
622    /// assert!(!month.contains(5));
623    /// ```
624    pub fn contains(self, day: u32) -> bool {
625        assert!((1..=31).contains(&day));
626        self.0 & (1 << (day - 1)) != 0
627    }
628
629    /// Iterate over the days included in this month.
630    ///
631    /// ```
632    /// use compact_calendar::CompactMonth;
633    ///
634    /// let mut month = CompactMonth::default();
635    /// month.insert(18);
636    /// month.insert(1);
637    ///
638    /// let days: Vec<u32> = month.iter().collect();
639    /// assert_eq!(days, [1, 18])
640    /// ```
641    pub fn iter(self) -> impl Iterator<Item = u32> {
642        let mut val = self.0;
643
644        std::iter::from_fn(move || {
645            if val != 0 {
646                let day0 = val.trailing_zeros();
647                val ^= 1 << day0;
648                Some(day0 + 1)
649            } else {
650                None
651            }
652        })
653    }
654
655    /// Get the first day included in this month if it is not empty.
656    ///
657    /// ```
658    /// use compact_calendar::CompactMonth;
659    ///
660    /// let mut month = CompactMonth::default();
661    /// assert_eq!(month.first(), None);
662    ///
663    /// month.insert(31);
664    /// assert_eq!(month.first(), Some(31));
665    ///
666    /// month.insert(8);
667    /// assert_eq!(month.first(), Some(8));
668    /// ```
669    pub fn first(self) -> Option<u32> {
670        if self.0 == 0 {
671            None
672        } else {
673            Some(self.0.trailing_zeros() + 1)
674        }
675    }
676
677    /// Get the first day included in this month that follows the input day, if such a day exists.
678    ///
679    /// ```
680    /// use compact_calendar::CompactMonth;
681    ///
682    /// let mut month = CompactMonth::default();
683    /// month.insert(4);
684    /// month.insert(17);
685    ///
686    /// assert_eq!(month.first_after(2), Some(4));
687    /// assert_eq!(month.first_after(4), Some(17));
688    /// assert_eq!(month.first_after(17), None);
689    /// ```
690    pub fn first_after(self, day: u32) -> Option<u32> {
691        assert!((1..=31).contains(&day));
692        let shifted = self.0 >> day;
693
694        if shifted == 0 {
695            None
696        } else {
697            Some(day + shifted.trailing_zeros() + 1)
698        }
699    }
700
701    /// Count number of days included for this month.
702    ///
703    /// ```
704    /// use compact_calendar::CompactMonth;
705    ///
706    /// let mut month = CompactMonth::default();
707    /// month.insert(26);
708    /// month.insert(3);
709    /// assert_eq!(month.count(), 2);
710    /// ```
711    pub fn count(self) -> u32 {
712        self.0.count_ones()
713    }
714
715    /// Serialize this month into a writer.
716    ///
717    /// ```
718    /// use compact_calendar::CompactMonth;
719    ///
720    /// let mut month = CompactMonth::default();
721    ///
722    /// let mut buf1 = Vec::new();
723    /// month.insert(31);
724    /// month.serialize(&mut buf1).unwrap();
725    ///
726    /// let mut buf2 = Vec::new();
727    /// month.insert(1);
728    /// month.serialize(&mut buf2).unwrap();
729    ///
730    /// assert_ne!(buf1, buf2);
731    /// ```
732    pub fn serialize(self, mut writer: impl io::Write) -> io::Result<()> {
733        writer.write_all(&self.0.to_ne_bytes())
734    }
735
736    /// Deserialize a month from a reader.
737    ///
738    /// ```
739    /// use compact_calendar::CompactMonth;
740    ///
741    /// let mut month1 = CompactMonth::default();
742    /// month1.insert(30);
743    /// month1.insert(2);
744    ///
745    /// let mut buf = Vec::new();
746    /// month1.serialize(&mut buf).unwrap();
747    ///
748    /// let month2 = CompactMonth::deserialize(buf.as_slice()).unwrap();
749    /// assert_eq!(month1, month2);
750    /// ```
751    pub fn deserialize(mut reader: impl io::Read) -> io::Result<Self> {
752        let mut buf = [0; std::mem::size_of::<u32>()];
753        reader.read_exact(&mut buf)?;
754        Ok(Self(u32::from_ne_bytes(buf)))
755    }
756}
757
758impl Default for CompactMonth {
759    /// Create a new month that does not include any day.
760    ///
761    /// ```
762    /// use compact_calendar::CompactMonth;
763    ///
764    /// let month = CompactMonth::default();
765    /// assert_eq!(month.count(), 0);
766    /// ```
767    fn default() -> Self {
768        Self(0)
769    }
770}
771
772impl fmt::Debug for CompactMonth {
773    /// ```
774    /// use compact_calendar::CompactMonth;
775    ///
776    /// let mut month = CompactMonth::default();
777    /// month.insert(26);
778    /// month.insert(3);
779    /// assert_eq!(format!("{month:?}"), "{03, 26}");
780    /// ```
781    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
782        struct DebugDay(u32);
783
784        impl fmt::Debug for DebugDay {
785            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
786                write!(f, "{:02}", self.0)
787            }
788        }
789
790        f.debug_set().entries(self.iter().map(DebugDay)).finish()
791    }
792}