From 77404d1642af99e26fe764a9f451b4626acb16c6 Mon Sep 17 00:00:00 2001 From: Matteo Settenvini Date: Fri, 29 Jul 2022 19:52:50 +0200 Subject: [PATCH] Fix outputting tzid and alarm trigger --- Cargo.lock | 71 ++++++++++++------------------------ Cargo.toml | 8 ++-- src/api_client.rs | 63 +++++++++++++++++++++++++++++--- src/commands/import.rs | 4 +- src/commands/schedule.rs | 4 +- src/commands/schedule_csv.rs | 21 ++++++----- src/event.rs | 24 ++++++++---- 7 files changed, 117 insertions(+), 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a83d89..6bb7b82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "android_system_properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.58" @@ -160,6 +169,7 @@ dependencies = [ "csv", "directories", "futures", + "iana-time-zone", "ics", "minicaldav", "reqwest", @@ -170,7 +180,7 @@ dependencies = [ "strum_macros", "tokio", "toml", - "uuid", + "ureq", "webbrowser", ] @@ -571,6 +581,19 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c0d80ad9ca8d30ca648bf6cb1e3e3326d75071b76dbe143dd4a9cedcd58975" +dependencies = [ + "android_system_properties", + "core-foundation", + "js-sys", + "wasm-bindgen", + "winapi", +] + [[package]] name = "ics" version = "0.5.7" @@ -944,12 +967,6 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - [[package]] name = "proc-macro-crate" version = "1.1.3" @@ -978,36 +995,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "redox_syscall" version = "0.2.13" @@ -1529,16 +1516,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "uuid" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" -dependencies = [ - "getrandom", - "rand", -] - [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 9ac7e8c..06b7af8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,9 @@ version = "0.13" version = "0.4" features = ["serde"] +[dependencies.iana-time-zone] +version = "0.1" + [dependencies.csv] version = "1.1" @@ -69,9 +72,8 @@ version = "0.5" version = "1" features = ["rt-multi-thread", "net", "macros"] -[dependencies.uuid] -version = "1.1" -features = ["v4", "fast-rng"] +[dependencies.ureq] +version = "2.5" [dependencies.webbrowser] version = "0.7" diff --git a/src/api_client.rs b/src/api_client.rs index 6acb3e4..33d7c43 100644 --- a/src/api_client.rs +++ b/src/api_client.rs @@ -2,18 +2,21 @@ // SPDX-License-Identifier: AGPL-3.0-or-later use { - crate::config::Config, crate::constants, anyhow::anyhow, + crate::config::Config, crate::constants, anyhow::anyhow, anyhow::Result, base64::write::EncoderWriter as Base64Encoder, reqwest::Url, std::io::Write, }; pub struct ApiClient { - pub base_url: Url, - pub username: String, - pub rest: reqwest::Client, + rest: reqwest::Client, + agent: ureq::Agent, + base_url: Url, + caldav_base_url: Url, + username: String, + password: String, } impl ApiClient { - pub fn new(server_name: &str, configuration: &Config) -> anyhow::Result { + pub fn new(server_name: &str, configuration: &Config) -> Result { let server = configuration .credentials .servers @@ -43,10 +46,58 @@ impl ApiClient { .default_headers(default_headers) .build()?; + let base_url = Url::parse(&server.url)?; + let caldav_base_url = futures::executor::block_on( + rest_client + .head(base_url.join("/.well-known/caldav")?) + .send(), + )? + .url() + .clone(); + Ok(ApiClient { - base_url: Url::parse(&server.url)?, + base_url, + caldav_base_url, username: server.login_name.clone(), + password: server.password.clone(), rest: rest_client, + agent: ureq::Agent::new(), }) } + + pub fn rest(&self) -> &reqwest::Client { + &self.rest + } + + pub fn base_url(&self) -> &Url { + &self.base_url + } + + pub fn username(&self) -> &str { + &self.username + } + + pub fn get_calendars( + &self, + ) -> core::result::Result, minicaldav::Error> { + minicaldav::get_calendars( + self.agent.clone(), + self.username(), + &self.password, + &self.caldav_base_url, + ) + } + + pub fn get_events( + &self, + calendar: &minicaldav::Calendar, + ) -> core::result::Result<(Vec, Vec), minicaldav::Error> + { + minicaldav::get_events( + self.agent.clone(), + self.username(), + &self.password, + calendar, + ) + } } diff --git a/src/commands/import.rs b/src/commands/import.rs index 3ac8331..f7c0e82 100644 --- a/src/commands/import.rs +++ b/src/commands/import.rs @@ -10,8 +10,8 @@ where { for url in urls { let response = api_client - .rest - .post(api_client.base_url.join("apps/cookbook/import")?) + .rest() + .post(api_client.base_url().join("apps/cookbook/import")?) .json(&serde_json::json!({ "url": url.as_ref(), })) diff --git a/src/commands/schedule.rs b/src/commands/schedule.rs index f92cab2..529b660 100644 --- a/src/commands/schedule.rs +++ b/src/commands/schedule.rs @@ -5,8 +5,8 @@ use {crate::api_client::ApiClient, crate::recipe, anyhow::Result}; pub async fn with(api_client: &ApiClient) -> Result<()> { let recipes = api_client - .rest - .get(api_client.base_url.join("apps/cookbook/api/recipes")?) + .rest() + .get(api_client.base_url().join("apps/cookbook/api/recipes")?) .send() .await?; println!("{:#?}", recipes.json::>().await?); diff --git a/src/commands/schedule_csv.rs b/src/commands/schedule_csv.rs index 4fa0c44..40769fa 100644 --- a/src/commands/schedule_csv.rs +++ b/src/commands/schedule_csv.rs @@ -71,8 +71,8 @@ where async fn get_all_recipes(api_client: &ApiClient) -> Result>> { let metadata = api_client - .rest - .get(api_client.base_url.join("apps/cookbook/api/recipes")?) + .rest() + .get(api_client.base_url().join("apps/cookbook/api/recipes")?) .send() .await? .json::>() @@ -80,10 +80,10 @@ async fn get_all_recipes(api_client: &ApiClient) -> Result From for CalEvent<'a> { fn from(ev: Event) -> Self { - let start_time = ev.ends_at - ev.recipe.total_time(); + 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()))); @@ -57,18 +61,22 @@ impl<'a> From for CalEvent<'a> { ev.recipe.id ))); event.push(calprop::Location::new(escape_text(ev.recipe.url.clone()))); - event.push(calprop::DtStart::new(dt_fmt( - &Local.from_local_datetime(&start_time).unwrap(), - ))); - event.push(calprop::DtEnd::new(dt_fmt( - &Local.from_local_datetime(&ev.ends_at).unwrap(), - ))); + + 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 event.push(calprop::RRule::new("FREQ=YEARLY")); + let mut trigger = calprop::Trigger::new("-PT15M"); + trigger.append(ics::parameters!("RELATED" => "START")); let alarm = ics::Alarm::display( - calprop::Trigger::new("-P15M"), + trigger, calprop::Description::new(escape_text(ev.recipe.name.clone())), ); event.add_alarm(alarm);