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}