// SPDX-FileCopyrightText: 2022 Matteo Settenvini // SPDX-License-Identifier: AGPL-3.0-or-later use { crate::constants, crate::recipe::Recipe, chrono::{DateTime, Datelike, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}, ics::escape_text, ics::properties as calprop, ics::Event as CalEvent, std::rc::Rc, }; pub struct Event { pub uid: String, pub ends_at: NaiveDateTime, pub recipe: Rc, } #[derive(strum_macros::Display)] pub enum Meal { Lunch, Dinner, } impl Event { pub fn new(date: NaiveDate, meal: Meal, recipe: Rc) -> Self { let uid = format!( "{}-{}@{}.montecristosoftware.eu", date, meal, env!("CARGO_PKG_NAME") ); let meal_time = match meal { Meal::Lunch => NaiveTime::from_hms(12, 00, 00), Meal::Dinner => NaiveTime::from_hms(19, 00, 00), }; let ends_at = NaiveDateTime::new(date, meal_time); Event { uid, ends_at, recipe, } } } impl<'a> From for CalEvent<'a> { fn from(ev: Event) -> Self { let start_time = Local .from_local_datetime(&(*&ev.ends_at - ev.recipe.total_time())) .unwrap(); let end_time = Local.from_local_datetime(&ev.ends_at).unwrap(); let timezone = iana_time_zone::get_timezone().unwrap(); let mut event = ics::Event::new(ev.uid.clone(), dt_utc_fmt(&Utc::now())); event.push(calprop::Summary::new(escape_text(ev.recipe.name.clone()))); event.push(calprop::Description::new(format!( "cookbook@{}", ev.recipe.id ))); event.push(calprop::Location::new(escape_text(ev.recipe.url.clone()))); let mut dtstart = calprop::DtStart::new(dt_fmt(&start_time)); dtstart.append(ics::parameters!("TZID" => timezone.clone())); event.push(dtstart); let mut dtend = calprop::DtEnd::new(dt_fmt(&end_time)); dtend.append(ics::parameters!("TZID" => timezone)); event.push(dtend); // TODO make configurable yearly repetition, for now this suits my personal uses const DAY_NAMES: [&str; 7] = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"]; event.push(calprop::RRule::new(format!( "FREQ=YEARLY;BYDAY={weekday};BYWEEKNO={weekno}", weekday = DAY_NAMES .get(start_time.weekday().num_days_from_monday() as usize) .unwrap(), weekno = start_time.iso_week().week(), ))); let mut trigger = calprop::Trigger::new("-PT15M"); trigger.append(ics::parameters!("RELATED" => "START")); let alarm = ics::Alarm::display( trigger, calprop::Description::new(escape_text(ev.recipe.name.clone())), ); event.add_alarm(alarm); event } } fn dt_fmt(datetime: &DateTime) -> String { datetime.format(constants::ICAL_LOCALTIME_FMT).to_string() } fn dt_utc_fmt(datetime: &DateTime) -> String { datetime.format(constants::ICAL_UTCTIME_FMT).to_string() }