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))]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
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.days_since(Weekday::Mon) + 1
106 }
107
108 #[inline]
114 pub const fn number_from_sunday(&self) -> u32 {
115 self.days_since(Weekday::Sun) + 1
116 }
117
118 #[inline]
138 pub const fn num_days_from_monday(&self) -> u32 {
139 self.days_since(Weekday::Mon)
140 }
141
142 #[inline]
148 pub const fn num_days_from_sunday(&self) -> u32 {
149 self.days_since(Weekday::Sun)
150 }
151
152 pub const fn days_since(&self, other: Weekday) -> u32 {
163 let lhs = *self as u32;
164 let rhs = other as u32;
165 if lhs < rhs { 7 + lhs - rhs } else { lhs - rhs }
166 }
167}
168
169impl fmt::Display for Weekday {
170 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171 f.pad(match *self {
172 Weekday::Mon => "Mon",
173 Weekday::Tue => "Tue",
174 Weekday::Wed => "Wed",
175 Weekday::Thu => "Thu",
176 Weekday::Fri => "Fri",
177 Weekday::Sat => "Sat",
178 Weekday::Sun => "Sun",
179 })
180 }
181}
182
183impl TryFrom<u8> for Weekday {
187 type Error = OutOfRange;
188
189 fn try_from(value: u8) -> Result<Self, Self::Error> {
190 match value {
191 0 => Ok(Weekday::Mon),
192 1 => Ok(Weekday::Tue),
193 2 => Ok(Weekday::Wed),
194 3 => Ok(Weekday::Thu),
195 4 => Ok(Weekday::Fri),
196 5 => Ok(Weekday::Sat),
197 6 => Ok(Weekday::Sun),
198 _ => Err(OutOfRange::new()),
199 }
200 }
201}
202
203impl num_traits::FromPrimitive for Weekday {
207 #[inline]
208 fn from_i64(n: i64) -> Option<Weekday> {
209 match n {
210 0 => Some(Weekday::Mon),
211 1 => Some(Weekday::Tue),
212 2 => Some(Weekday::Wed),
213 3 => Some(Weekday::Thu),
214 4 => Some(Weekday::Fri),
215 5 => Some(Weekday::Sat),
216 6 => Some(Weekday::Sun),
217 _ => None,
218 }
219 }
220
221 #[inline]
222 fn from_u64(n: u64) -> Option<Weekday> {
223 match n {
224 0 => Some(Weekday::Mon),
225 1 => Some(Weekday::Tue),
226 2 => Some(Weekday::Wed),
227 3 => Some(Weekday::Thu),
228 4 => Some(Weekday::Fri),
229 5 => Some(Weekday::Sat),
230 6 => Some(Weekday::Sun),
231 _ => None,
232 }
233 }
234}
235
236#[derive(Clone, PartialEq, Eq)]
238pub struct ParseWeekdayError {
239 pub(crate) _dummy: (),
240}
241
242#[cfg(all(not(feature = "std"), feature = "core-error"))]
243impl core::error::Error for ParseWeekdayError {}
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 = "defmt")]
261impl defmt::Format for ParseWeekdayError {
262 fn format(&self, fmt: defmt::Formatter) {
263 defmt::write!(fmt, "ParseWeekdayError {{ .. }}")
264 }
265}
266
267#[cfg(feature = "serde")]
270mod weekday_serde {
271 use super::Weekday;
272 use core::fmt;
273 use serde::{de, ser};
274
275 impl ser::Serialize for Weekday {
276 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
277 where
278 S: ser::Serializer,
279 {
280 serializer.collect_str(&self)
281 }
282 }
283
284 struct WeekdayVisitor;
285
286 impl de::Visitor<'_> for WeekdayVisitor {
287 type Value = Weekday;
288
289 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
290 f.write_str("Weekday")
291 }
292
293 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
294 where
295 E: de::Error,
296 {
297 value.parse().map_err(|_| E::custom("short or long weekday names expected"))
298 }
299 }
300
301 impl<'de> de::Deserialize<'de> for Weekday {
302 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
303 where
304 D: de::Deserializer<'de>,
305 {
306 deserializer.deserialize_str(WeekdayVisitor)
307 }
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use super::Weekday;
314
315 #[test]
316 fn test_days_since() {
317 for i in 0..7 {
318 let base_day = Weekday::try_from(i).unwrap();
319
320 assert_eq!(base_day.num_days_from_monday(), base_day.days_since(Weekday::Mon));
321 assert_eq!(base_day.num_days_from_sunday(), base_day.days_since(Weekday::Sun));
322
323 assert_eq!(base_day.days_since(base_day), 0);
324
325 assert_eq!(base_day.days_since(base_day.pred()), 1);
326 assert_eq!(base_day.days_since(base_day.pred().pred()), 2);
327 assert_eq!(base_day.days_since(base_day.pred().pred().pred()), 3);
328 assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred()), 4);
329 assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred()), 5);
330 assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred().pred()), 6);
331
332 assert_eq!(base_day.days_since(base_day.succ()), 6);
333 assert_eq!(base_day.days_since(base_day.succ().succ()), 5);
334 assert_eq!(base_day.days_since(base_day.succ().succ().succ()), 4);
335 assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ()), 3);
336 assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ()), 2);
337 assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ().succ()), 1);
338 }
339 }
340
341 #[test]
342 fn test_formatting_alignment() {
343 assert_eq!(format!("{:x>7}", Weekday::Mon), "xxxxMon");
347 assert_eq!(format!("{:^7}", Weekday::Mon), " Mon ");
348 assert_eq!(format!("{:Z<7}", Weekday::Mon), "MonZZZZ");
349 }
350
351 #[test]
352 #[cfg(feature = "serde")]
353 fn test_serde_serialize() {
354 use Weekday::*;
355 use serde_json::to_string;
356
357 let cases: Vec<(Weekday, &str)> = vec![
358 (Mon, "\"Mon\""),
359 (Tue, "\"Tue\""),
360 (Wed, "\"Wed\""),
361 (Thu, "\"Thu\""),
362 (Fri, "\"Fri\""),
363 (Sat, "\"Sat\""),
364 (Sun, "\"Sun\""),
365 ];
366
367 for (weekday, expected_str) in cases {
368 let string = to_string(&weekday).unwrap();
369 assert_eq!(string, expected_str);
370 }
371 }
372
373 #[test]
374 #[cfg(feature = "serde")]
375 fn test_serde_deserialize() {
376 use Weekday::*;
377 use serde_json::from_str;
378
379 let cases: Vec<(&str, Weekday)> = vec![
380 ("\"mon\"", Mon),
381 ("\"MONDAY\"", Mon),
382 ("\"MonDay\"", Mon),
383 ("\"mOn\"", Mon),
384 ("\"tue\"", Tue),
385 ("\"tuesday\"", Tue),
386 ("\"wed\"", Wed),
387 ("\"wednesday\"", Wed),
388 ("\"thu\"", Thu),
389 ("\"thursday\"", Thu),
390 ("\"fri\"", Fri),
391 ("\"friday\"", Fri),
392 ("\"sat\"", Sat),
393 ("\"saturday\"", Sat),
394 ("\"sun\"", Sun),
395 ("\"sunday\"", Sun),
396 ];
397
398 for (str, expected_weekday) in cases {
399 let weekday = from_str::<Weekday>(str).unwrap();
400 assert_eq!(weekday, expected_weekday);
401 }
402
403 let errors: Vec<&str> =
404 vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""];
405
406 for str in errors {
407 from_str::<Weekday>(str).unwrap_err();
408 }
409 }
410
411 #[test]
412 #[cfg(feature = "rkyv-validation")]
413 fn test_rkyv_validation() {
414 let mon = Weekday::Mon;
415 let bytes = rkyv::to_bytes::<_, 1>(&mon).unwrap();
416
417 assert_eq!(rkyv::from_bytes::<Weekday>(&bytes).unwrap(), mon);
418 }
419}