Fix outputting tzid and alarm trigger
This commit is contained in:
parent
096db12c74
commit
77404d1642
|
@ -8,6 +8,15 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
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]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.58"
|
version = "1.0.58"
|
||||||
|
@ -160,6 +169,7 @@ dependencies = [
|
||||||
"csv",
|
"csv",
|
||||||
"directories",
|
"directories",
|
||||||
"futures",
|
"futures",
|
||||||
|
"iana-time-zone",
|
||||||
"ics",
|
"ics",
|
||||||
"minicaldav",
|
"minicaldav",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
@ -170,7 +180,7 @@ dependencies = [
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
"uuid",
|
"ureq",
|
||||||
"webbrowser",
|
"webbrowser",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -571,6 +581,19 @@ dependencies = [
|
||||||
"tokio-native-tls",
|
"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]]
|
[[package]]
|
||||||
name = "ics"
|
name = "ics"
|
||||||
version = "0.5.7"
|
version = "0.5.7"
|
||||||
|
@ -944,12 +967,6 @@ version = "0.3.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ppv-lite86"
|
|
||||||
version = "0.2.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-crate"
|
name = "proc-macro-crate"
|
||||||
version = "1.1.3"
|
version = "1.1.3"
|
||||||
|
@ -978,36 +995,6 @@ dependencies = [
|
||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.13"
|
version = "0.2.13"
|
||||||
|
@ -1529,16 +1516,6 @@ dependencies = [
|
||||||
"percent-encoding",
|
"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]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
|
|
@ -26,6 +26,9 @@ version = "0.13"
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
features = ["serde"]
|
features = ["serde"]
|
||||||
|
|
||||||
|
[dependencies.iana-time-zone]
|
||||||
|
version = "0.1"
|
||||||
|
|
||||||
[dependencies.csv]
|
[dependencies.csv]
|
||||||
version = "1.1"
|
version = "1.1"
|
||||||
|
|
||||||
|
@ -69,9 +72,8 @@ version = "0.5"
|
||||||
version = "1"
|
version = "1"
|
||||||
features = ["rt-multi-thread", "net", "macros"]
|
features = ["rt-multi-thread", "net", "macros"]
|
||||||
|
|
||||||
[dependencies.uuid]
|
[dependencies.ureq]
|
||||||
version = "1.1"
|
version = "2.5"
|
||||||
features = ["v4", "fast-rng"]
|
|
||||||
|
|
||||||
[dependencies.webbrowser]
|
[dependencies.webbrowser]
|
||||||
version = "0.7"
|
version = "0.7"
|
||||||
|
|
|
@ -2,18 +2,21 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
use {
|
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,
|
base64::write::EncoderWriter as Base64Encoder, reqwest::Url, std::io::Write,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ApiClient {
|
pub struct ApiClient {
|
||||||
pub base_url: Url,
|
rest: reqwest::Client,
|
||||||
pub username: String,
|
agent: ureq::Agent,
|
||||||
pub rest: reqwest::Client,
|
base_url: Url,
|
||||||
|
caldav_base_url: Url,
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApiClient {
|
impl ApiClient {
|
||||||
pub fn new(server_name: &str, configuration: &Config) -> anyhow::Result<Self> {
|
pub fn new(server_name: &str, configuration: &Config) -> Result<Self> {
|
||||||
let server = configuration
|
let server = configuration
|
||||||
.credentials
|
.credentials
|
||||||
.servers
|
.servers
|
||||||
|
@ -43,10 +46,58 @@ impl ApiClient {
|
||||||
.default_headers(default_headers)
|
.default_headers(default_headers)
|
||||||
.build()?;
|
.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 {
|
Ok(ApiClient {
|
||||||
base_url: Url::parse(&server.url)?,
|
base_url,
|
||||||
|
caldav_base_url,
|
||||||
username: server.login_name.clone(),
|
username: server.login_name.clone(),
|
||||||
|
password: server.password.clone(),
|
||||||
rest: rest_client,
|
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<Vec<minicaldav::Calendar>, 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<minicaldav::Event>, Vec<minicaldav::Error>), minicaldav::Error>
|
||||||
|
{
|
||||||
|
minicaldav::get_events(
|
||||||
|
self.agent.clone(),
|
||||||
|
self.username(),
|
||||||
|
&self.password,
|
||||||
|
calendar,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ where
|
||||||
{
|
{
|
||||||
for url in urls {
|
for url in urls {
|
||||||
let response = api_client
|
let response = api_client
|
||||||
.rest
|
.rest()
|
||||||
.post(api_client.base_url.join("apps/cookbook/import")?)
|
.post(api_client.base_url().join("apps/cookbook/import")?)
|
||||||
.json(&serde_json::json!({
|
.json(&serde_json::json!({
|
||||||
"url": url.as_ref(),
|
"url": url.as_ref(),
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -5,8 +5,8 @@ use {crate::api_client::ApiClient, crate::recipe, anyhow::Result};
|
||||||
|
|
||||||
pub async fn with(api_client: &ApiClient) -> Result<()> {
|
pub async fn with(api_client: &ApiClient) -> Result<()> {
|
||||||
let recipes = api_client
|
let recipes = api_client
|
||||||
.rest
|
.rest()
|
||||||
.get(api_client.base_url.join("apps/cookbook/api/recipes")?)
|
.get(api_client.base_url().join("apps/cookbook/api/recipes")?)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
println!("{:#?}", recipes.json::<Vec<recipe::Metadata>>().await?);
|
println!("{:#?}", recipes.json::<Vec<recipe::Metadata>>().await?);
|
||||||
|
|
|
@ -71,8 +71,8 @@ where
|
||||||
|
|
||||||
async fn get_all_recipes(api_client: &ApiClient) -> Result<HashMap<String, Rc<recipe::Recipe>>> {
|
async fn get_all_recipes(api_client: &ApiClient) -> Result<HashMap<String, Rc<recipe::Recipe>>> {
|
||||||
let metadata = api_client
|
let metadata = api_client
|
||||||
.rest
|
.rest()
|
||||||
.get(api_client.base_url.join("apps/cookbook/api/recipes")?)
|
.get(api_client.base_url().join("apps/cookbook/api/recipes")?)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.json::<Vec<recipe::Metadata>>()
|
.json::<Vec<recipe::Metadata>>()
|
||||||
|
@ -80,10 +80,10 @@ async fn get_all_recipes(api_client: &ApiClient) -> Result<HashMap<String, Rc<re
|
||||||
|
|
||||||
let recipes = metadata.iter().map(|rm| async {
|
let recipes = metadata.iter().map(|rm| async {
|
||||||
let response = api_client
|
let response = api_client
|
||||||
.rest
|
.rest()
|
||||||
.get(
|
.get(
|
||||||
api_client
|
api_client
|
||||||
.base_url
|
.base_url()
|
||||||
.join(&format!("apps/cookbook/api/recipes/{id}", id = rm.id))
|
.join(&format!("apps/cookbook/api/recipes/{id}", id = rm.id))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
)
|
)
|
||||||
|
@ -113,21 +113,22 @@ where
|
||||||
let calendar_prototype: ics::ICalendar = ics::ICalendar::new(
|
let calendar_prototype: ics::ICalendar = ics::ICalendar::new(
|
||||||
"2.0",
|
"2.0",
|
||||||
format!(
|
format!(
|
||||||
"-//{}//NONSGML {}//EN",
|
"-//IDN {}//{} {}//EN",
|
||||||
constants::VENDOR,
|
constants::VENDOR,
|
||||||
env!("CARGO_PKG_NAME")
|
env!("CARGO_PKG_NAME"),
|
||||||
|
env!("CARGO_PKG_VERSION")
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
let dav_base = api_client
|
let dav_base = api_client
|
||||||
.rest
|
.rest()
|
||||||
.head(api_client.base_url.join("/.well-known/caldav")?)
|
.head(api_client.base_url().join("/.well-known/caldav")?)
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let calendar_url = dav_base.url().join(&format!(
|
let calendar_url = dav_base.url().join(&format!(
|
||||||
"calendars/{}/{}/",
|
"calendars/{}/{}/",
|
||||||
&api_client.username,
|
&api_client.username(),
|
||||||
calendar.to_lowercase().as_str().replace(" ", "-")
|
calendar.to_lowercase().as_str().replace(" ", "-")
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
|
@ -139,7 +140,7 @@ where
|
||||||
cal.add_event(ev.into());
|
cal.add_event(ev.into());
|
||||||
|
|
||||||
api_client
|
api_client
|
||||||
.rest
|
.rest()
|
||||||
.put(url)
|
.put(url)
|
||||||
.header("Content-Type", "text/calendar; charset=utf-8")
|
.header("Content-Type", "text/calendar; charset=utf-8")
|
||||||
.body(cal.to_string())
|
.body(cal.to_string())
|
||||||
|
|
24
src/event.rs
24
src/event.rs
|
@ -48,7 +48,11 @@ impl Event {
|
||||||
|
|
||||||
impl<'a> From<Event> for CalEvent<'a> {
|
impl<'a> From<Event> for CalEvent<'a> {
|
||||||
fn from(ev: Event) -> Self {
|
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()));
|
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::Summary::new(escape_text(ev.recipe.name.clone())));
|
||||||
|
@ -57,18 +61,22 @@ impl<'a> From<Event> for CalEvent<'a> {
|
||||||
ev.recipe.id
|
ev.recipe.id
|
||||||
)));
|
)));
|
||||||
event.push(calprop::Location::new(escape_text(ev.recipe.url.clone())));
|
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(),
|
let mut dtstart = calprop::DtStart::new(dt_fmt(&start_time));
|
||||||
)));
|
dtstart.append(ics::parameters!("TZID" => timezone.clone()));
|
||||||
event.push(calprop::DtEnd::new(dt_fmt(
|
event.push(dtstart);
|
||||||
&Local.from_local_datetime(&ev.ends_at).unwrap(),
|
|
||||||
)));
|
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
|
// TODO make configurable yearly repetition, for now this suits my personal uses
|
||||||
event.push(calprop::RRule::new("FREQ=YEARLY"));
|
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(
|
let alarm = ics::Alarm::display(
|
||||||
calprop::Trigger::new("-P15M"),
|
trigger,
|
||||||
calprop::Description::new(escape_text(ev.recipe.name.clone())),
|
calprop::Description::new(escape_text(ev.recipe.name.clone())),
|
||||||
);
|
);
|
||||||
event.add_alarm(alarm);
|
event.add_alarm(alarm);
|
||||||
|
|
Loading…
Reference in New Issue