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}