use serde::de::Error; use serde::Serialize; use serde::{Deserialize, Deserializer, Serializer}; use std::str::FromStr; #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct Date { pub year: u16, pub month: u8, pub day: u8, } impl Date { #[cfg(test)] pub fn new(year: u16, month: u8, day: u8) -> Self { Date { year, month, day } } } #[derive(Clone, PartialEq, Eq, Debug)] pub enum DateParseError { InvalidNumber, MonthOutOfRange, DayOutOfRange, WrongFormat, } impl FromStr for Date { type Err = DateParseError; fn from_str(input: &str) -> Result { let mut parts = input.splitn(3, "-"); let year = parts .next() .ok_or(DateParseError::WrongFormat)? .parse::() .map_err(|_| DateParseError::InvalidNumber)?; let month = parts .next() .ok_or(DateParseError::WrongFormat)? .parse::() .map_err(|_| DateParseError::InvalidNumber)?; let day = parts .next() .ok_or(DateParseError::WrongFormat)? .parse::() .map_err(|_| DateParseError::InvalidNumber)?; if month > 12 { return Err(DateParseError::DayOutOfRange); } if day > 31 { return Err(DateParseError::MonthOutOfRange); } Ok(Date { year, month, day }) } } impl Serialize for Date { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&format!( "{:04}-{:02}-{:02}", self.year, self.month, self.day )) } } impl<'de> Deserialize<'de> for Date { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let data = String::deserialize(deserializer)?; match Date::from_str(&data) { Ok(date) => Ok(date), Err(_) => Err(D::Error::custom( "Expected a String in this format: YYYY-MM-DD.", )), } } } #[cfg(test)] mod test { use super::*; #[test] fn order() { // Other Test not needed, since the Ord is derived. assert!(Date::new(2019, 10, 10) > Date::new(2018, 10, 10)); assert!(Date::new(2018, 11, 10) > Date::new(2018, 10, 10)); assert!(Date::new(2018, 10, 11) > Date::new(2018, 10, 10)); } #[test] fn from_str() { assert_eq!(Ok(Date::new(2019, 10, 10)), Date::from_str("2019-10-10")); assert_eq!(Ok(Date::new(2019, 1, 1)), Date::from_str("2019-1-1")); assert!(Date::from_str("2019-1-1-444").is_err()); assert_eq!( Ok(Date::new(2019, 1, 1)), Date::from_str("2019-0000000000001-00000001") ); assert!(Date::from_str("XXX-400-400").is_err()); assert!(Date::from_str("400-400-400").is_err()); } }