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