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}