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.pad(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::Visitor<'_> 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 fn test_formatting_alignment() {
336 assert_eq!(format!("{:x>7}", Weekday::Mon), "xxxxMon");
340 assert_eq!(format!("{:^7}", Weekday::Mon), " Mon ");
341 assert_eq!(format!("{:Z<7}", Weekday::Mon), "MonZZZZ");
342 }
343
344 #[test]
345 #[cfg(feature = "serde")]
346 fn test_serde_serialize() {
347 use serde_json::to_string;
348 use Weekday::*;
349
350 let cases: Vec<(Weekday, &str)> = vec![
351 (Mon, "\"Mon\""),
352 (Tue, "\"Tue\""),
353 (Wed, "\"Wed\""),
354 (Thu, "\"Thu\""),
355 (Fri, "\"Fri\""),
356 (Sat, "\"Sat\""),
357 (Sun, "\"Sun\""),
358 ];
359
360 for (weekday, expected_str) in cases {
361 let string = to_string(&weekday).unwrap();
362 assert_eq!(string, expected_str);
363 }
364 }
365
366 #[test]
367 #[cfg(feature = "serde")]
368 fn test_serde_deserialize() {
369 use serde_json::from_str;
370 use Weekday::*;
371
372 let cases: Vec<(&str, Weekday)> = vec![
373 ("\"mon\"", Mon),
374 ("\"MONDAY\"", Mon),
375 ("\"MonDay\"", Mon),
376 ("\"mOn\"", Mon),
377 ("\"tue\"", Tue),
378 ("\"tuesday\"", Tue),
379 ("\"wed\"", Wed),
380 ("\"wednesday\"", Wed),
381 ("\"thu\"", Thu),
382 ("\"thursday\"", Thu),
383 ("\"fri\"", Fri),
384 ("\"friday\"", Fri),
385 ("\"sat\"", Sat),
386 ("\"saturday\"", Sat),
387 ("\"sun\"", Sun),
388 ("\"sunday\"", Sun),
389 ];
390
391 for (str, expected_weekday) in cases {
392 let weekday = from_str::<Weekday>(str).unwrap();
393 assert_eq!(weekday, expected_weekday);
394 }
395
396 let errors: Vec<&str> =
397 vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
398
399 for str in errors {
400 from_str::<Weekday>(str).unwrap_err();
401 }
402 }
403
404 #[test]
405 #[cfg(feature = "rkyv-validation")]
406 fn test_rkyv_validation() {
407 let mon = Weekday::Mon;
408 let bytes = rkyv::to_bytes::<_, 1>(&mon).unwrap();
409
410 assert_eq!(rkyv::from_bytes::<Weekday>(&bytes).unwrap(), mon);
411 }
412}