1use core::fmt;
2
3#[cfg(feature = "rkyv")]
4use rkyv::{Archive, Deserialize, Serialize};
5
6use crate::OutOfRange;
7
8#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
33#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
34#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
35#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
36pub enum Weekday {
37 Mon = 0,
39 Tue = 1,
41 Wed = 2,
43 Thu = 3,
45 Fri = 4,
47 Sat = 5,
49 Sun = 6,
51}
52
53impl Weekday {
54 #[inline]
60 #[must_use]
61 pub const fn succ(&self) -> Weekday {
62 match *self {
63 Weekday::Mon => Weekday::Tue,
64 Weekday::Tue => Weekday::Wed,
65 Weekday::Wed => Weekday::Thu,
66 Weekday::Thu => Weekday::Fri,
67 Weekday::Fri => Weekday::Sat,
68 Weekday::Sat => Weekday::Sun,
69 Weekday::Sun => Weekday::Mon,
70 }
71 }
72
73 #[inline]
79 #[must_use]
80 pub const fn pred(&self) -> Weekday {
81 match *self {
82 Weekday::Mon => Weekday::Sun,
83 Weekday::Tue => Weekday::Mon,
84 Weekday::Wed => Weekday::Tue,
85 Weekday::Thu => Weekday::Wed,
86 Weekday::Fri => Weekday::Thu,
87 Weekday::Sat => Weekday::Fri,
88 Weekday::Sun => Weekday::Sat,
89 }
90 }
91
92 #[inline]
98 pub const fn number_from_monday(&self) -> u32 {
99 self.num_days_from(Weekday::Mon) + 1
100 }
101
102 #[inline]
108 pub const fn number_from_sunday(&self) -> u32 {
109 self.num_days_from(Weekday::Sun) + 1
110 }
111
112 #[cfg_attr(not(feature = "clock"), doc = "```ignore")]
121 #[cfg_attr(feature = "clock", doc = "```rust")]
122 #[inline]
131 pub const fn num_days_from_monday(&self) -> u32 {
132 self.num_days_from(Weekday::Mon)
133 }
134
135 #[inline]
141 pub const fn num_days_from_sunday(&self) -> u32 {
142 self.num_days_from(Weekday::Sun)
143 }
144
145 #[inline]
151 pub(crate) const fn num_days_from(&self, day: Weekday) -> u32 {
152 (*self as u32 + 7 - day as u32) % 7
153 }
154}
155
156impl fmt::Display for Weekday {
157 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158 f.write_str(match *self {
159 Weekday::Mon => "Mon",
160 Weekday::Tue => "Tue",
161 Weekday::Wed => "Wed",
162 Weekday::Thu => "Thu",
163 Weekday::Fri => "Fri",
164 Weekday::Sat => "Sat",
165 Weekday::Sun => "Sun",
166 })
167 }
168}
169
170impl TryFrom<u8> for Weekday {
174 type Error = OutOfRange;
175
176 fn try_from(value: u8) -> Result<Self, Self::Error> {
177 match value {
178 0 => Ok(Weekday::Mon),
179 1 => Ok(Weekday::Tue),
180 2 => Ok(Weekday::Wed),
181 3 => Ok(Weekday::Thu),
182 4 => Ok(Weekday::Fri),
183 5 => Ok(Weekday::Sat),
184 6 => Ok(Weekday::Sun),
185 _ => Err(OutOfRange::new()),
186 }
187 }
188}
189
190impl num_traits::FromPrimitive for Weekday {
194 #[inline]
195 fn from_i64(n: i64) -> Option<Weekday> {
196 match n {
197 0 => Some(Weekday::Mon),
198 1 => Some(Weekday::Tue),
199 2 => Some(Weekday::Wed),
200 3 => Some(Weekday::Thu),
201 4 => Some(Weekday::Fri),
202 5 => Some(Weekday::Sat),
203 6 => Some(Weekday::Sun),
204 _ => None,
205 }
206 }
207
208 #[inline]
209 fn from_u64(n: u64) -> Option<Weekday> {
210 match n {
211 0 => Some(Weekday::Mon),
212 1 => Some(Weekday::Tue),
213 2 => Some(Weekday::Wed),
214 3 => Some(Weekday::Thu),
215 4 => Some(Weekday::Fri),
216 5 => Some(Weekday::Sat),
217 6 => Some(Weekday::Sun),
218 _ => None,
219 }
220 }
221}
222
223#[derive(Clone, PartialEq, Eq)]
225pub struct ParseWeekdayError {
226 pub(crate) _dummy: (),
227}
228
229#[cfg(feature = "std")]
230#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
231impl std::error::Error for ParseWeekdayError {}
232
233impl fmt::Display for ParseWeekdayError {
234 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
235 f.write_fmt(format_args!("{:?}", self))
236 }
237}
238
239impl fmt::Debug for ParseWeekdayError {
240 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241 write!(f, "ParseWeekdayError {{ .. }}")
242 }
243}
244
245#[cfg(feature = "serde")]
248#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
249mod weekday_serde {
250 use super::Weekday;
251 use core::fmt;
252 use serde::{de, ser};
253
254 impl ser::Serialize for Weekday {
255 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
256 where
257 S: ser::Serializer,
258 {
259 serializer.collect_str(&self)
260 }
261 }
262
263 struct WeekdayVisitor;
264
265 impl<'de> de::Visitor<'de> for WeekdayVisitor {
266 type Value = Weekday;
267
268 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
269 f.write_str("Weekday")
270 }
271
272 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
273 where
274 E: de::Error,
275 {
276 value.parse().map_err(|_| E::custom("short or long weekday names expected"))
277 }
278 }
279
280 impl<'de> de::Deserialize<'de> for Weekday {
281 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
282 where
283 D: de::Deserializer<'de>,
284 {
285 deserializer.deserialize_str(WeekdayVisitor)
286 }
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::Weekday;
293
294 #[test]
295 fn test_num_days_from() {
296 for i in 0..7 {
297 let base_day = Weekday::try_from(i).unwrap();
298
299 assert_eq!(base_day.num_days_from_monday(), base_day.num_days_from(Weekday::Mon));
300 assert_eq!(base_day.num_days_from_sunday(), base_day.num_days_from(Weekday::Sun));
301
302 assert_eq!(base_day.num_days_from(base_day), 0);
303
304 assert_eq!(base_day.num_days_from(base_day.pred()), 1);
305 assert_eq!(base_day.num_days_from(base_day.pred().pred()), 2);
306 assert_eq!(base_day.num_days_from(base_day.pred().pred().pred()), 3);
307 assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred()), 4);
308 assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred().pred()), 5);
309 assert_eq!(
310 base_day.num_days_from(base_day.pred().pred().pred().pred().pred().pred()),
311 6
312 );
313
314 assert_eq!(base_day.num_days_from(base_day.succ()), 6);
315 assert_eq!(base_day.num_days_from(base_day.succ().succ()), 5);
316 assert_eq!(base_day.num_days_from(base_day.succ().succ().succ()), 4);
317 assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ()), 3);
318 assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ().succ()), 2);
319 assert_eq!(
320 base_day.num_days_from(base_day.succ().succ().succ().succ().succ().succ()),
321 1
322 );
323 }
324 }
325
326 #[test]
327 #[cfg(feature = "serde")]
328 fn test_serde_serialize() {
329 use serde_json::to_string;
330 use Weekday::*;
331
332 let cases: Vec<(Weekday, &str)> = vec![
333 (Mon, "\"Mon\""),
334 (Tue, "\"Tue\""),
335 (Wed, "\"Wed\""),
336 (Thu, "\"Thu\""),
337 (Fri, "\"Fri\""),
338 (Sat, "\"Sat\""),
339 (Sun, "\"Sun\""),
340 ];
341
342 for (weekday, expected_str) in cases {
343 let string = to_string(&weekday).unwrap();
344 assert_eq!(string, expected_str);
345 }
346 }
347
348 #[test]
349 #[cfg(feature = "serde")]
350 fn test_serde_deserialize() {
351 use serde_json::from_str;
352 use Weekday::*;
353
354 let cases: Vec<(&str, Weekday)> = vec![
355 ("\"mon\"", Mon),
356 ("\"MONDAY\"", Mon),
357 ("\"MonDay\"", Mon),
358 ("\"mOn\"", Mon),
359 ("\"tue\"", Tue),
360 ("\"tuesday\"", Tue),
361 ("\"wed\"", Wed),
362 ("\"wednesday\"", Wed),
363 ("\"thu\"", Thu),
364 ("\"thursday\"", Thu),
365 ("\"fri\"", Fri),
366 ("\"friday\"", Fri),
367 ("\"sat\"", Sat),
368 ("\"saturday\"", Sat),
369 ("\"sun\"", Sun),
370 ("\"sunday\"", Sun),
371 ];
372
373 for (str, expected_weekday) in cases {
374 let weekday = from_str::<Weekday>(str).unwrap();
375 assert_eq!(weekday, expected_weekday);
376 }
377
378 let errors: Vec<&str> =
379 vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
380
381 for str in errors {
382 from_str::<Weekday>(str).unwrap_err();
383 }
384 }
385}