1use std::{collections::BTreeSet, fmt::Debug, num::NonZero};
4
5use weekday_num_set::WeekdayNumSet;
6
7use calendar_types::{
8 primitive::Sign,
9 time::{IsoWeek, Month, Weekday},
10};
11
12use crate::time::DateTimeOrDate;
13
14pub mod weekday_num_set;
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct RRule {
22 pub freq: FreqByRules,
24 pub core_by_rules: CoreByRules,
26 pub interval: Option<Interval>,
28 pub termination: Option<Termination>,
30 pub week_start: Option<Weekday>,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum Termination {
37 Count(u64),
39 Until(DateTimeOrDate),
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
45pub struct Interval(pub(crate) NonZero<u64>);
46
47impl Interval {
48 pub const fn new(value: NonZero<u64>) -> Self {
50 Self(value)
51 }
52
53 pub const fn get(self) -> NonZero<u64> {
55 self.0
56 }
57}
58
59impl Default for Interval {
60 fn default() -> Self {
61 Self(std::num::NonZeroU64::MIN)
62 }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum Freq {
68 Secondly,
69 Minutely,
70 Hourly,
71 Daily,
72 Weekly,
73 Monthly,
74 Yearly,
75}
76
77impl From<&FreqByRules> for Freq {
78 fn from(value: &FreqByRules) -> Self {
79 match value {
80 FreqByRules::Secondly(_) => Self::Secondly,
81 FreqByRules::Minutely(_) => Self::Minutely,
82 FreqByRules::Hourly(_) => Self::Hourly,
83 FreqByRules::Daily(_) => Self::Daily,
84 FreqByRules::Weekly => Self::Weekly,
85 FreqByRules::Monthly(_) => Self::Monthly,
86 FreqByRules::Yearly(_) => Self::Yearly,
87 }
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
96pub enum FreqByRules {
97 Secondly(ByPeriodDayRules),
99 Minutely(ByPeriodDayRules),
101 Hourly(ByPeriodDayRules),
103 Daily(ByMonthDayRule),
105 Weekly,
107 Monthly(ByMonthDayRule),
109 Yearly(YearlyByRules),
111}
112
113#[derive(Debug, Default, Clone, PartialEq, Eq)]
115pub struct CoreByRules {
116 pub by_second: Option<SecondSet>,
118 pub by_minute: Option<MinuteSet>,
120 pub by_hour: Option<HourSet>,
122 pub by_month: Option<MonthSet>,
124 pub by_day: Option<WeekdayNumSet>,
126 pub by_set_pos: Option<BTreeSet<YearDayNum>>,
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
132pub struct ByPeriodDayRules {
133 pub by_month_day: Option<MonthDaySet>,
135 pub by_year_day: Option<BTreeSet<YearDayNum>>,
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141pub struct ByMonthDayRule {
142 pub by_month_day: Option<MonthDaySet>,
144}
145
146#[derive(Debug, Default, Clone, PartialEq, Eq)]
148pub struct YearlyByRules {
149 pub by_month_day: Option<MonthDaySet>,
151 pub by_year_day: Option<BTreeSet<YearDayNum>>,
153 pub by_week_no: Option<WeekNoSet>,
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
159pub struct YearDayNum(NonZero<i16>);
160
161impl YearDayNum {
162 pub const fn get(self) -> i16 {
164 self.0.get()
165 }
166
167 pub const fn from_signed_index(sign: Sign, index: u16) -> Option<Self> {
169 match index {
170 1..=366 => {
171 let value = (index as i16) * (sign as i16);
172
173 Some(Self(unsafe { NonZero::new_unchecked(value) }))
176 }
177 _ => None,
178 }
179 }
180}
181
182#[derive(Clone, Copy, PartialEq, Eq, Hash)]
184pub struct WeekdayNum {
185 pub ordinal: Option<(Sign, IsoWeek)>,
187 pub weekday: Weekday,
189}
190
191impl Debug for WeekdayNum {
192 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193 if let Some((sign, week)) = self.ordinal {
194 match sign {
195 Sign::Pos => write!(f, "+"),
196 Sign::Neg => write!(f, "-"),
197 }?;
198
199 let w = week as u8;
200 write!(f, "{w:02}")?;
201 }
202
203 write!(
204 f,
205 "{}",
206 match self.weekday {
207 Weekday::Monday => "MO",
208 Weekday::Tuesday => "TU",
209 Weekday::Wednesday => "WE",
210 Weekday::Thursday => "TH",
211 Weekday::Friday => "FR",
212 Weekday::Saturday => "SA",
213 Weekday::Sunday => "SU",
214 }
215 )
216 }
217}
218
219impl PartialOrd for WeekdayNum {
220 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
221 Some(self.cmp(other))
222 }
223}
224
225impl Ord for WeekdayNum {
226 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
227 let lhs = self
232 .ordinal
233 .map(|(sign, week)| (sign as i16) * (week as i16))
234 .unwrap_or(i16::MIN);
235
236 let rhs = other
237 .ordinal
238 .map(|(sign, week)| (sign as i16) * (week as i16))
239 .unwrap_or(i16::MIN);
240
241 (lhs, self.weekday).cmp(&(rhs, other.weekday))
242 }
243}
244
245#[derive(Clone, Copy, PartialEq, Eq, Hash)]
255pub struct SecondSet(NonZero<u64>);
256
257impl Debug for SecondSet {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 let mut set = BTreeSet::new();
260
261 for second in Second::iter() {
262 if self.get(second) {
263 set.insert(second);
264 }
265 }
266
267 write!(f, "{set:#?}")
268 }
269}
270
271impl SecondSet {
272 pub(crate) const EMPTY: Self = Self(NonZero::new(1 << 63).unwrap());
273
274 pub const fn get(&self, second: Second) -> bool {
276 let mask = 1 << (second as u8);
277 (self.0.get() & mask) != 0
278 }
279
280 pub const fn set(&mut self, second: Second) {
282 let mask = 1 << (second as u8);
283 let updated = self.0.get() | mask;
284
285 *self = Self(unsafe { NonZero::new_unchecked(updated) })
287 }
288}
289
290impl Default for SecondSet {
291 fn default() -> Self {
292 Self::EMPTY
293 }
294}
295
296#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
298#[repr(u8)]
299pub enum Second {
300 S0,
301 S1,
302 S2,
303 S3,
304 S4,
305 S5,
306 S6,
307 S7,
308 S8,
309 S9,
310 S10,
311 S11,
312 S12,
313 S13,
314 S14,
315 S15,
316 S16,
317 S17,
318 S18,
319 S19,
320 S20,
321 S21,
322 S22,
323 S23,
324 S24,
325 S25,
326 S26,
327 S27,
328 S28,
329 S29,
330 S30,
331 S31,
332 S32,
333 S33,
334 S34,
335 S35,
336 S36,
337 S37,
338 S38,
339 S39,
340 S40,
341 S41,
342 S42,
343 S43,
344 S44,
345 S45,
346 S46,
347 S47,
348 S48,
349 S49,
350 S50,
351 S51,
352 S52,
353 S53,
354 S54,
355 S55,
356 S56,
357 S57,
358 S58,
359 S59,
360 S60,
361}
362
363impl Debug for Second {
364 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365 let s = *self as u8;
366 write!(f, "{s:02}")
367 }
368}
369
370impl Second {
371 pub const fn from_repr(repr: u8) -> Option<Self> {
373 match repr {
374 0..=60 => {
375 Some(unsafe { std::mem::transmute::<u8, Self>(repr) })
378 }
379 _ => None,
380 }
381 }
382
383 pub fn iter() -> impl ExactSizeIterator<Item = Self> {
385 (0..=60u8).map(|s| {
386 unsafe { Self::from_repr(s).unwrap_unchecked() }
389 })
390 }
391}
392
393#[derive(Clone, Copy, PartialEq, Eq, Hash)]
403pub struct MinuteSet(NonZero<u64>);
404
405impl Debug for MinuteSet {
406 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
407 let mut set = BTreeSet::new();
408
409 for minute in Minute::iter() {
410 if self.get(minute) {
411 set.insert(minute);
412 }
413 }
414
415 write!(f, "{set:#?}")
416 }
417}
418
419impl MinuteSet {
420 pub(crate) const EMPTY: Self = Self(NonZero::new(1 << 63).unwrap());
421
422 pub const fn get(&self, minute: Minute) -> bool {
424 let mask = 1 << (minute as u8);
425 (self.0.get() & mask) != 0
426 }
427
428 pub const fn set(&mut self, minute: Minute) {
430 let mask = 1 << (minute as u8);
431 let updated = self.0.get() | mask;
432
433 *self = Self(unsafe { NonZero::new_unchecked(updated) })
435 }
436}
437
438impl Default for MinuteSet {
439 fn default() -> Self {
440 Self::EMPTY
441 }
442}
443
444#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
446#[repr(u8)]
447pub enum Minute {
448 M0,
449 M1,
450 M2,
451 M3,
452 M4,
453 M5,
454 M6,
455 M7,
456 M8,
457 M9,
458 M10,
459 M11,
460 M12,
461 M13,
462 M14,
463 M15,
464 M16,
465 M17,
466 M18,
467 M19,
468 M20,
469 M21,
470 M22,
471 M23,
472 M24,
473 M25,
474 M26,
475 M27,
476 M28,
477 M29,
478 M30,
479 M31,
480 M32,
481 M33,
482 M34,
483 M35,
484 M36,
485 M37,
486 M38,
487 M39,
488 M40,
489 M41,
490 M42,
491 M43,
492 M44,
493 M45,
494 M46,
495 M47,
496 M48,
497 M49,
498 M50,
499 M51,
500 M52,
501 M53,
502 M54,
503 M55,
504 M56,
505 M57,
506 M58,
507 M59,
508}
509
510impl Debug for Minute {
511 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
512 let m = *self as u8;
513 write!(f, "{m:02}")
514 }
515}
516
517impl Minute {
518 pub const fn from_repr(repr: u8) -> Option<Self> {
520 match repr {
521 0..=59 => {
522 Some(unsafe { std::mem::transmute::<u8, Self>(repr) })
525 }
526 _ => None,
527 }
528 }
529
530 pub fn iter() -> impl ExactSizeIterator<Item = Self> {
532 (0..=59u8).map(|m| {
533 unsafe { Self::from_repr(m).unwrap_unchecked() }
536 })
537 }
538}
539
540#[derive(Clone, Copy, PartialEq, Eq, Hash)]
550pub struct HourSet(NonZero<u32>);
551
552impl Debug for HourSet {
553 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
554 let mut set = BTreeSet::new();
555
556 for hour in Hour::iter() {
557 if self.get(hour) {
558 set.insert(hour);
559 }
560 }
561
562 write!(f, "{set:#?}")
563 }
564}
565
566impl HourSet {
567 pub(crate) const EMPTY: Self = Self(NonZero::new(1 << 31).unwrap());
568
569 pub const fn get(&self, hour: Hour) -> bool {
571 let mask = 1 << (hour as u8);
572 (self.0.get() & mask) != 0
573 }
574
575 pub const fn set(&mut self, hour: Hour) {
577 let mask = 1 << (hour as u8);
578 let updated = self.0.get() | mask;
579
580 *self = Self(unsafe { NonZero::new_unchecked(updated) })
582 }
583}
584
585impl Default for HourSet {
586 fn default() -> Self {
587 Self::EMPTY
588 }
589}
590
591#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
593#[repr(u8)]
594pub enum Hour {
595 H0,
596 H1,
597 H2,
598 H3,
599 H4,
600 H5,
601 H6,
602 H7,
603 H8,
604 H9,
605 H10,
606 H11,
607 H12,
608 H13,
609 H14,
610 H15,
611 H16,
612 H17,
613 H18,
614 H19,
615 H20,
616 H21,
617 H22,
618 H23,
619}
620
621impl Debug for Hour {
622 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
623 let h = *self as u8;
624 write!(f, "{h:02}")
625 }
626}
627
628impl Hour {
629 pub const fn from_repr(repr: u8) -> Option<Self> {
631 match repr {
632 0..=23 => {
633 Some(unsafe { std::mem::transmute::<u8, Self>(repr) })
636 }
637 _ => None,
638 }
639 }
640
641 pub fn iter() -> impl ExactSizeIterator<Item = Self> {
643 (0..=23u8).map(|h| {
644 unsafe { Self::from_repr(h).unwrap_unchecked() }
647 })
648 }
649}
650
651#[derive(Clone, Copy, PartialEq, Eq, Hash)]
662pub struct MonthSet(NonZero<u16>);
663
664impl Debug for MonthSet {
665 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
666 let mut set = BTreeSet::new();
667
668 for month in Month::iter() {
669 if self.get(month) {
670 set.insert(month);
671 }
672 }
673
674 write!(f, "{set:#?}")
675 }
676}
677
678impl MonthSet {
679 pub(crate) const EMPTY: Self = Self(NonZero::new(1 << 15).unwrap());
680
681 pub const fn get(&self, index: Month) -> bool {
683 let mask = 1 << index.number().get();
684 (self.0.get() & mask) != 0
685 }
686
687 pub const fn set(&mut self, index: Month) {
689 let mask = 1 << index.number().get();
690 let updated = self.0.get() | mask;
691
692 *self = Self(unsafe { NonZero::new_unchecked(updated) })
694 }
695}
696
697impl Default for MonthSet {
698 fn default() -> Self {
699 Self::EMPTY
700 }
701}
702
703#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
714pub struct MonthDaySet(NonZero<u64>);
715
716#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
718pub struct MonthDaySetIndex(NonZero<u8>);
719
720impl MonthDaySet {
721 pub(crate) const EMPTY: Self = Self(NonZero::new(1 << 63).unwrap());
722
723 pub const fn get(&self, index: MonthDaySetIndex) -> bool {
725 let mask = 1 << index.0.get();
726 (self.0.get() & mask) != 0
727 }
728
729 pub const fn set(&mut self, index: MonthDaySetIndex) {
731 let mask = 1 << index.0.get();
732 let updated = self.0.get() | mask;
733
734 *self = Self(unsafe { NonZero::new_unchecked(updated) })
736 }
737}
738
739impl MonthDaySetIndex {
740 pub const fn from_signed_month_day(sign: Sign, day: MonthDay) -> Self {
742 let day = day as u8;
743 let offset = match sign {
744 Sign::Pos => 0,
745 Sign::Neg => 31,
746 };
747
748 Self(unsafe { NonZero::new_unchecked(day + offset) })
750 }
751}
752
753impl Default for MonthDaySet {
754 fn default() -> Self {
755 Self::EMPTY
756 }
757}
758
759#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
771pub struct WeekNoSet(NonZero<u128>);
772
773#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
775pub struct WeekNoSetIndex(NonZero<u8>);
776
777impl WeekNoSet {
778 pub(crate) const EMPTY: Self = Self(NonZero::new(1 << 127).unwrap());
779
780 pub const fn get(&self, index: WeekNoSetIndex) -> bool {
782 let mask = 1 << (index.0.get());
783 (mask & self.0.get()) != 0
784 }
785
786 pub const fn set(&mut self, index: WeekNoSetIndex) {
788 let mask = 1 << (index.0.get());
789 let updated = mask | self.0.get();
790
791 *self = Self(unsafe { NonZero::new_unchecked(updated) })
793 }
794}
795
796impl WeekNoSetIndex {
797 pub const fn from_signed_week(sign: Sign, week: IsoWeek) -> Self {
799 let week = week as u8;
800 let offset = match sign {
801 Sign::Pos => 0,
802 Sign::Neg => 64,
803 };
804
805 Self(unsafe { NonZero::new_unchecked(week + offset) })
807 }
808}
809
810impl Default for WeekNoSet {
811 fn default() -> Self {
812 Self::EMPTY
813 }
814}
815
816#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
818#[repr(u8)]
819pub enum MonthDay {
820 D1 = 1,
821 D2,
822 D3,
823 D4,
824 D5,
825 D6,
826 D7,
827 D8,
828 D9,
829 D10,
830 D11,
831 D12,
832 D13,
833 D14,
834 D15,
835 D16,
836 D17,
837 D18,
838 D19,
839 D20,
840 D21,
841 D22,
842 D23,
843 D24,
844 D25,
845 D26,
846 D27,
847 D28,
848 D29,
849 D30,
850 D31,
851}
852
853impl MonthDay {
854 pub const fn from_repr(repr: u8) -> Option<Self> {
856 match repr {
857 1..=31 => {
858 Some(unsafe { std::mem::transmute::<u8, Self>(repr) })
861 }
862 _ => None,
863 }
864 }
865}
866
867#[derive(Debug, Clone, Copy, PartialEq, Eq)]
869pub enum PartName {
870 Freq,
871 Until,
872 Count,
873 Interval,
874 BySecond,
875 ByMinute,
876 ByHour,
877 ByDay,
878 ByMonthDay,
879 ByYearDay,
880 ByWeekNo,
881 ByMonth,
882 BySetPos,
883 WkSt,
884}
885
886impl From<&Part> for PartName {
887 fn from(value: &Part) -> Self {
888 match value {
889 Part::Freq(_) => Self::Freq,
890 Part::Until(_) => Self::Until,
891 Part::Count(_) => Self::Count,
892 Part::Interval(_) => Self::Interval,
893 Part::BySecond(_) => Self::BySecond,
894 Part::ByMinute(_) => Self::ByMinute,
895 Part::ByHour(_) => Self::ByHour,
896 Part::ByDay(_) => Self::ByDay,
897 Part::ByMonthDay(_) => Self::ByMonthDay,
898 Part::ByYearDay(_) => Self::ByYearDay,
899 Part::ByWeekNo(_) => Self::ByWeekNo,
900 Part::ByMonth(_) => Self::ByMonth,
901 Part::BySetPos(_) => Self::BySetPos,
902 Part::WkSt(_) => Self::WkSt,
903 }
904 }
905}
906
907impl From<ByRuleName> for PartName {
908 fn from(value: ByRuleName) -> Self {
909 match value {
910 ByRuleName::BySecond => Self::BySecond,
911 ByRuleName::ByMinute => Self::ByMinute,
912 ByRuleName::ByHour => Self::ByHour,
913 ByRuleName::ByDay => Self::ByDay,
914 ByRuleName::ByMonthDay => Self::ByMonthDay,
915 ByRuleName::ByYearDay => Self::ByYearDay,
916 ByRuleName::ByWeekNo => Self::ByWeekNo,
917 ByRuleName::ByMonth => Self::ByMonth,
918 ByRuleName::BySetPos => Self::BySetPos,
919 }
920 }
921}
922
923impl PartName {
924 pub const fn as_by_rule(&self) -> Option<ByRuleName> {
926 match self {
927 PartName::BySecond => Some(ByRuleName::BySecond),
928 PartName::ByMinute => Some(ByRuleName::ByMinute),
929 PartName::ByHour => Some(ByRuleName::ByHour),
930 PartName::ByDay => Some(ByRuleName::ByDay),
931 PartName::ByMonthDay => Some(ByRuleName::ByMonthDay),
932 PartName::ByYearDay => Some(ByRuleName::ByYearDay),
933 PartName::ByWeekNo => Some(ByRuleName::ByWeekNo),
934 PartName::ByMonth => Some(ByRuleName::ByMonth),
935 PartName::BySetPos => Some(ByRuleName::BySetPos),
936 _ => None,
937 }
938 }
939}
940
941#[derive(Debug, Clone, Copy, PartialEq, Eq)]
943pub enum ByRuleName {
944 BySecond,
945 ByMinute,
946 ByHour,
947 ByDay,
948 ByMonthDay,
949 ByYearDay,
950 ByWeekNo,
951 ByMonth,
952 BySetPos,
953}
954
955#[derive(Debug, Clone, Copy, PartialEq, Eq)]
957pub enum ByRuleBehavior {
958 Limit,
959 Expand,
960 Note1,
961 Note2,
962}
963
964impl ByRuleName {
965 pub const fn behavior_with(&self, freq: Freq) -> Option<ByRuleBehavior> {
968 match (*self, freq) {
969 (Self::ByMonth, Freq::Yearly) => Some(ByRuleBehavior::Expand),
970 (Self::ByWeekNo, Freq::Yearly) => Some(ByRuleBehavior::Expand),
971 (Self::ByWeekNo, _) => None,
972 (Self::ByYearDay, Freq::Secondly | Freq::Minutely | Freq::Hourly) => {
973 Some(ByRuleBehavior::Limit)
974 }
975 (Self::ByYearDay, Freq::Yearly) => Some(ByRuleBehavior::Expand),
976 (Self::ByYearDay, _) => None,
977 (Self::ByMonthDay, Freq::Weekly) => None,
978 (Self::ByMonthDay, Freq::Monthly | Freq::Yearly) => Some(ByRuleBehavior::Expand),
979 (Self::ByDay, Freq::Weekly) => Some(ByRuleBehavior::Expand),
980 (Self::ByDay, Freq::Monthly) => Some(ByRuleBehavior::Note1),
981 (Self::ByDay, Freq::Yearly) => Some(ByRuleBehavior::Note2),
982 (Self::ByHour, Freq::Secondly | Freq::Minutely | Freq::Hourly) => {
983 Some(ByRuleBehavior::Limit)
984 }
985 (Self::ByHour, _) => Some(ByRuleBehavior::Expand),
986 (Self::ByMinute, Freq::Secondly | Freq::Minutely) => Some(ByRuleBehavior::Limit),
987 (Self::ByMinute, _) => Some(ByRuleBehavior::Expand),
988 (Self::BySecond, Freq::Secondly) => Some(ByRuleBehavior::Limit),
989 (Self::BySecond, _) => Some(ByRuleBehavior::Expand),
990 _ => Some(ByRuleBehavior::Limit),
991 }
992 }
993}
994
995#[derive(Debug, Clone, PartialEq, Eq)]
997pub enum Part {
998 Freq(Freq),
999 Until(DateTimeOrDate),
1000 Count(u64),
1001 Interval(Interval),
1002 BySecond(SecondSet),
1003 ByMinute(MinuteSet),
1004 ByHour(HourSet),
1005 ByDay(WeekdayNumSet),
1006 ByMonthDay(MonthDaySet),
1007 ByYearDay(BTreeSet<YearDayNum>),
1008 ByWeekNo(WeekNoSet),
1009 ByMonth(MonthSet),
1010 BySetPos(BTreeSet<YearDayNum>),
1011 WkSt(Weekday),
1012}
1013
1014#[cfg(test)]
1015mod tests {
1016 use super::*;
1017
1018 #[test]
1019 fn second_set_empty() {
1020 let empty = SecondSet::default();
1021 let bitstring = format!("{:b}", empty.0);
1022 assert_eq!(bitstring.len(), 64);
1023
1024 let mut chars = bitstring.chars();
1025 assert_eq!(chars.next(), Some('1'));
1026
1027 for char in chars {
1028 assert_eq!(char, '0');
1029 }
1030 }
1031
1032 #[test]
1033 fn second_from_index() {
1034 for i in 0..=60 {
1035 assert!(Second::from_repr(i).is_some());
1036 }
1037
1038 for i in 61..=255 {
1039 assert!(Second::from_repr(i).is_none());
1040 }
1041 }
1042
1043 #[test]
1044 fn minute_set_empty() {
1045 let empty = MinuteSet::default();
1046 let bitstring = format!("{:b}", empty.0);
1047 assert_eq!(bitstring.len(), 64);
1048
1049 let mut chars = bitstring.chars();
1050 assert_eq!(chars.next(), Some('1'));
1051
1052 for char in chars {
1053 assert_eq!(char, '0');
1054 }
1055 }
1056
1057 #[test]
1058 fn minute_from_index() {
1059 for i in 0..=59 {
1060 assert!(Minute::from_repr(i).is_some());
1061 }
1062
1063 for i in 60..=255 {
1064 assert!(Minute::from_repr(i).is_none());
1065 }
1066 }
1067
1068 #[test]
1069 fn hour_set_empty() {
1070 let empty = HourSet::default();
1071 let bitstring = format!("{:b}", empty.0);
1072 assert_eq!(bitstring.len(), 32);
1073
1074 let mut chars = bitstring.chars();
1075 assert_eq!(chars.next(), Some('1'));
1076
1077 for char in chars {
1078 assert_eq!(char, '0');
1079 }
1080 }
1081
1082 #[test]
1083 fn hour_set_bit_twiddling() {
1084 let mut set = HourSet::default();
1085
1086 let i1 = Hour::H0;
1087 let i2 = Hour::H3;
1088 let i3 = Hour::H4;
1089 let i4 = Hour::H13;
1090 let i5 = Hour::H22;
1091
1092 for i in [i1, i2, i3, i4, i5] {
1093 assert!(!set.get(i));
1094 }
1095
1096 for i in [i1, i2, i3, i4, i5] {
1097 set.set(i);
1098 }
1099
1100 for i in [i1, i2, i3, i4, i5] {
1101 assert!(set.get(i));
1102 }
1103 }
1104
1105 #[test]
1106 fn month_set_empty() {
1107 let empty = MonthSet::default();
1108 let bitstring = format!("{:b}", empty.0);
1109 assert_eq!(bitstring.len(), 16);
1110
1111 let mut chars = bitstring.chars();
1112 assert_eq!(chars.next(), Some('1'));
1113
1114 for char in chars {
1115 assert_eq!(char, '0');
1116 }
1117 }
1118
1119 #[test]
1120 fn month_set_bit_twiddling() {
1121 let mut month_set = MonthSet::default();
1122
1123 let i1 = Month::Jan;
1124 let i2 = Month::Apr;
1125 let i3 = Month::Aug;
1126 let i4 = Month::Sep;
1127
1128 for i in [i1, i2, i3, i4] {
1129 assert!(!month_set.get(i));
1130 }
1131
1132 for i in [i1, i2, i3, i4] {
1133 month_set.set(i);
1134 }
1135
1136 for i in [i1, i2, i3, i4] {
1137 assert!(month_set.get(i));
1138 }
1139 }
1140
1141 #[test]
1142 fn month_day_set_empty() {
1143 let empty = MonthDaySet::default();
1144 let bitstring = format!("{:b}", empty.0);
1145 assert_eq!(bitstring.len(), 64);
1146
1147 let mut chars = bitstring.chars();
1148 assert_eq!(chars.next(), Some('1'));
1149
1150 for char in chars {
1151 assert_eq!(char, '0');
1152 }
1153 }
1154
1155 #[test]
1156 fn month_day_set_index_from_signed_month_day() {
1157 assert_eq!(
1158 MonthDaySetIndex::from_signed_month_day(Sign::Pos, MonthDay::D1)
1159 .0
1160 .get(),
1161 1
1162 );
1163
1164 assert_eq!(
1165 MonthDaySetIndex::from_signed_month_day(Sign::Pos, MonthDay::D31)
1166 .0
1167 .get(),
1168 31
1169 );
1170
1171 assert_eq!(
1172 MonthDaySetIndex::from_signed_month_day(Sign::Neg, MonthDay::D1)
1173 .0
1174 .get(),
1175 31 + 1
1176 );
1177
1178 assert_eq!(
1179 MonthDaySetIndex::from_signed_month_day(Sign::Neg, MonthDay::D31)
1180 .0
1181 .get(),
1182 31 + 31
1183 );
1184 }
1185
1186 #[test]
1187 fn month_day_set_bit_twiddling() {
1188 let mut month_day_set = MonthDaySet::default();
1189
1190 let i1 = MonthDaySetIndex::from_signed_month_day(Sign::Pos, MonthDay::D1);
1191 let i2 = MonthDaySetIndex::from_signed_month_day(Sign::Pos, MonthDay::D25);
1192 let i3 = MonthDaySetIndex::from_signed_month_day(Sign::Neg, MonthDay::D6);
1193
1194 for i in [i1, i2, i3] {
1195 assert!(!month_day_set.get(i));
1196 }
1197
1198 for i in [i1, i2, i3] {
1199 month_day_set.set(i);
1200 }
1201
1202 for i in [i1, i2, i3] {
1203 assert!(month_day_set.get(i));
1204 }
1205 }
1206
1207 #[test]
1208 fn week_no_set_empty() {
1209 let empty = WeekNoSet::default();
1210 let bitstring = format!("{:b}", empty.0);
1211 assert_eq!(bitstring.len(), 128);
1212
1213 let mut chars = bitstring.chars();
1214 assert_eq!(chars.next(), Some('1'));
1215
1216 for char in chars {
1217 assert_eq!(char, '0');
1218 }
1219 }
1220
1221 #[test]
1222 fn week_no_set_index_from_signed_week() {
1223 assert_eq!(
1224 WeekNoSetIndex::from_signed_week(Sign::Pos, IsoWeek::W1),
1225 WeekNoSetIndex(NonZero::new(1).unwrap())
1226 );
1227
1228 assert_eq!(
1229 WeekNoSetIndex::from_signed_week(Sign::Neg, IsoWeek::W1),
1230 WeekNoSetIndex(NonZero::new(64 + 1).unwrap())
1231 );
1232
1233 assert_eq!(
1234 WeekNoSetIndex::from_signed_week(Sign::Pos, IsoWeek::W53),
1235 WeekNoSetIndex(NonZero::new(53).unwrap())
1236 );
1237
1238 assert_eq!(
1239 WeekNoSetIndex::from_signed_week(Sign::Neg, IsoWeek::W53),
1240 WeekNoSetIndex(NonZero::new(64 + 53).unwrap())
1241 );
1242 }
1243
1244 #[test]
1245 fn week_no_set_bit_twiddling() {
1246 let mut week_no_set = WeekNoSet::default();
1247
1248 let i1 = WeekNoSetIndex::from_signed_week(Sign::Pos, IsoWeek::W12);
1249 let i2 = WeekNoSetIndex::from_signed_week(Sign::Neg, IsoWeek::W8);
1250 let i3 = WeekNoSetIndex::from_signed_week(Sign::Neg, IsoWeek::W37);
1251
1252 for i in [i1, i2, i3] {
1253 assert!(!week_no_set.get(i));
1254 }
1255
1256 for i in [i1, i2, i3] {
1257 week_no_set.set(i);
1258 }
1259
1260 for i in [i1, i2, i3] {
1261 assert!(week_no_set.get(i));
1262 }
1263 }
1264
1265 #[test]
1266 fn weekday_num_ord_impl() {
1267 let none_monday = WeekdayNum {
1268 ordinal: None,
1269 weekday: Weekday::Monday,
1270 };
1271
1272 let none_tuesday = WeekdayNum {
1273 ordinal: None,
1274 weekday: Weekday::Tuesday,
1275 };
1276
1277 let none_friday = WeekdayNum {
1278 ordinal: None,
1279 weekday: Weekday::Friday,
1280 };
1281
1282 assert!(none_monday < none_tuesday);
1283 assert!(none_monday < none_friday);
1284 assert!(none_tuesday < none_friday);
1285
1286 let sub_53_monday = WeekdayNum {
1287 ordinal: Some((Sign::Neg, IsoWeek::W53)),
1288 weekday: Weekday::Monday,
1289 };
1290
1291 let sub_50_monday = WeekdayNum {
1292 ordinal: Some((Sign::Neg, IsoWeek::W50)),
1293 weekday: Weekday::Monday,
1294 };
1295
1296 let sub_53_wednesday = WeekdayNum {
1297 ordinal: Some((Sign::Neg, IsoWeek::W53)),
1298 weekday: Weekday::Wednesday,
1299 };
1300
1301 let sub_50_thursday = WeekdayNum {
1302 ordinal: Some((Sign::Neg, IsoWeek::W50)),
1303 weekday: Weekday::Thursday,
1304 };
1305
1306 assert!(none_monday < sub_53_wednesday);
1307 assert!(none_tuesday < sub_53_wednesday);
1308 assert!(none_friday < sub_53_wednesday);
1309
1310 assert!(sub_53_monday < sub_53_wednesday);
1311 assert!(sub_53_monday < sub_50_monday);
1312 assert!(sub_53_wednesday < sub_50_monday);
1313 assert!(sub_53_wednesday < sub_50_thursday);
1314 assert!(sub_50_monday < sub_50_thursday);
1315
1316 let pos_53_monday = WeekdayNum {
1317 ordinal: Some((Sign::Pos, IsoWeek::W53)),
1318 weekday: Weekday::Monday,
1319 };
1320
1321 let pos_50_monday = WeekdayNum {
1322 ordinal: Some((Sign::Pos, IsoWeek::W50)),
1323 weekday: Weekday::Monday,
1324 };
1325
1326 let pos_53_wednesday = WeekdayNum {
1327 ordinal: Some((Sign::Pos, IsoWeek::W53)),
1328 weekday: Weekday::Wednesday,
1329 };
1330
1331 let pos_50_thursday = WeekdayNum {
1332 ordinal: Some((Sign::Pos, IsoWeek::W50)),
1333 weekday: Weekday::Thursday,
1334 };
1335
1336 assert!(sub_53_monday < pos_53_monday);
1337 assert!(sub_50_monday < pos_50_monday);
1338 assert!(sub_50_thursday < pos_50_thursday);
1339
1340 assert!(pos_50_thursday < pos_53_wednesday);
1341 assert!(pos_53_monday < pos_53_wednesday);
1342 }
1343
1344 #[test]
1345 fn behavior_with_table() {
1346 let freqs = [
1347 Freq::Secondly,
1348 Freq::Minutely,
1349 Freq::Hourly,
1350 Freq::Daily,
1351 Freq::Weekly,
1352 Freq::Monthly,
1353 Freq::Yearly,
1354 ];
1355
1356 let by_rules = [
1357 ByRuleName::ByMonth,
1358 ByRuleName::ByWeekNo,
1359 ByRuleName::ByYearDay,
1360 ByRuleName::ByMonthDay,
1361 ByRuleName::ByDay,
1362 ByRuleName::ByHour,
1363 ByRuleName::ByMinute,
1364 ByRuleName::BySecond,
1365 ByRuleName::BySetPos,
1366 ];
1367
1368 #[allow(non_snake_case)]
1369 let Limit = Some(ByRuleBehavior::Limit);
1370 #[allow(non_snake_case)]
1371 let Expand = Some(ByRuleBehavior::Expand);
1372 #[allow(non_snake_case)]
1373 let Note1 = Some(ByRuleBehavior::Note1);
1374 #[allow(non_snake_case)]
1375 let Note2 = Some(ByRuleBehavior::Note2);
1376
1377 let table = [
1379 [Limit, Limit, Limit, Limit, Limit, Limit, Expand],
1380 [None, None, None, None, None, None, Expand],
1381 [Limit, Limit, Limit, None, None, None, Expand],
1382 [Limit, Limit, Limit, Limit, None, Expand, Expand],
1383 [Limit, Limit, Limit, Limit, Expand, Note1, Note2],
1384 [Limit, Limit, Limit, Expand, Expand, Expand, Expand],
1385 [Limit, Limit, Expand, Expand, Expand, Expand, Expand],
1386 [Limit, Expand, Expand, Expand, Expand, Expand, Expand],
1387 [Limit, Limit, Limit, Limit, Limit, Limit, Limit],
1388 ];
1389
1390 for (i, &rule) in by_rules.iter().enumerate() {
1391 for (j, &freq) in freqs.iter().enumerate() {
1392 let expected = table[i][j];
1393 let got = rule.behavior_with(freq);
1394 assert_eq!(got, expected);
1395 }
1396 }
1397 }
1398}