use super::calendar::{create_timetable, get_todays_date}; use chrono::NaiveDate; use chrono::NaiveTime; use chrono_tz::Etc::UTC; use chrono_tz::Tz; use clap::Parser; use std::error::Error; use std::fmt; use std::iter; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] pub struct Args { #[arg(short, long)] pub timezone: Vec, #[arg(short, long)] pub date: Option, #[arg(short, long, default_value = "Local")] pub local: String, pub time: String, } #[derive(PartialEq, Debug)] pub enum CliError { CouldNotParseTime, CouldNotParseDate, CouldNotParseTimezone(String), CouldNoGenerateTimetable(String), } impl fmt::Display for CliError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let message = match *self { Self::CouldNotParseTime => "Couldn't parse time".to_string(), Self::CouldNotParseDate => "Couldn't parse date".to_string(), Self::CouldNotParseTimezone(ref tz) => { format!("Couldn't parse timezone {}", tz).to_string() } Self::CouldNoGenerateTimetable(ref er) => { format!("Could not generate timetable: {}", er) } }; f.write_str(&message) } } impl Error for CliError {} pub fn print_timezones( timezones: &Vec, time: &String, date: Option<&String>, local_timezone: &String, ) -> Result<(), CliError> { let todays_date = &get_todays_date(); let date = date.unwrap_or(todays_date); let parsed_time = if let Ok(hour) = time.parse::() { NaiveTime::from_hms_opt(hour, 0, 0).ok_or(CliError::CouldNotParseTime)? } else { NaiveTime::parse_from_str(time, "%H:%M").map_err(|_| CliError::CouldNotParseTime)? }; let parsed_date = NaiveDate::parse_from_str(date, "%d-%m-%Y") .or_else(|_| NaiveDate::parse_from_str(date, "%d-%m")) .map_err(|_| CliError::CouldNotParseDate)?; let parsed_local_timezone: Tz = if local_timezone == "Local" { UTC // FIXME: use local timezone instead } else { local_timezone .parse::() .map_err(|_| CliError::CouldNoGenerateTimetable(local_timezone.to_string()))? }; let parsed_timezones: Vec = timezones .iter() .map(|tz| { tz.parse::() .map_err(|_| CliError::CouldNotParseTimezone(tz.to_string())) }) .collect::, _>>()?; let all_timezones: Vec = iter::once(parsed_local_timezone) .chain(parsed_timezones) .collect(); let max_len = all_timezones .iter() .map(|t| t.name().len()) .max() .unwrap_or(0) as u32; for timezone in all_timezones { let timezone_name = timezone.name(); let name_padding_len = max_len - timezone_name.len() as u32; match create_timetable( &parsed_time, &parsed_date, &parsed_local_timezone, &timezone, 5, ) { Ok(timetable) => { print_timetable(&timezone_name.to_string(), name_padding_len, &timetable) } Err(error) => panic!("Failed: {}", error), } } Ok(()) } fn print_timetable(name: &String, name_padding_len: u32, timetable: &Vec) { let formatted: Vec = timetable .iter() .enumerate() .map(|(i, t)| t.format("%H:%M").to_string()) .collect(); let name_padding = (0..name_padding_len) .map(|_| " ".to_string()) .collect::>() .join(""); println!("{}:{}\t{}", name, name_padding, formatted.join("\t")); }