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 { 7 + lhs - rhs } else { lhs - rhs }
165 }
166}
167
168impl fmt::Display for Weekday {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 f.pad(match *self {
171 Weekday::Mon => "Mon",
172 Weekday::Tue => "Tue",
173 Weekday::Wed => "Wed",
174 Weekday::Thu => "Thu",
175 Weekday::Fri => "Fri",
176 Weekday::Sat => "Sat",
177 Weekday::Sun => "Sun",
178 })
179 }
180}
181
182impl TryFrom<u8> for Weekday {
186 type Error = OutOfRange;
187
188 fn try_from(value: u8) -> Result<Self, Self::Error> {
189 match value {
190 0 => Ok(Weekday::Mon),
191 1 => Ok(Weekday::Tue),
192 2 => Ok(Weekday::Wed),
193 3 => Ok(Weekday::Thu),
194 4 => Ok(Weekday::Fri),
195 5 => Ok(Weekday::Sat),
196 6 => Ok(Weekday::Sun),
197 _ => Err(OutOfRange::new()),
198 }
199 }
200}
201
202impl num_traits::FromPrimitive for Weekday {
206 #[inline]
207 fn from_i64(n: i64) -> Option<Weekday> {
208 match n {
209 0 => Some(Weekday::Mon),
210 1 => Some(Weekday::Tue),
211 2 => Some(Weekday::Wed),
212 3 => Some(Weekday::Thu),
213 4 => Some(Weekday::Fri),
214 5 => Some(Weekday::Sat),
215 6 => Some(Weekday::Sun),
216 _ => None,
217 }
218 }
219
220 #[inline]
221 fn from_u64(n: u64) -> Option<Weekday> {
222 match n {
223 0 => Some(Weekday::Mon),
224 1 => Some(Weekday::Tue),
225 2 => Some(Weekday::Wed),
226 3 => Some(Weekday::Thu),
227 4 => Some(Weekday::Fri),
228 5 => Some(Weekday::Sat),
229 6 => Some(Weekday::Sun),
230 _ => None,
231 }
232 }
233}
234
235#[derive(Clone, PartialEq, Eq)]
237pub struct ParseWeekdayError {
238 pub(crate) _dummy: (),
239}
240
241#[cfg(all(not(feature = "std"), feature = "core-error"))]
242impl core::error::Error for ParseWeekdayError {}
243
244#[cfg(feature = "std")]
245impl std::error::Error for ParseWeekdayError {}
246
247impl fmt::Display for ParseWeekdayError {
248 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
249 f.write_fmt(format_args!("{self:?}"))
250 }
251}
252
253impl fmt::Debug for ParseWeekdayError {
254 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
255 write!(f, "ParseWeekdayError {{ .. }}")
256 }
257}
258
259#[cfg(feature = "serde")]
262mod weekday_serde {
263 use super::Weekday;
264 use core::fmt;
265 use serde::{de, ser};
266
267 impl ser::Serialize for Weekday {
268 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
269 where
270 S: ser::Serializer,
271 {
272 serializer.collect_str(&self)
273 }
274 }
275
276 struct WeekdayVisitor;
277
278 impl de::Visitor<'_> for WeekdayVisitor {
279 type Value = Weekday;
280
281 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
282 f.write_str("Weekday")
283 }
284
285 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
286 where
287 E: de::Error,
288 {
289 value.parse().map_err(|_| E::custom("short or long weekday names expected"))
290 }
291 }
292
293 impl<'de> de::Deserialize<'de> for Weekday {
294 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
295 where
296 D: de::Deserializer<'de>,
297 {
298 deserializer.deserialize_str(WeekdayVisitor)
299 }
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::Weekday;
306
307 #[test]
308 fn test_days_since() {
309 for i in 0..7 {
310 let base_day = Weekday::try_from(i).unwrap();
311
312 assert_eq!(base_day.num_days_from_monday(), base_day.days_since(Weekday::Mon));
313 assert_eq!(base_day.num_days_from_sunday(), base_day.days_since(Weekday::Sun));
314
315 assert_eq!(base_day.days_since(base_day), 0);
316
317 assert_eq!(base_day.days_since(base_day.pred()), 1);
318 assert_eq!(base_day.days_since(base_day.pred().pred()), 2);
319 assert_eq!(base_day.days_since(base_day.pred().pred().pred()), 3);
320 assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred()), 4);
321 assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred()), 5);
322 assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred().pred()), 6);
323
324 assert_eq!(base_day.days_since(base_day.succ()), 6);
325 assert_eq!(base_day.days_since(base_day.succ().succ()), 5);
326 assert_eq!(base_day.days_since(base_day.succ().succ().succ()), 4);
327 assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ()), 3);
328 assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ()), 2);
329 assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ().succ()), 1);
330 }
331 }
332
333 #[test]
334 fn test_formatting_alignment() {
335 assert_eq!(format!("{:x>7}", Weekday::Mon), "xxxxMon");
339 assert_eq!(format!("{:^7}", Weekday::Mon), " Mon ");
340 assert_eq!(format!("{:Z<7}", Weekday::Mon), "MonZZZZ");
341 }
342
343 #[test]
344 #[cfg(feature = "serde")]
345 fn test_serde_serialize() {
346 use Weekday::*;
347 use serde_json::to_string;
348
349 let cases: Vec<(Weekday, &str)> = vec![
350 (Mon, "\"Mon\""),
351 (Tue, "\"Tue\""),
352 (Wed, "\"Wed\""),
353 (Thu, "\"Thu\""),
354 (Fri, "\"Fri\""),
355 (Sat, "\"Sat\""),
356 (Sun, "\"Sun\""),
357 ];
358
359 for (weekday, expected_str) in cases {
360 let string = to_string(&weekday).unwrap();
361 assert_eq!(string, expected_str);
362 }
363 }
364
365 #[test]
366 #[cfg(feature = "serde")]
367 fn test_serde_deserialize() {
368 use Weekday::*;
369 use serde_json::from_str;
370
371 let cases: Vec<(&str, Weekday)> = vec![
372 ("\"mon\"", Mon),
373 ("\"MONDAY\"", Mon),
374 ("\"MonDay\"", Mon),
375 ("\"mOn\"", Mon),
376 ("\"tue\"", Tue),
377 ("\"tuesday\"", Tue),
378 ("\"wed\"", Wed),
379 ("\"wednesday\"", Wed),
380 ("\"thu\"", Thu),
381 ("\"thursday\"", Thu),
382 ("\"fri\"", Fri),
383 ("\"friday\"", Fri),
384 ("\"sat\"", Sat),
385 ("\"saturday\"", Sat),
386 ("\"sun\"", Sun),
387 ("\"sunday\"", Sun),
388 ];
389
390 for (str, expected_weekday) in cases {
391 let weekday = from_str::<Weekday>(str).unwrap();
392 assert_eq!(weekday, expected_weekday);
393 }
394
395 let errors: Vec<&str> =
396 vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
397
398 for str in errors {
399 from_str::<Weekday>(str).unwrap_err();
400 }
401 }
402
403 #[test]
404 #[cfg(feature = "rkyv-validation")]
405 fn test_rkyv_validation() {
406 let mon = Weekday::Mon;
407 let bytes = rkyv::to_bytes::<_, 1>(&mon).unwrap();
408
409 assert_eq!(rkyv::from_bytes::<Weekday>(&bytes).unwrap(), mon);
410 }
411}