1use core::fmt;
2
3#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
4use rkyv::{Archive, Deserialize, Serialize};
5
6use crate::OutOfRange;
7
8#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
33#[cfg_attr(
34    any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
35    derive(Archive, Deserialize, Serialize),
36    archive(compare(PartialEq)),
37    archive_attr(derive(Clone, Copy, PartialEq, Eq, Debug, Hash))
38)]
39#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
40#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
41pub enum Weekday {
42    Mon = 0,
44    Tue = 1,
46    Wed = 2,
48    Thu = 3,
50    Fri = 4,
52    Sat = 5,
54    Sun = 6,
56}
57
58impl Weekday {
59    #[inline]
65    #[must_use]
66    pub const fn succ(&self) -> Weekday {
67        match *self {
68            Weekday::Mon => Weekday::Tue,
69            Weekday::Tue => Weekday::Wed,
70            Weekday::Wed => Weekday::Thu,
71            Weekday::Thu => Weekday::Fri,
72            Weekday::Fri => Weekday::Sat,
73            Weekday::Sat => Weekday::Sun,
74            Weekday::Sun => Weekday::Mon,
75        }
76    }
77
78    #[inline]
84    #[must_use]
85    pub const fn pred(&self) -> Weekday {
86        match *self {
87            Weekday::Mon => Weekday::Sun,
88            Weekday::Tue => Weekday::Mon,
89            Weekday::Wed => Weekday::Tue,
90            Weekday::Thu => Weekday::Wed,
91            Weekday::Fri => Weekday::Thu,
92            Weekday::Sat => Weekday::Fri,
93            Weekday::Sun => Weekday::Sat,
94        }
95    }
96
97    #[inline]
103    pub const fn number_from_monday(&self) -> u32 {
104        self.days_since(Weekday::Mon) + 1
105    }
106
107    #[inline]
113    pub const fn number_from_sunday(&self) -> u32 {
114        self.days_since(Weekday::Sun) + 1
115    }
116
117    #[inline]
137    pub const fn num_days_from_monday(&self) -> u32 {
138        self.days_since(Weekday::Mon)
139    }
140
141    #[inline]
147    pub const fn num_days_from_sunday(&self) -> u32 {
148        self.days_since(Weekday::Sun)
149    }
150
151    pub const fn days_since(&self, other: Weekday) -> u32 {
162        let lhs = *self as u32;
163        let rhs = other as u32;
164        if lhs < rhs {
165            7 + lhs - rhs
166        } else {
167            lhs - rhs
168        }
169    }
170}
171
172impl fmt::Display for Weekday {
173    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174        f.write_str(match *self {
175            Weekday::Mon => "Mon",
176            Weekday::Tue => "Tue",
177            Weekday::Wed => "Wed",
178            Weekday::Thu => "Thu",
179            Weekday::Fri => "Fri",
180            Weekday::Sat => "Sat",
181            Weekday::Sun => "Sun",
182        })
183    }
184}
185
186impl TryFrom<u8> for Weekday {
190    type Error = OutOfRange;
191
192    fn try_from(value: u8) -> Result<Self, Self::Error> {
193        match value {
194            0 => Ok(Weekday::Mon),
195            1 => Ok(Weekday::Tue),
196            2 => Ok(Weekday::Wed),
197            3 => Ok(Weekday::Thu),
198            4 => Ok(Weekday::Fri),
199            5 => Ok(Weekday::Sat),
200            6 => Ok(Weekday::Sun),
201            _ => Err(OutOfRange::new()),
202        }
203    }
204}
205
206impl num_traits::FromPrimitive for Weekday {
210    #[inline]
211    fn from_i64(n: i64) -> Option<Weekday> {
212        match n {
213            0 => Some(Weekday::Mon),
214            1 => Some(Weekday::Tue),
215            2 => Some(Weekday::Wed),
216            3 => Some(Weekday::Thu),
217            4 => Some(Weekday::Fri),
218            5 => Some(Weekday::Sat),
219            6 => Some(Weekday::Sun),
220            _ => None,
221        }
222    }
223
224    #[inline]
225    fn from_u64(n: u64) -> Option<Weekday> {
226        match n {
227            0 => Some(Weekday::Mon),
228            1 => Some(Weekday::Tue),
229            2 => Some(Weekday::Wed),
230            3 => Some(Weekday::Thu),
231            4 => Some(Weekday::Fri),
232            5 => Some(Weekday::Sat),
233            6 => Some(Weekday::Sun),
234            _ => None,
235        }
236    }
237}
238
239#[derive(Clone, PartialEq, Eq)]
241pub struct ParseWeekdayError {
242    pub(crate) _dummy: (),
243}
244
245#[cfg(feature = "std")]
246impl std::error::Error for ParseWeekdayError {}
247
248impl fmt::Display for ParseWeekdayError {
249    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250        f.write_fmt(format_args!("{:?}", self))
251    }
252}
253
254impl fmt::Debug for ParseWeekdayError {
255    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
256        write!(f, "ParseWeekdayError {{ .. }}")
257    }
258}
259
260#[cfg(feature = "serde")]
263mod weekday_serde {
264    use super::Weekday;
265    use core::fmt;
266    use serde::{de, ser};
267
268    impl ser::Serialize for Weekday {
269        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
270        where
271            S: ser::Serializer,
272        {
273            serializer.collect_str(&self)
274        }
275    }
276
277    struct WeekdayVisitor;
278
279    impl<'de> de::Visitor<'de> for WeekdayVisitor {
280        type Value = Weekday;
281
282        fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
283            f.write_str("Weekday")
284        }
285
286        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
287        where
288            E: de::Error,
289        {
290            value.parse().map_err(|_| E::custom("short or long weekday names expected"))
291        }
292    }
293
294    impl<'de> de::Deserialize<'de> for Weekday {
295        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
296        where
297            D: de::Deserializer<'de>,
298        {
299            deserializer.deserialize_str(WeekdayVisitor)
300        }
301    }
302}
303
304#[cfg(test)]
305mod tests {
306    use super::Weekday;
307
308    #[test]
309    fn test_days_since() {
310        for i in 0..7 {
311            let base_day = Weekday::try_from(i).unwrap();
312
313            assert_eq!(base_day.num_days_from_monday(), base_day.days_since(Weekday::Mon));
314            assert_eq!(base_day.num_days_from_sunday(), base_day.days_since(Weekday::Sun));
315
316            assert_eq!(base_day.days_since(base_day), 0);
317
318            assert_eq!(base_day.days_since(base_day.pred()), 1);
319            assert_eq!(base_day.days_since(base_day.pred().pred()), 2);
320            assert_eq!(base_day.days_since(base_day.pred().pred().pred()), 3);
321            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred()), 4);
322            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred()), 5);
323            assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred().pred()), 6);
324
325            assert_eq!(base_day.days_since(base_day.succ()), 6);
326            assert_eq!(base_day.days_since(base_day.succ().succ()), 5);
327            assert_eq!(base_day.days_since(base_day.succ().succ().succ()), 4);
328            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ()), 3);
329            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ()), 2);
330            assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ().succ()), 1);
331        }
332    }
333
334    #[test]
335    #[cfg(feature = "serde")]
336    fn test_serde_serialize() {
337        use serde_json::to_string;
338        use Weekday::*;
339
340        let cases: Vec<(Weekday, &str)> = vec![
341            (Mon, "\"Mon\""),
342            (Tue, "\"Tue\""),
343            (Wed, "\"Wed\""),
344            (Thu, "\"Thu\""),
345            (Fri, "\"Fri\""),
346            (Sat, "\"Sat\""),
347            (Sun, "\"Sun\""),
348        ];
349
350        for (weekday, expected_str) in cases {
351            let string = to_string(&weekday).unwrap();
352            assert_eq!(string, expected_str);
353        }
354    }
355
356    #[test]
357    #[cfg(feature = "serde")]
358    fn test_serde_deserialize() {
359        use serde_json::from_str;
360        use Weekday::*;
361
362        let cases: Vec<(&str, Weekday)> = vec![
363            ("\"mon\"", Mon),
364            ("\"MONDAY\"", Mon),
365            ("\"MonDay\"", Mon),
366            ("\"mOn\"", Mon),
367            ("\"tue\"", Tue),
368            ("\"tuesday\"", Tue),
369            ("\"wed\"", Wed),
370            ("\"wednesday\"", Wed),
371            ("\"thu\"", Thu),
372            ("\"thursday\"", Thu),
373            ("\"fri\"", Fri),
374            ("\"friday\"", Fri),
375            ("\"sat\"", Sat),
376            ("\"saturday\"", Sat),
377            ("\"sun\"", Sun),
378            ("\"sunday\"", Sun),
379        ];
380
381        for (str, expected_weekday) in cases {
382            let weekday = from_str::<Weekday>(str).unwrap();
383            assert_eq!(weekday, expected_weekday);
384        }
385
386        let errors: Vec<&str> =
387            vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
388
389        for str in errors {
390            from_str::<Weekday>(str).unwrap_err();
391        }
392    }
393
394    #[test]
395    #[cfg(feature = "rkyv-validation")]
396    fn test_rkyv_validation() {
397        let mon = Weekday::Mon;
398        let bytes = rkyv::to_bytes::<_, 1>(&mon).unwrap();
399
400        assert_eq!(rkyv::from_bytes::<Weekday>(&bytes).unwrap(), mon);
401    }
402}