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