nextcloud-cooking-schedule/src/event.rs

102 lines
3.1 KiB
Rust

// SPDX-FileCopyrightText: 2022 Matteo Settenvini <matteo.settenvini@montecristosoftware.eu>
// 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<Recipe>,
}
#[derive(strum_macros::Display)]
pub enum Meal {
Lunch,
Dinner,
}
impl Event {
pub fn new(date: NaiveDate, meal: Meal, recipe: Rc<Recipe>) -> 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<Event> 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<Local>) -> String {
datetime.format(constants::ICAL_LOCALTIME_FMT).to_string()
}
fn dt_utc_fmt(datetime: &DateTime<Utc>) -> String {
datetime.format(constants::ICAL_UTCTIME_FMT).to_string()
}