1use std::{
2 fmt::{Debug, Display},
3 str::FromStr,
4};
5
6use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
7
8#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
12pub struct Date {
13 pub year: i64,
15
16 pub month: u8,
18
19 pub day: u8,
21}
22
23impl Display for Date {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 let Self { year, month, day } = self;
26 write!(f, "{year:04}-{month:02}-{day:02}")
27 }
28}
29
30impl FromStr for Date {
31 type Err = String;
32
33 fn from_str(s: &str) -> Result<Self, Self::Err> {
34 let err = || -> String { format!("expected YYYY-MM-DD, got: {s:?}") };
35
36 let [year, month, day]: [&str; 3] = s
37 .splitn(3, '-')
38 .collect::<Vec<_>>()
39 .try_into()
40 .map_err(|_| err())?;
41
42 if year.len() != 4 || month.len() != 2 || day.len() != 2 {
43 Err(err())
44 } else {
45 let date = Self {
46 year: year.parse().map_err(|_| err())?,
47 month: month.parse().map_err(|_| err())?,
48 day: day.parse().map_err(|_| err())?,
49 };
50
51 if date.month == 0 || date.month > 12 {
52 Err(format!(
53 "month should be in range 1-12, got: {}",
54 date.month
55 ))
56 } else if date.day == 0 || date.day > 31 {
57 Err(format!("day should be in range 1-31, got: {}", date.day))
58 } else {
59 Ok(date)
60 }
61 }
62 }
63}
64
65impl Serialize for Date {
66 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
67 where
68 S: Serializer,
69 {
70 let s = self.to_string();
71 s.serialize(serializer)
72 }
73}
74
75impl<'de> Deserialize<'de> for Date {
76 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
77 where
78 D: Deserializer<'de>,
79 {
80 let s = String::deserialize(deserializer)?;
81 Date::from_str(&s).map_err(D::Error::custom)
82 }
83}