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