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, { let data = (self.year, self.month, self.day); data.serialize(serializer) } } impl<'de> Deserialize<'de> for Date { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let data = <(u16, u8, u8)>::deserialize(deserializer)?; Ok(Date { year: data.0, month: data.1, day: data.2, }) } } #[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()); } }