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(feature = "std")]
242impl std::error::Error for ParseWeekdayError {}
243
244impl fmt::Display for ParseWeekdayError {
245 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246 f.write_fmt(format_args!("{:?}", self))
247 }
248}
249
250impl fmt::Debug for ParseWeekdayError {
251 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
252 write!(f, "ParseWeekdayError {{ .. }}")
253 }
254}
255
256#[cfg(feature = "serde")]
259mod weekday_serde {
260 use super::Weekday;
261 use core::fmt;
262 use serde::{de, ser};
263
264 impl ser::Serialize for Weekday {
265 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
266 where
267 S: ser::Serializer,
268 {
269 serializer.collect_str(&self)
270 }
271 }
272
273 struct WeekdayVisitor;
274
275 impl de::Visitor<'_> for WeekdayVisitor {
276 type Value = Weekday;
277
278 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
279 f.write_str("Weekday")
280 }
281
282 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
283 where
284 E: de::Error,
285 {
286 value.parse().map_err(|_| E::custom("short or long weekday names expected"))
287 }
288 }
289
290 impl<'de> de::Deserialize<'de> for Weekday {
291 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
292 where
293 D: de::Deserializer<'de>,
294 {
295 deserializer.deserialize_str(WeekdayVisitor)
296 }
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use super::Weekday;
303
304 #[test]
305 fn test_days_since() {
306 for i in 0..7 {
307 let base_day = Weekday::try_from(i).unwrap();
308
309 assert_eq!(base_day.num_days_from_monday(), base_day.days_since(Weekday::Mon));
310 assert_eq!(base_day.num_days_from_sunday(), base_day.days_since(Weekday::Sun));
311
312 assert_eq!(base_day.days_since(base_day), 0);
313
314 assert_eq!(base_day.days_since(base_day.pred()), 1);
315 assert_eq!(base_day.days_since(base_day.pred().pred()), 2);
316 assert_eq!(base_day.days_since(base_day.pred().pred().pred()), 3);
317 assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred()), 4);
318 assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred()), 5);
319 assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred().pred()), 6);
320
321 assert_eq!(base_day.days_since(base_day.succ()), 6);
322 assert_eq!(base_day.days_since(base_day.succ().succ()), 5);
323 assert_eq!(base_day.days_since(base_day.succ().succ().succ()), 4);
324 assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ()), 3);
325 assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ()), 2);
326 assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ().succ()), 1);
327 }
328 }
329
330 #[test]
331 fn test_formatting_alignment() {
332 assert_eq!(format!("{:x>7}", Weekday::Mon), "xxxxMon");
336 assert_eq!(format!("{:^7}", Weekday::Mon), " Mon ");
337 assert_eq!(format!("{:Z<7}", Weekday::Mon), "MonZZZZ");
338 }
339
340 #[test]
341 #[cfg(feature = "serde")]
342 fn test_serde_serialize() {
343 use Weekday::*;
344 use serde_json::to_string;
345
346 let cases: Vec<(Weekday, &str)> = vec![
347 (Mon, "\"Mon\""),
348 (Tue, "\"Tue\""),
349 (Wed, "\"Wed\""),
350 (Thu, "\"Thu\""),
351 (Fri, "\"Fri\""),
352 (Sat, "\"Sat\""),
353 (Sun, "\"Sun\""),
354 ];
355
356 for (weekday, expected_str) in cases {
357 let string = to_string(&weekday).unwrap();
358 assert_eq!(string, expected_str);
359 }
360 }
361
362 #[test]
363 #[cfg(feature = "serde")]
364 fn test_serde_deserialize() {
365 use Weekday::*;
366 use serde_json::from_str;
367
368 let cases: Vec<(&str, Weekday)> = vec![
369 ("\"mon\"", Mon),
370 ("\"MONDAY\"", Mon),
371 ("\"MonDay\"", Mon),
372 ("\"mOn\"", Mon),
373 ("\"tue\"", Tue),
374 ("\"tuesday\"", Tue),
375 ("\"wed\"", Wed),
376 ("\"wednesday\"", Wed),
377 ("\"thu\"", Thu),
378 ("\"thursday\"", Thu),
379 ("\"fri\"", Fri),
380 ("\"friday\"", Fri),
381 ("\"sat\"", Sat),
382 ("\"saturday\"", Sat),
383 ("\"sun\"", Sun),
384 ("\"sunday\"", Sun),
385 ];
386
387 for (str, expected_weekday) in cases {
388 let weekday = from_str::<Weekday>(str).unwrap();
389 assert_eq!(weekday, expected_weekday);
390 }
391
392 let errors: Vec<&str> =
393 vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
394
395 for str in errors {
396 from_str::<Weekday>(str).unwrap_err();
397 }
398 }
399
400 #[test]
401 #[cfg(feature = "rkyv-validation")]
402 fn test_rkyv_validation() {
403 let mon = Weekday::Mon;
404 let bytes = rkyv::to_bytes::<_, 1>(&mon).unwrap();
405
406 assert_eq!(rkyv::from_bytes::<Weekday>(&bytes).unwrap(), mon);
407 }
408}