use std::collections::HashSet; use anyhow::{anyhow, bail}; pub struct Date { pub day: u8, pub month: u8, pub year: u16, } impl Date { /// Create a day from String. Expects strings in format '06-26-2026' pub fn from_string(val: &str) -> anyhow::Result { let mut parts = val.split("-"); let month = parts .next() .ok_or(anyhow!("Month missing from date string"))? .parse()?; if month > 12 || month == 0 { bail!("Month should be > 0 and <= 12") } let day = parts .next() .ok_or(anyhow!("Day missing from date string"))? .parse()?; if day == 0 { bail!("Day must be > 0") } match month { 2 => { if day > 29 { bail!("February can have a max of 29 days (on leap years)") } } 4 | 6 | 9 | 11 => { if day > 30 { bail!("The provided month only has 30 days") } } 1 | 3 | 5 | 7 | 8 | 10 | 12 => { if day > 31 { bail!("The provided month only has 31 days") } } _ => unreachable!(), } let year = parts .next() .ok_or(anyhow!("Year missing from date string"))? .parse()?; Ok(Date { day, month, year }) } } pub struct Habit { pub name: String, pub days_completed: HashSet, } #[cfg(test)] mod test { use crate::model::Date; #[test] fn correct_date_gets_parsed() { let date = Date::from_string("06-16-2026").unwrap(); assert_eq!(date.day, 16); assert_eq!(date.month, 6); assert_eq!(date.year, 2026); let date = Date::from_string("6-16-2026").unwrap(); assert_eq!(date.day, 16); assert_eq!(date.month, 6); assert_eq!(date.year, 2026); let date = Date::from_string("6-6-2026").unwrap(); assert_eq!(date.day, 6); assert_eq!(date.month, 6); assert_eq!(date.year, 2026); } #[test] fn bad_date_doesnt_get_parsed() { let date = Date::from_string("-12-2026"); assert!(date.is_err()); let date = Date::from_string("13-01-2026"); assert!(date.is_err()); let date = Date::from_string("01-32-2026"); assert!(date.is_err()); } }