1#![deny(missing_docs)]
91
92use std::{
93 fmt::Display,
94 ops::{Add, Div, Mul, Sub},
95 str::FromStr,
96};
97use syn::{
98 parse::{Parse, ParseStream, Result},
99 Error, Ident, LitInt, Token,
100};
101
102#[cfg(test)]
103mod tests;
104
105#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
393pub enum TimeExpression {
394 Specific(PointInTime), Range(TimeRange), Duration(Duration), }
401
402impl Parse for TimeExpression {
403 fn parse(input: ParseStream) -> Result<Self> {
404 if !input.peek(Ident) && !input.peek(LitInt) {
405 return Err(Error::new(input.span(), "expected [number] or [keyword]"));
406 }
407 if input.peek(Ident) {
408 let ident = input.fork().parse::<Ident>()?;
409 if ident.to_string().to_lowercase().as_str() == "from" {
410 return Ok(TimeExpression::Range(input.parse()?));
411 }
412 return Ok(TimeExpression::Specific(input.parse()?));
413 }
414 if input.peek(LitInt) && input.peek2(Token![/]) {
415 return Ok(TimeExpression::Specific(input.parse()?));
417 }
418 let fork = input.fork();
420 if fork.parse::<PointInTime>().is_ok() {
421 return Ok(TimeExpression::Specific(input.parse()?));
422 }
423 Ok(TimeExpression::Duration(input.parse()?))
424 }
425}
426
427impl Display for TimeExpression {
428 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
429 match self {
430 TimeExpression::Specific(point) => write!(f, "{point}"),
431 TimeExpression::Range(tr) => write!(f, "{tr}"),
432 TimeExpression::Duration(dur) => write!(f, "{dur}"),
433 }
434 }
435}
436
437#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
440pub struct TimeRange(pub PointInTime, pub PointInTime);
441
442impl Parse for TimeRange {
443 fn parse(input: ParseStream) -> Result<Self> {
444 let ident = input.parse::<Ident>()?;
445 if ident.to_string().to_lowercase() != "from" {
446 return Err(Error::new(ident.span(), "expected `from`"));
447 }
448 let t1 = input.parse::<PointInTime>()?;
449 let ident = input.parse::<Ident>()?;
450 if ident.to_string().to_lowercase() != "to" {
451 return Err(Error::new(ident.span(), "expected `to`"));
452 }
453 let t2 = input.parse::<PointInTime>()?;
454 Ok(TimeRange(t1, t2))
455 }
456}
457
458impl Display for TimeRange {
459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460 write!(f, "from {} to {}", self.0, self.1)
461 }
462}
463
464#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
469pub struct Duration {
470 pub minutes: Number,
472 pub hours: Number,
474 pub days: Number,
476 pub weeks: Number,
478 pub months: Number,
480 pub years: Number,
482}
483
484impl Parse for Duration {
485 fn parse(input: ParseStream) -> Result<Self> {
486 let mut minutes: Option<Number> = None;
487 let mut hours: Option<Number> = None;
488 let mut days: Option<Number> = None;
489 let mut weeks: Option<Number> = None;
490 let mut months: Option<Number> = None;
491 let mut years: Option<Number> = None;
492 while input.peek(LitInt) {
493 let num = input.parse::<Number>()?;
494 let unit = input.parse::<TimeUnit>()?;
495 match unit {
496 TimeUnit::Minutes => minutes = Some(minutes.unwrap_or(Number(0)) + num),
497 TimeUnit::Hours => hours = Some(hours.unwrap_or(Number(0)) + num),
498 TimeUnit::Days => days = Some(days.unwrap_or(Number(0)) + num),
499 TimeUnit::Weeks => weeks = Some(weeks.unwrap_or(Number(0)) + num),
500 TimeUnit::Months => months = Some(months.unwrap_or(Number(0)) + num),
501 TimeUnit::Years => years = Some(years.unwrap_or(Number(0)) + num),
502 }
503 if input.peek(Token![,]) {
504 input.parse::<Token![,]>()?;
505 }
506 if input.peek(Ident) {
507 let ident = input.fork().parse::<Ident>()?; if ident.to_string().to_lowercase() == "and" {
509 input.parse::<Ident>()?; }
511 }
512 }
513 if minutes.is_none()
514 && hours.is_none()
515 && days.is_none()
516 && weeks.is_none()
517 && months.is_none()
518 && years.is_none()
519 {
520 return Err(Error::new(
521 input.span(),
522 "expected [number] followed by one of `minutes`, `hours`, `days`, `years`",
523 ));
524 }
525 Ok(Duration {
526 minutes: minutes.unwrap_or(Number(0)),
527 hours: hours.unwrap_or(Number(0)),
528 days: days.unwrap_or(Number(0)),
529 weeks: weeks.unwrap_or(Number(0)),
530 months: months.unwrap_or(Number(0)),
531 years: years.unwrap_or(Number(0)),
532 })
533 }
534}
535
536impl Display for Duration {
537 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
538 let mut before = false;
539 if self.years > 0 {
540 before = true;
541 }
542 if self.years == 1 {
543 write!(f, "1 year")?;
544 } else if self.years > 1 {
545 write!(f, "{} years", self.years)?;
546 }
547 if self.months > 0 {
548 if before {
549 write!(f, ", ")?;
550 }
551 before = true;
552 }
553 if self.months == 1 {
554 write!(f, "1 month")?;
555 } else if self.months > 1 {
556 write!(f, "{} months", self.months)?;
557 }
558 if self.weeks > 0 {
559 if before {
560 write!(f, ", ")?;
561 }
562 before = true;
563 }
564 if self.weeks == 1 {
565 write!(f, "1 week")?;
566 } else if self.weeks > 1 {
567 write!(f, "{} weeks", self.weeks)?;
568 }
569 if self.days > 0 {
570 if before {
571 write!(f, ", ")?;
572 }
573 before = true;
574 }
575 if self.days == 1 {
576 write!(f, "1 day")?;
577 } else if self.days > 1 {
578 write!(f, "{} days", self.days)?;
579 }
580 if self.hours > 0 {
581 if before {
582 write!(f, ", ")?;
583 }
584 before = true;
585 }
586 if self.hours == 1 {
587 write!(f, "1 hour")?;
588 } else if self.hours > 1 {
589 write!(f, "{} hours", self.hours)?;
590 }
591 if self.minutes > 0 {
592 if before {
593 write!(f, ", ")?;
594 }
595 }
596 if self.minutes == 1 {
597 write!(f, "1 minute")?;
598 } else if self.minutes > 1 {
599 write!(f, "{} minutes", self.minutes)?;
600 }
601 Ok(())
602 }
603}
604
605#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
609pub enum PointInTime {
610 Absolute(AbsoluteTime),
613 Relative(RelativeTime),
615}
616
617impl Parse for PointInTime {
618 fn parse(input: ParseStream) -> Result<Self> {
619 if input.peek(LitInt) && input.peek2(Token![/]) {
620 Ok(PointInTime::Absolute(input.parse::<AbsoluteTime>()?))
621 } else {
622 Ok(PointInTime::Relative(input.parse::<RelativeTime>()?))
623 }
624 }
625}
626
627impl Display for PointInTime {
628 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
629 match self {
630 PointInTime::Absolute(abs) => write!(f, "{abs}"),
631 PointInTime::Relative(rel) => write!(f, "{rel}"),
632 }
633 }
634}
635
636#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
638pub enum AbsoluteTime {
639 Date(Date),
641 DateTime(DateTime),
643}
644
645impl Parse for AbsoluteTime {
646 fn parse(input: ParseStream) -> Result<Self> {
647 let fork = input.fork();
648 fork.parse::<Date>()?;
649 if (fork.peek(LitInt) && fork.peek2(Token![:]) && fork.peek3(LitInt))
650 || (fork.peek(Ident) && fork.peek2(LitInt) && fork.peek3(Token![:]))
651 {
652 return Ok(AbsoluteTime::DateTime(input.parse()?));
653 }
654 Ok(AbsoluteTime::Date(input.parse()?))
655 }
656}
657
658impl Display for AbsoluteTime {
659 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
660 match self {
661 AbsoluteTime::Date(date) => write!(f, "{}", date),
662 AbsoluteTime::DateTime(date_time) => write!(f, "{}", date_time),
663 }
664 }
665}
666
667#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
669pub enum RelativeTimeUnit {
670 Week,
672 Month,
674 Year,
676 Monday,
678 Tuesday,
680 Wednesday,
682 Thursday,
684 Friday,
686 Saturday,
688 Sunday,
690}
691
692impl Parse for RelativeTimeUnit {
693 fn parse(input: ParseStream) -> Result<Self> {
694 let ident = input.parse::<Ident>()?;
695 match ident.to_string().to_lowercase().as_str() {
696 "week" => Ok(RelativeTimeUnit::Week),
697 "month" => Ok(RelativeTimeUnit::Month),
698 "year" => Ok(RelativeTimeUnit::Year),
699 "monday" => Ok(RelativeTimeUnit::Monday),
700 "tuesday" => Ok(RelativeTimeUnit::Tuesday),
701 "wednesday" => Ok(RelativeTimeUnit::Wednesday),
702 "thursday" => Ok(RelativeTimeUnit::Thursday),
703 "friday" => Ok(RelativeTimeUnit::Friday),
704 "saturday" => Ok(RelativeTimeUnit::Saturday),
705 "sunday" => Ok(RelativeTimeUnit::Sunday),
706 _ => Err(Error::new(
707 ident.span(),
708 "expected one of `week`, `month`, `year`, `monday`, `tuesday`, `wednesday`, \
709 `thursday`, `friday`, `saturday` or `sunday`",
710 )),
711 }
712 }
713}
714
715impl Display for RelativeTimeUnit {
716 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
717 match self {
718 RelativeTimeUnit::Week => f.write_str("week"),
719 RelativeTimeUnit::Month => f.write_str("month"),
720 RelativeTimeUnit::Year => f.write_str("year"),
721 RelativeTimeUnit::Monday => f.write_str("Monday"),
722 RelativeTimeUnit::Tuesday => f.write_str("Tuesday"),
723 RelativeTimeUnit::Wednesday => f.write_str("Wednesday"),
724 RelativeTimeUnit::Thursday => f.write_str("Thursday"),
725 RelativeTimeUnit::Friday => f.write_str("Friday"),
726 RelativeTimeUnit::Saturday => f.write_str("Saturday"),
727 RelativeTimeUnit::Sunday => f.write_str("Sunday"),
728 }
729 }
730}
731
732#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
734pub enum NamedRelativeTime {
735 Now,
737 Today,
739 Tomorrow,
741 Yesterday,
743 DayAfterTomorrow,
745 DayBeforeYesterday,
747}
748
749impl Parse for NamedRelativeTime {
750 fn parse(input: ParseStream) -> Result<Self> {
751 let mut ident1 = input.parse::<Ident>()?;
752 if let Some(variant) = match ident1.to_string().to_lowercase().as_str() {
753 "now" => Some(NamedRelativeTime::Now),
754 "today" => Some(NamedRelativeTime::Today),
755 "tomorrow" => Some(NamedRelativeTime::Tomorrow),
756 "yesterday" => Some(NamedRelativeTime::Yesterday),
757 _ => None,
758 } {
759 return Ok(variant);
761 }
762 if ident1 == "the" && input.peek(Ident) {
763 ident1 = input.parse::<Ident>()?;
765 }
766 let ident2 = input.parse::<Ident>()?;
767 let ident3 = input.parse::<Ident>()?;
768 let ident1_str = ident1.to_string().to_lowercase();
769 let ident2_str = ident2.to_string().to_lowercase();
770 let ident3_str = ident3.to_string().to_lowercase();
771 match (
772 ident1_str.as_str(),
773 ident2_str.as_str(),
774 ident3_str.as_str(),
775 ) {
776 ("day", "after", "tomorrow") => Ok(NamedRelativeTime::DayAfterTomorrow),
777 ("day", "before", "yesterday") => Ok(NamedRelativeTime::DayBeforeYesterday),
778 _ => {
779 if ident1_str != "day" {
780 return Err(Error::new(
781 ident1.span(),
782 "expected one of `day`, `now`, `today`, `tomorrow`, `yesterday`, `the`",
783 ));
784 }
785 if ident2_str != "before" && ident2_str != "after" {
786 return Err(Error::new(ident2.span(), "expected `before` or `after`"));
787 }
788 if ident3_str == "tomorrow" {
789 Err(Error::new(ident3.span(), "expected `yesterday`"))
790 } else {
791 Err(Error::new(ident3.span(), "expected `tomorrow`"))
792 }
793 }
794 }
795 }
796}
797
798impl Display for NamedRelativeTime {
799 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
800 match self {
801 NamedRelativeTime::Now => f.write_str("now"),
802 NamedRelativeTime::Today => f.write_str("today"),
803 NamedRelativeTime::Tomorrow => f.write_str("tomorrow"),
804 NamedRelativeTime::Yesterday => f.write_str("yesterday"),
805 NamedRelativeTime::DayAfterTomorrow => f.write_str("the day after tomorrow"),
806 NamedRelativeTime::DayBeforeYesterday => f.write_str("the day before yesterday"),
807 }
808 }
809}
810
811#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
814pub enum RelativeTime {
815 Directional {
818 duration: Duration,
820 dir: TimeDirection,
822 },
823 Named(NamedRelativeTime),
825 Next(RelativeTimeUnit),
827 Last(RelativeTimeUnit),
829}
830
831impl Parse for RelativeTime {
832 fn parse(input: ParseStream) -> Result<Self> {
833 let fork = input.fork();
834 if fork.peek(Ident) {
835 let ident1 = fork.parse::<Ident>().unwrap().to_string().to_lowercase();
836 match ident1.as_str() {
837 "next" | "last" => {
838 input.parse::<Ident>()?;
840 let unit = input.parse::<RelativeTimeUnit>()?;
841 if ident1 == "next" {
842 return Ok(RelativeTime::Next(unit));
843 } else {
844 return Ok(RelativeTime::Last(unit));
845 }
846 }
847 "day" | "now" | "today" | "tomorrow" | "yesterday" | "the" => {
848 return Ok(RelativeTime::Named(input.parse::<NamedRelativeTime>()?))
849 }
850 _ => (),
851 }
852 }
853 let duration = input.parse::<Duration>()?;
854 let dir = input.parse::<TimeDirection>()?;
855 Ok(RelativeTime::Directional { duration, dir })
856 }
857}
858
859impl Display for RelativeTime {
860 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
861 match self {
862 RelativeTime::Directional { duration, dir } => write!(f, "{duration} {dir}"),
863 RelativeTime::Next(unit) => write!(f, "next {unit}"),
864 RelativeTime::Last(unit) => write!(f, "last {unit}"),
865 RelativeTime::Named(named) => write!(f, "{named}"),
866 }
867 }
868}
869
870#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
872pub struct Date(pub Month, pub DayOfMonth, pub Year);
873
874impl Parse for Date {
875 fn parse(input: ParseStream) -> Result<Self> {
876 let day = input.parse::<DayOfMonth>()?;
877 input.parse::<Token![/]>()?;
878 let month = input.parse::<Month>()?;
879 input.parse::<Token![/]>()?;
880 let year = input.parse::<Year>()?;
881 Ok(Date(month, day, year))
882 }
883}
884
885impl Display for Date {
886 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
887 f.write_fmt(format_args!("{}/{}/{}", self.1, self.0, self.2))
888 }
889}
890
891#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
896pub struct DateTime(pub Date, pub Time); impl Parse for DateTime {
899 fn parse(input: ParseStream) -> Result<Self> {
900 let date = input.parse::<Date>()?;
901 if input.peek(Ident) {
902 let ident = input.parse::<Ident>()?;
903 if ident.to_string().to_lowercase().as_str() != "at" {
904 return Err(Error::new(ident.span(), "expected `at`"));
905 }
906 }
907 let time = input.parse::<Time>()?;
908 Ok(DateTime(date, time))
909 }
910}
911
912impl Display for DateTime {
913 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
914 f.write_fmt(format_args!("{} at {}", self.0, self.1))
915 }
916}
917
918#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
922pub struct Time(pub Hour, pub Minute);
923
924impl Parse for Time {
925 fn parse(input: ParseStream) -> Result<Self> {
926 let hour_lit = input.parse::<LitInt>()?;
927 let hour_val = hour_lit.base10_parse::<u8>()?;
928 input.parse::<Token![:]>()?;
929 let min = input.parse::<Minute>()?;
930 if input.peek(Ident)
931 && ["am", "pm"].contains(
932 &input
933 .fork()
934 .parse::<Ident>()
935 .unwrap()
936 .to_string()
937 .to_lowercase()
938 .as_str(),
939 )
940 {
941 let am_pm = input.parse::<AmPm>()?;
942 if hour_val > 12 || hour_val == 0 {
943 return Err(Error::new(
944 hour_lit.span(),
945 "hour must be between 1 and 12 (inclusive)",
946 ));
947 }
948 return Ok(Time(Hour::Hour12(hour_val, am_pm), min));
949 }
950 if hour_val > 24 {
951 return Err(Error::new(
952 hour_lit.span(),
953 "hour must be between 0 and 24 (inclusive)",
954 ));
955 }
956 Ok(Time(Hour::Hour24(hour_val), min))
957 }
958}
959
960impl Display for Time {
961 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
962 match self {
963 Time(Hour::Hour12(hour, am_pm), minute) => {
964 write!(f, "{}:{:02} {}", hour, minute, am_pm)
965 }
966 Time(Hour::Hour24(hour), minute) => write!(f, "{}:{:02}", hour, minute),
967 }
968 }
969}
970
971#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
973pub struct DayOfMonth(pub u8);
974
975impl Parse for DayOfMonth {
976 fn parse(input: ParseStream) -> Result<Self> {
977 let lit = input.parse::<LitInt>()?;
978 let int_val = lit.base10_parse::<u8>()?;
979 if int_val > 31 || int_val == 0 {
980 return Err(Error::new(
981 lit.span(),
982 "day must be between 1 and 31 (inclusive)",
983 ));
984 }
985 Ok(DayOfMonth(int_val))
986 }
987}
988
989impl Display for DayOfMonth {
990 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
991 f.write_fmt(format_args!("{}", self.0))
992 }
993}
994
995#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
997pub struct Year(pub u16);
998
999impl Parse for Year {
1000 fn parse(input: ParseStream) -> Result<Self> {
1001 let lit = input.parse::<LitInt>()?;
1002 let int_val = lit.base10_parse::<u16>()?;
1003 Ok(Year(int_val))
1004 }
1005}
1006
1007impl Display for Year {
1008 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1009 f.write_fmt(format_args!("{}", self.0))
1010 }
1011}
1012
1013#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
1015pub enum Hour {
1016 Hour12(u8, AmPm),
1018 Hour24(u8),
1020}
1021
1022impl Parse for Hour {
1023 fn parse(input: ParseStream) -> Result<Self> {
1024 let lit = input.parse::<LitInt>()?;
1025 let int_val = lit.base10_parse::<u8>()?;
1026 if let Ok(am_pm) = input.parse::<AmPm>() {
1027 if int_val > 12 || int_val == 0 {
1028 return Err(Error::new(
1029 lit.span(),
1030 "hour must be between 1 and 12 (inclusive)",
1031 ));
1032 }
1033 return Ok(Hour::Hour12(int_val, am_pm));
1034 }
1035 if int_val > 24 {
1036 return Err(Error::new(
1037 lit.span(),
1038 "hour must be between 0 and 24 (inclusive)",
1039 ));
1040 }
1041 Ok(Hour::Hour24(int_val))
1042 }
1043}
1044
1045impl Display for Hour {
1046 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1047 match self {
1048 Hour::Hour12(hour, am_pm) => f.write_fmt(format_args!("{hour} {am_pm}",)),
1049 Hour::Hour24(hour) => f.write_fmt(format_args!("{hour}")),
1050 }
1051 }
1052}
1053
1054#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
1056pub struct Minute(pub u8);
1057
1058impl Parse for Minute {
1059 fn parse(input: ParseStream) -> Result<Self> {
1060 let lit = input.parse::<LitInt>()?;
1061 let int_val = lit.base10_parse::<u8>()?;
1062 if int_val > 60 {
1063 return Err(Error::new(
1064 lit.span(),
1065 "minute must be between 0 and 60 (inclusive)",
1066 ));
1067 }
1068 Ok(Minute(int_val))
1069 }
1070}
1071
1072impl Display for Minute {
1073 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1074 f.write_fmt(format_args!("{:02}", self.0))
1075 }
1076}
1077
1078#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
1080#[repr(u8)]
1081pub enum Month {
1082 January = 1,
1084 February,
1086 March,
1088 April,
1090 May,
1092 June,
1094 July,
1096 August,
1098 September,
1100 October,
1102 November,
1104 December,
1106}
1107
1108impl Parse for Month {
1109 fn parse(input: ParseStream) -> Result<Self> {
1110 let lit = input.parse::<LitInt>()?;
1111 let int_val = lit.base10_parse::<u8>()?;
1112 if int_val > 12 || int_val == 0 {
1113 return Err(Error::new(
1114 lit.span(),
1115 "month must be between 1 and 12 (inclusive)",
1116 ));
1117 }
1118 use Month::*;
1119 Ok(match int_val {
1120 1 => January,
1121 2 => February,
1122 3 => March,
1123 4 => April,
1124 5 => May,
1125 6 => June,
1126 7 => July,
1127 8 => August,
1128 9 => September,
1129 10 => October,
1130 11 => November,
1131 12 => December,
1132 _ => unreachable!(),
1133 })
1134 }
1135}
1136
1137impl From<Month> for u8 {
1138 fn from(value: Month) -> Self {
1139 use Month::*;
1140 match value {
1141 January => 1,
1142 February => 2,
1143 March => 3,
1144 April => 4,
1145 May => 5,
1146 June => 6,
1147 July => 7,
1148 August => 8,
1149 September => 9,
1150 October => 10,
1151 November => 11,
1152 December => 12,
1153 }
1154 }
1155}
1156
1157impl From<&Month> for u8 {
1158 fn from(value: &Month) -> Self {
1159 (*value).into()
1160 }
1161}
1162
1163impl Display for Month {
1164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1165 let as_u8: u8 = self.into();
1166 f.write_fmt(format_args!("{}", as_u8))
1167 }
1168}
1169
1170#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
1172pub enum AmPm {
1173 AM,
1175 PM,
1177}
1178
1179impl Parse for AmPm {
1180 fn parse(input: ParseStream) -> Result<Self> {
1181 let ident = input.parse::<Ident>()?;
1182 match ident.to_string().to_lowercase().as_str() {
1183 "am" => Ok(AmPm::AM),
1184 "pm" => Ok(AmPm::PM),
1185 _ => Err(Error::new(ident.span(), "expected `AM` or `PM`")),
1186 }
1187 }
1188}
1189
1190impl Display for AmPm {
1191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1192 match self {
1193 AmPm::AM => f.write_str("AM"),
1194 AmPm::PM => f.write_str("PM"),
1195 }
1196 }
1197}
1198
1199impl AsRef<str> for AmPm {
1200 fn as_ref(&self) -> &str {
1201 match self {
1202 AmPm::AM => "AM",
1203 AmPm::PM => "PM",
1204 }
1205 }
1206}
1207
1208#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
1210pub enum TimeUnit {
1211 Minutes,
1213 Hours,
1215 Days,
1217 Weeks,
1219 Months,
1221 Years,
1223}
1224
1225impl Parse for TimeUnit {
1226 fn parse(input: ParseStream) -> Result<Self> {
1227 let ident = input.parse::<Ident>()?;
1228 use TimeUnit::*;
1229 Ok(match ident.to_string().to_lowercase().as_str() {
1230 "mins" | "minutes" | "minute" | "min" => Minutes,
1231 "hours" | "hrs" | "hour" | "hr" => Hours,
1232 "days" | "day" => Days,
1233 "weeks" | "week" => Weeks,
1234 "months" | "month" => Months,
1235 "years" | "yr" | "year" => Years,
1236 _ => {
1237 return Err(Error::new(
1238 ident.span(),
1239 "expected one of `minutes`, `hours`, `days`, `weeks`, `months`, and `years`",
1240 ))
1241 }
1242 })
1243 }
1244}
1245
1246impl AsRef<str> for TimeUnit {
1247 fn as_ref(&self) -> &str {
1248 match self {
1249 TimeUnit::Minutes => "minutes",
1250 TimeUnit::Hours => "hours",
1251 TimeUnit::Days => "days",
1252 TimeUnit::Weeks => "minutes",
1253 TimeUnit::Months => "months",
1254 TimeUnit::Years => "years",
1255 }
1256 }
1257}
1258
1259impl Display for TimeUnit {
1260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1261 f.write_str(self.as_ref())
1262 }
1263}
1264
1265#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
1267pub enum TimeDirection {
1268 AfterAbsolute(AbsoluteTime),
1270 BeforeAbsolute(AbsoluteTime),
1272 AfterNamed(NamedRelativeTime),
1274 BeforeNamed(NamedRelativeTime),
1276 BeforeNext(RelativeTimeUnit),
1278 BeforeLast(RelativeTimeUnit),
1280 AfterNext(RelativeTimeUnit),
1282 AfterLast(RelativeTimeUnit),
1284 Ago,
1286 FromNow,
1288}
1289
1290impl Parse for TimeDirection {
1291 fn parse(input: ParseStream) -> Result<Self> {
1292 let ident1 = input.parse::<Ident>()?;
1293 match ident1.to_string().to_lowercase().as_str() {
1294 "after" => {
1295 if input.peek(LitInt) {
1296 Ok(TimeDirection::AfterAbsolute(input.parse()?))
1297 } else {
1298 let ident2 = input.fork().parse::<Ident>()?.to_string().to_lowercase();
1299 match ident2.as_str() {
1300 "next" => {
1301 input.parse::<Ident>()?;
1302 Ok(TimeDirection::AfterNext(input.parse()?))
1303 }
1304 "last" => {
1305 input.parse::<Ident>()?;
1306 Ok(TimeDirection::AfterLast(input.parse()?))
1307 }
1308 _ => Ok(TimeDirection::AfterNamed(input.parse()?)),
1309 }
1310 }
1311 }
1312 "before" => {
1313 if input.peek(LitInt) {
1314 Ok(TimeDirection::BeforeAbsolute(input.parse()?))
1315 } else {
1316 let ident2 = input.fork().parse::<Ident>()?.to_string().to_lowercase();
1317 match ident2.as_str() {
1318 "next" => {
1319 input.parse::<Ident>()?;
1320 Ok(TimeDirection::BeforeNext(input.parse()?))
1321 }
1322 "last" => {
1323 input.parse::<Ident>()?;
1324 Ok(TimeDirection::BeforeLast(input.parse()?))
1325 }
1326 _ => Ok(TimeDirection::BeforeNamed(input.parse()?)),
1327 }
1328 }
1329 }
1330 "ago" => Ok(TimeDirection::Ago),
1331 "from" => {
1332 let ident2 = input.parse::<Ident>()?;
1333 if ident2.to_string().to_lowercase().as_str() != "now" {
1334 return Err(Error::new(ident2.span(), "expected `now`"));
1335 }
1336 Ok(TimeDirection::FromNow)
1337 }
1338 _ => Err(Error::new(
1339 ident1.span(),
1340 "expected one of `after`, `before`, `ago`, `from`",
1341 )),
1342 }
1343 }
1344}
1345
1346impl Display for TimeDirection {
1347 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1348 match self {
1349 TimeDirection::AfterAbsolute(abs_time) => write!(f, "after {abs_time}"),
1350 TimeDirection::BeforeAbsolute(abs_time) => write!(f, "before {abs_time}"),
1351 TimeDirection::Ago => f.write_str("ago"),
1352 TimeDirection::FromNow => f.write_str("from now"),
1353 TimeDirection::AfterNamed(named) => write!(f, "after {named}"),
1354 TimeDirection::BeforeNamed(named) => write!(f, "before {named}"),
1355 TimeDirection::BeforeNext(unit) => write!(f, "before next {unit}"),
1356 TimeDirection::BeforeLast(unit) => write!(f, "before last {unit}"),
1357 TimeDirection::AfterNext(unit) => write!(f, "after next {unit}"),
1358 TimeDirection::AfterLast(unit) => write!(f, "after last {unit}"),
1359 }
1360 }
1361}
1362
1363#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
1365pub struct Number(pub u64);
1366
1367impl From<u64> for Number {
1368 fn from(value: u64) -> Self {
1369 Number(value)
1370 }
1371}
1372
1373impl From<Number> for u64 {
1374 fn from(value: Number) -> Self {
1375 value.0
1376 }
1377}
1378
1379impl Add for Number {
1380 type Output = Number;
1381
1382 fn add(self, rhs: Self) -> Self::Output {
1383 Number(self.0 + rhs.0)
1384 }
1385}
1386
1387impl Sub for Number {
1388 type Output = Number;
1389
1390 fn sub(self, rhs: Self) -> Self::Output {
1391 Number(self.0 - rhs.0)
1392 }
1393}
1394
1395impl Mul for Number {
1396 type Output = Number;
1397
1398 fn mul(self, rhs: Self) -> Self::Output {
1399 Number(self.0 * rhs.0)
1400 }
1401}
1402
1403impl Div for Number {
1404 type Output = Number;
1405
1406 fn div(self, rhs: Self) -> Self::Output {
1407 Number(self.0 / rhs.0)
1408 }
1409}
1410
1411impl PartialEq<u64> for Number {
1412 fn eq(&self, other: &u64) -> bool {
1413 self.0 == *other
1414 }
1415}
1416
1417impl PartialOrd<u64> for Number {
1418 fn partial_cmp(&self, other: &u64) -> Option<std::cmp::Ordering> {
1419 self.0.partial_cmp(other)
1420 }
1421}
1422
1423impl Parse for Number {
1424 fn parse(input: ParseStream) -> Result<Self> {
1425 let lit = input.parse::<LitInt>()?;
1426 let int_val = lit.base10_parse::<u64>()?;
1427 Ok(Number(int_val))
1428 }
1429}
1430
1431impl Display for Number {
1432 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1433 f.write_fmt(format_args!("{}", self.0))
1434 }
1435}
1436
1437macro_rules! impl_parse_str {
1438 ($ident:ident) => {
1439 impl FromStr for $ident {
1440 type Err = syn::Error;
1441
1442 fn from_str(s: &str) -> std::prelude::v1::Result<Self, Self::Err> {
1443 syn::parse_str(s)
1444 }
1445 }
1446 };
1447}
1448
1449impl_parse_str!(TimeExpression);
1450impl_parse_str!(TimeDirection);
1451impl_parse_str!(TimeUnit);
1452impl_parse_str!(TimeRange);
1453impl_parse_str!(AmPm);
1454impl_parse_str!(DayOfMonth);
1455impl_parse_str!(Minute);
1456impl_parse_str!(Month);
1457impl_parse_str!(Hour);
1458impl_parse_str!(AbsoluteTime);
1459impl_parse_str!(Duration);
1460impl_parse_str!(RelativeTime);
1461impl_parse_str!(PointInTime);
1462impl_parse_str!(Time);
1463impl_parse_str!(DateTime);
1464impl_parse_str!(RelativeTimeUnit);
1465impl_parse_str!(NamedRelativeTime);
1466
1467#[cfg(test)]
1468macro_rules! assert_impl_all {
1469 ($($typ:ty),* : $($tt:tt)*) => {{
1470 const fn _assert_impl<T>() where T: $($tt)*, {}
1471 $(_assert_impl::<$typ>();)*
1472 }};
1473}
1474
1475#[test]
1476fn test_traits() {
1477 assert_impl_all!(
1478 TimeDirection,
1479 TimeUnit,
1480 AmPm,
1481 DayOfMonth,
1482 Minute,
1483 Month,
1484 Hour,
1485 AbsoluteTime,
1486 Duration,
1487 RelativeTime,
1488 PointInTime,
1489 Time,
1490 DateTime,
1491 RelativeTimeUnit,
1492 NamedRelativeTime,
1493 TimeRange,
1494 TimeExpression : Copy
1495 + Clone
1496 + PartialEq
1497 + Eq
1498 + PartialOrd
1499 + Ord
1500 + core::fmt::Debug
1501 + core::fmt::Display
1502 + Parse
1503 + core::hash::Hash
1504 + FromStr
1505 );
1506}