diff --git a/src/birthday.rs b/src/birthday.rs index a3d5d6c..d35c224 100644 --- a/src/birthday.rs +++ b/src/birthday.rs @@ -20,6 +20,10 @@ impl Birthday { birthday_next_year } } + + pub fn is_today(&self, today: NaiveDate) -> bool { + self.date.month() == today.month() && self.date.day() == today.day() + } } #[cfg(test)] @@ -68,4 +72,19 @@ mod tests { NaiveDate::from_ymd_opt(2025, 5, 3).unwrap() ) } + + #[test] + fn test_is_today() { + let birthday = Birthday { + id: 1, + name: "Ben Dover".to_string(), + date: NaiveDate::from_ymd_opt(1990, 5, 3).unwrap(), + }; + + assert!(birthday.is_today(NaiveDate::from_ymd_opt(2024, 5, 3).unwrap())); + + assert!(!birthday.is_today(NaiveDate::from_ymd_opt(2024, 5, 4).unwrap())); + + assert!(!birthday.is_today(NaiveDate::from_ymd_opt(2024, 6, 3).unwrap())); + } } diff --git a/src/main.rs b/src/main.rs index 809983c..08030cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ +mod output; use anyhow::Result; -use birthday::Birthday; use chrono::{Datelike, Utc}; use clap::{Parser, Subcommand}; @@ -38,19 +38,18 @@ enum Command { fn main() -> Result<()> { let cli = Cli::parse(); - println!("You ran cli with: {:?}", cli); + let today = Utc::now().date_naive(); match cli.command { Command::Add { name, date } => birthday::add(name, date), Command::All {} => { let birthdays = birthday::get_all()?; - print_birthdays(birthdays); + output::output(birthdays, today); Ok(()) } Command::Next {} => { - let today = Utc::now().date_naive(); let maybe_birthday = birthday::get_next(today)?; if let Some(birthday) = maybe_birthday { - print_birthdays(vec![birthday]); + output::output(vec![birthday], today); } Ok(()) } @@ -61,7 +60,7 @@ fn main() -> Result<()> { day, } => { let birthdays = birthday::search(name, year, month, day)?; - print_birthdays(birthdays); + output::output(birthdays, today); Ok(()) } Command::Today {} => { @@ -72,22 +71,8 @@ fn main() -> Result<()> { Some(today.month()), Some(today.day()), )?; - print_birthdays(birthdays); + output::output(birthdays, today); Ok(()) } } } - -fn print_birthdays(birthdays: Vec) { - let today = Utc::now().date_naive(); - for birthday in birthdays { - let age = match birthday.age(today) { - Some(age) => age.to_string(), - None => "".to_owned(), - }; - println!( - "Name={} Birthdate={} Age={}", - birthday.name, birthday.date, age - ); - } -} diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 0000000..3383563 --- /dev/null +++ b/src/output.rs @@ -0,0 +1,51 @@ +use birthday::Birthday; +use chrono::{Datelike, NaiveDate}; +use tabled::{settings::Style, Table, Tabled}; + +#[derive(Tabled)] +struct DisplayedBirthday { + #[tabled(rename = "Id")] + id: i32, + #[tabled(rename = "Name")] + name: String, + #[tabled(rename = "Birthday")] + birthday: String, + #[tabled(rename = "Age")] + age: String, + #[tabled(rename = "Next birthday")] + next_birthday: String, +} + +impl DisplayedBirthday { + fn from_birthday(birthday: &Birthday, today: NaiveDate) -> DisplayedBirthday { + let age = birthday.age(today).unwrap(); + let birthyear = birthday.date.year(); + let next_birthday_in_days = (birthday.next(today) - today).num_days(); + let next_birthday = if birthday.is_today(today) { + "today".to_string() + } else { + match next_birthday_in_days { + 1 => "1 day".to_string(), + _ => format!("{next_birthday_in_days} days"), + } + }; + DisplayedBirthday { + id: birthday.id, + name: birthday.name.clone(), + birthday: birthday.date.format("%d %B").to_string(), + age: format!("{age} ({birthyear})"), + next_birthday, + } + } +} + +pub fn output(birthdays: Vec, today: NaiveDate) { + let displayed_birthdays: Vec = birthdays + .iter() + .map(|birthday| DisplayedBirthday::from_birthday(birthday, today)) + .collect(); + let table = Table::new(displayed_birthdays) + .with(Style::rounded()) + .to_string(); + println!("{table}") +}