Collect ingredients from recipes for grocery list
This commit is contained in:
parent
c649fbf88c
commit
ed5a6d2306
|
@ -18,7 +18,7 @@
|
||||||
"kind": "bin"
|
"kind": "bin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"args": ["schedule-csv", "Cucina", "examples/example-schedule.csv"],
|
"args": ["groceries", "Cucina"],
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,12 +2,6 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "adler"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.18"
|
version = "0.7.18"
|
||||||
|
@ -117,12 +111,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "chunked_transfer"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ci_info"
|
name = "ci_info"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
|
@ -179,9 +167,10 @@ dependencies = [
|
||||||
"directories",
|
"directories",
|
||||||
"futures",
|
"futures",
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
|
"icalendar",
|
||||||
"ics",
|
"ics",
|
||||||
|
"libxml",
|
||||||
"markdown-gen",
|
"markdown-gen",
|
||||||
"minicaldav",
|
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rusty-hook",
|
"rusty-hook",
|
||||||
|
@ -191,7 +180,6 @@ dependencies = [
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
"ureq",
|
|
||||||
"webbrowser",
|
"webbrowser",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -211,15 +199,6 @@ version = "0.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc32fast"
|
|
||||||
version = "1.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "csv"
|
name = "csv"
|
||||||
version = "1.1.6"
|
version = "1.1.6"
|
||||||
|
@ -325,16 +304,6 @@ dependencies = [
|
||||||
"instant",
|
"instant",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "flate2"
|
|
||||||
version = "1.0.24"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
|
|
||||||
dependencies = [
|
|
||||||
"crc32fast",
|
|
||||||
"miniz_oxide",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
@ -605,6 +574,17 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "icalendar"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf4c8ac7e9b7eefe850f6380c68ef5fd1300858c1ef65d121d602a9bab63c2f4"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"nom",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ics"
|
name = "ics"
|
||||||
version = "0.5.7"
|
version = "0.5.7"
|
||||||
|
@ -706,6 +686,17 @@ version = "0.2.126"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libxml"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "687f5a78939052c5d02865c0fe3ea2ce2acdca875f7f81db82f7aef256dd97ac"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.17"
|
version = "0.4.17"
|
||||||
|
@ -740,26 +731,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minicaldav"
|
name = "minimal-lexical"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb263a7d12c40d5f200dda93b3665b9ae714d4fe64a6467938c92d974a579edb"
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
dependencies = [
|
|
||||||
"base64",
|
|
||||||
"log",
|
|
||||||
"ureq",
|
|
||||||
"url",
|
|
||||||
"xmltree",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miniz_oxide"
|
|
||||||
version = "0.5.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
|
|
||||||
dependencies = [
|
|
||||||
"adler",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
|
@ -853,6 +828,16 @@ version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab250442c86f1850815b5d268639dff018c0627022bc1940eb2d642ca1ce12f0"
|
checksum = "ab250442c86f1850815b5d268639dff018c0627022bc1940eb2d642ca1ce12f0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.45"
|
version = "0.1.45"
|
||||||
|
@ -1101,33 +1086,6 @@ dependencies = [
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ring"
|
|
||||||
version = "0.16.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"once_cell",
|
|
||||||
"spin",
|
|
||||||
"untrusted",
|
|
||||||
"web-sys",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls"
|
|
||||||
version = "0.20.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"ring",
|
|
||||||
"sct",
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
|
@ -1171,16 +1129,6 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sct"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
|
||||||
dependencies = [
|
|
||||||
"ring",
|
|
||||||
"untrusted",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
|
@ -1273,12 +1221,6 @@ dependencies = [
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spin"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -1514,30 +1456,6 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "untrusted"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ureq"
|
|
||||||
version = "2.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f"
|
|
||||||
dependencies = [
|
|
||||||
"base64",
|
|
||||||
"chunked_transfer",
|
|
||||||
"encoding_rs",
|
|
||||||
"flate2",
|
|
||||||
"log",
|
|
||||||
"once_cell",
|
|
||||||
"rustls",
|
|
||||||
"url",
|
|
||||||
"webpki",
|
|
||||||
"webpki-roots",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.2.2"
|
version = "2.2.2"
|
||||||
|
@ -1550,6 +1468,15 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -1679,25 +1606,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webpki"
|
|
||||||
version = "0.22.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
|
||||||
dependencies = [
|
|
||||||
"ring",
|
|
||||||
"untrusted",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webpki-roots"
|
|
||||||
version = "0.22.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf"
|
|
||||||
dependencies = [
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "widestring"
|
name = "widestring"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -1786,18 +1694,3 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "xml-rs"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "xmltree"
|
|
||||||
version = "0.10.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb"
|
|
||||||
dependencies = [
|
|
||||||
"xml-rs",
|
|
||||||
]
|
|
||||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -42,15 +42,19 @@ version = "4.0"
|
||||||
[dependencies.futures]
|
[dependencies.futures]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
|
||||||
|
[dependencies.icalendar]
|
||||||
|
version = "0.13.0"
|
||||||
|
features = ["parser"]
|
||||||
|
|
||||||
[dependencies.ics]
|
[dependencies.ics]
|
||||||
version = "0.5"
|
version = "0.5"
|
||||||
|
|
||||||
|
[dependencies.libxml]
|
||||||
|
version = "0.3"
|
||||||
|
|
||||||
[dependencies.markdown-gen]
|
[dependencies.markdown-gen]
|
||||||
version = "1.2"
|
version = "1.2"
|
||||||
|
|
||||||
[dependencies.minicaldav]
|
|
||||||
version = "0.2"
|
|
||||||
|
|
||||||
[dependencies.regex]
|
[dependencies.regex]
|
||||||
version = "1.6"
|
version = "1.6"
|
||||||
|
|
||||||
|
@ -78,8 +82,5 @@ version = "0.5"
|
||||||
version = "1"
|
version = "1"
|
||||||
features = ["rt-multi-thread", "net", "macros"]
|
features = ["rt-multi-thread", "net", "macros"]
|
||||||
|
|
||||||
[dependencies.ureq]
|
|
||||||
version = "2.5"
|
|
||||||
|
|
||||||
[dependencies.webbrowser]
|
[dependencies.webbrowser]
|
||||||
version = "0.7"
|
version = "0.7"
|
||||||
|
|
|
@ -3,16 +3,15 @@
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::config::Config, crate::constants, anyhow::anyhow, anyhow::Result,
|
crate::config::Config, crate::constants, anyhow::anyhow, anyhow::Result,
|
||||||
base64::write::EncoderWriter as Base64Encoder, reqwest::Url, std::io::Write,
|
base64::write::EncoderWriter as Base64Encoder, chrono::DateTime, icalendar::CalendarComponent,
|
||||||
|
reqwest::Url, std::io::Write, std::ops::Range,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ApiClient {
|
pub struct ApiClient {
|
||||||
rest: reqwest::Client,
|
rest: reqwest::Client,
|
||||||
agent: ureq::Agent,
|
|
||||||
base_url: Url,
|
base_url: Url,
|
||||||
caldav_base_url: Url,
|
caldav_base_url: Url,
|
||||||
username: String,
|
username: String,
|
||||||
password: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApiClient {
|
impl ApiClient {
|
||||||
|
@ -59,9 +58,7 @@ impl ApiClient {
|
||||||
base_url,
|
base_url,
|
||||||
caldav_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(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,27 +74,97 @@ impl ApiClient {
|
||||||
&self.username
|
&self.username
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_calendars(
|
pub async fn get_events<Tz>(
|
||||||
&self,
|
&self,
|
||||||
) -> core::result::Result<Vec<minicaldav::Calendar>, minicaldav::Error> {
|
calendar_name: &str,
|
||||||
minicaldav::get_calendars(
|
date_range: Range<DateTime<Tz>>,
|
||||||
self.agent.clone(),
|
) -> Result<Vec<icalendar::Event>>
|
||||||
self.username(),
|
where
|
||||||
&self.password,
|
Tz: chrono::TimeZone,
|
||||||
&self.caldav_base_url,
|
Tz::Offset: std::fmt::Display,
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_events(
|
|
||||||
&self,
|
|
||||||
calendar: &minicaldav::Calendar,
|
|
||||||
) -> core::result::Result<(Vec<minicaldav::Event>, Vec<minicaldav::Error>), minicaldav::Error>
|
|
||||||
{
|
{
|
||||||
minicaldav::get_events(
|
let report_method = reqwest::Method::from_bytes(b"REPORT").unwrap();
|
||||||
self.agent.clone(),
|
let events_xml = self
|
||||||
self.username(),
|
.rest()
|
||||||
&self.password,
|
.request(
|
||||||
calendar,
|
report_method,
|
||||||
)
|
// TODO extract into helper method
|
||||||
|
self.caldav_base_url.join(&format!(
|
||||||
|
"calendars/{}/{}",
|
||||||
|
self.username(),
|
||||||
|
calendar_name.to_lowercase().replace(" ", "-")
|
||||||
|
))?,
|
||||||
|
)
|
||||||
|
.header("Prefer", "return-minimal")
|
||||||
|
.header("Content-Type", "application/xml; charset=utf-8")
|
||||||
|
.header("Depth", 1)
|
||||||
|
.body(format!(
|
||||||
|
"<c:calendar-query xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\">
|
||||||
|
<d:prop>
|
||||||
|
<c:calendar-data />
|
||||||
|
</d:prop>
|
||||||
|
<c:filter>
|
||||||
|
<c:comp-filter name=\"VCALENDAR\">
|
||||||
|
<c:prop-filter name=\"PRODID\">
|
||||||
|
<c:text-match>{}</c:text-match>
|
||||||
|
</c:prop-filter>
|
||||||
|
<c:comp-filter name=\"VEVENT\">
|
||||||
|
<c:prop-filter name=\"DTSTART\">
|
||||||
|
<time-range start=\"{}\" end=\"{}\" />
|
||||||
|
</c:prop-filter>
|
||||||
|
</c:comp-filter>
|
||||||
|
</c:comp-filter>
|
||||||
|
</c:filter>
|
||||||
|
</c:calendar-query>
|
||||||
|
",
|
||||||
|
constants::CALENDAR_PROVIDER,
|
||||||
|
date_range
|
||||||
|
.start
|
||||||
|
.naive_utc()
|
||||||
|
.format(constants::ICAL_UTCTIME_FMT)
|
||||||
|
.to_string(),
|
||||||
|
date_range
|
||||||
|
.end
|
||||||
|
.naive_utc()
|
||||||
|
.format(constants::ICAL_UTCTIME_FMT)
|
||||||
|
.to_string()
|
||||||
|
))
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.text()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let xml_doc = libxml::parser::Parser::default().parse_string(events_xml.as_bytes())?;
|
||||||
|
let mut xpath_ctx = libxml::xpath::Context::new(&xml_doc).unwrap();
|
||||||
|
xpath_ctx
|
||||||
|
.register_namespace("cal", "urn:ietf:params:xml:ns:caldav")
|
||||||
|
.unwrap();
|
||||||
|
let calendars_txt = xpath_ctx
|
||||||
|
.findnodes("//cal:calendar-data/text()", None)
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|n| icalendar::parser::unfold(&n.get_content()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let calendars = calendars_txt
|
||||||
|
.iter()
|
||||||
|
.map(|cal| icalendar::parser::read_calendar(cal).unwrap())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let events = calendars
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|cal| {
|
||||||
|
cal.components
|
||||||
|
.into_iter()
|
||||||
|
.filter(|comp| comp.name == "VEVENT")
|
||||||
|
})
|
||||||
|
.map(|comp| {
|
||||||
|
let event: CalendarComponent = comp.into();
|
||||||
|
match event {
|
||||||
|
CalendarComponent::Event(e) => e,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(events.collect::<Vec<_>>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,17 @@
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::api_client::ApiClient,
|
crate::api_client::ApiClient,
|
||||||
crate::constants,
|
|
||||||
crate::recipe::{Ingredient, Recipe},
|
crate::recipe::{Ingredient, Recipe},
|
||||||
anyhow::{anyhow, Result},
|
anyhow::Result,
|
||||||
chrono::{Duration, Local, NaiveDateTime},
|
chrono::{Duration, Local},
|
||||||
|
icalendar::Component,
|
||||||
regex::Regex,
|
regex::Regex,
|
||||||
std::collections::HashSet,
|
std::collections::HashSet,
|
||||||
|
std::ops::Range,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn with(api_client: &ApiClient, calendar_name: &str, days: u32) -> Result<()> {
|
pub async fn with(api_client: &ApiClient, calendar_name: &str, days: u32) -> Result<()> {
|
||||||
let ids = map_events_to_recipe_ids(api_client, calendar_name, days)?;
|
let ids = map_events_to_recipe_ids(api_client, calendar_name, days).await?;
|
||||||
let ingredients = get_ingredients(api_client, ids).await?;
|
let ingredients = get_ingredients(api_client, ids).await?;
|
||||||
|
|
||||||
todo!("Recipe ingredients: {:?}", ingredients)
|
todo!("Recipe ingredients: {:?}", ingredients)
|
||||||
|
@ -22,57 +23,25 @@ pub async fn with(api_client: &ApiClient, calendar_name: &str, days: u32) -> Res
|
||||||
// save_grocery_list(api_client, "", md).await?;
|
// save_grocery_list(api_client, "", md).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_events_to_recipe_ids(
|
async fn map_events_to_recipe_ids(
|
||||||
api_client: &ApiClient,
|
api_client: &ApiClient,
|
||||||
calendar_name: &str,
|
calendar_name: &str,
|
||||||
days: u32,
|
days: u32,
|
||||||
) -> Result<HashSet<usize>> {
|
) -> Result<HashSet<usize>> {
|
||||||
// minicaldav is quite hamfisted in retrieving events (it gets everything, every time).
|
let date_range = Range {
|
||||||
// Consider crafting an XML request and use something else for ical parsing.
|
start: Local::now(),
|
||||||
|
end: Local::now() + Duration::days(days as i64),
|
||||||
|
};
|
||||||
|
|
||||||
let calendars = api_client
|
let all_events = api_client.get_events(calendar_name, date_range).await?;
|
||||||
.get_calendars()
|
|
||||||
.map_err(|err| convert_caldav_err(err))?;
|
|
||||||
let calendar = calendars
|
|
||||||
.into_iter()
|
|
||||||
.filter(|c| c.name() == calendar_name)
|
|
||||||
.next()
|
|
||||||
.ok_or(anyhow!(
|
|
||||||
"Unable to find calendar '{}' on server",
|
|
||||||
calendar_name
|
|
||||||
))?;
|
|
||||||
|
|
||||||
let all_events = api_client
|
|
||||||
.get_events(&calendar)
|
|
||||||
.map_err(|err| convert_caldav_err(err))?
|
|
||||||
.0;
|
|
||||||
|
|
||||||
let relevant_events = all_events.into_iter().filter(|ev| {
|
|
||||||
ev.get("PRODID")
|
|
||||||
.map(|p| p == constants::CALENDAR_PROVIDER)
|
|
||||||
.unwrap_or(false)
|
|
||||||
&& {
|
|
||||||
let start_time = NaiveDateTime::parse_from_str(
|
|
||||||
ev.property("DTSTART").unwrap().value(),
|
|
||||||
"%Y%m%dT%H%M%S",
|
|
||||||
)
|
|
||||||
.unwrap_or(NaiveDateTime::from_timestamp(0, 0));
|
|
||||||
let now = Local::now().naive_local();
|
|
||||||
start_time >= now && start_time < now + Duration::days(days as i64)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let recipe_id_regex: Regex = Regex::new(r"cookbook@(\d+)").unwrap();
|
let recipe_id_regex: Regex = Regex::new(r"cookbook@(\d+)").unwrap();
|
||||||
let recipe_ids = relevant_events
|
let recipe_ids = all_events
|
||||||
.flat_map(|event| {
|
.iter()
|
||||||
event
|
.flat_map(|event| event.property_value("DESCRIPTION"))
|
||||||
.property("DESCRIPTION")
|
.flat_map(|descr| recipe_id_regex.captures(descr))
|
||||||
.iter()
|
.flat_map(|c| c.get(1))
|
||||||
.flat_map(|descr| recipe_id_regex.captures(descr.value()))
|
.flat_map(|m| m.as_str().parse::<usize>())
|
||||||
.flat_map(|c| c.get(1))
|
|
||||||
.flat_map(|m| m.as_str().parse::<usize>())
|
|
||||||
.collect::<Vec<usize>>()
|
|
||||||
})
|
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
|
|
||||||
Ok(recipe_ids)
|
Ok(recipe_ids)
|
||||||
|
@ -101,7 +70,3 @@ where
|
||||||
let ingredients = futures::future::try_join_all(ingredients).await?;
|
let ingredients = futures::future::try_join_all(ingredients).await?;
|
||||||
Ok(ingredients.into_iter().flatten().collect())
|
Ok(ingredients.into_iter().flatten().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_caldav_err(err: minicaldav::Error) -> anyhow::Error {
|
|
||||||
anyhow!(format!("{:?}", err))
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,3 +9,5 @@ pub const CALENDAR_PROVIDER: &str = concat!(
|
||||||
env!("CARGO_PKG_VERSION"),
|
env!("CARGO_PKG_VERSION"),
|
||||||
"//EN"
|
"//EN"
|
||||||
);
|
);
|
||||||
|
pub const ICAL_LOCALTIME_FMT: &str = "%Y%m%dT%H%M%S";
|
||||||
|
pub const ICAL_UTCTIME_FMT: &str = "%Y%m%dT%H%M%SZ";
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
use {
|
use {
|
||||||
|
crate::constants,
|
||||||
crate::recipe::Recipe,
|
crate::recipe::Recipe,
|
||||||
chrono::{DateTime, Datelike, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc},
|
chrono::{DateTime, Datelike, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc},
|
||||||
ics::escape_text,
|
ics::escape_text,
|
||||||
|
@ -92,9 +93,9 @@ impl<'a> From<Event> for CalEvent<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dt_fmt(datetime: &DateTime<Local>) -> String {
|
fn dt_fmt(datetime: &DateTime<Local>) -> String {
|
||||||
datetime.format("%Y%m%dT%H%M%S").to_string()
|
datetime.format(constants::ICAL_LOCALTIME_FMT).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dt_utc_fmt(datetime: &DateTime<Utc>) -> String {
|
fn dt_utc_fmt(datetime: &DateTime<Utc>) -> String {
|
||||||
datetime.format("%Y%m%dT%H%M%SZ").to_string()
|
datetime.format(constants::ICAL_UTCTIME_FMT).to_string()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue