mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-29 02:29:02 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c29400a799 | ||
|
|
047552a726 | ||
|
|
1cfc8e7c23 | ||
|
|
b0fdca1b64 | ||
|
|
b65cca9d17 | ||
|
|
55ecbdcd41 | ||
|
|
89d3d3b7a4 |
126
Cargo.lock
generated
126
Cargo.lock
generated
@@ -275,9 +275,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "3.4.1"
|
||||
version = "3.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc"
|
||||
checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
|
||||
dependencies = [
|
||||
"event-listener 5.4.1",
|
||||
"event-listener-strategy",
|
||||
@@ -360,9 +360,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.8.7"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425"
|
||||
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"bytes",
|
||||
@@ -393,9 +393,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.5.5"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22"
|
||||
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -412,9 +412,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "axum-extra"
|
||||
version = "0.12.2"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbfe9f610fe4e99cf0cfcd03ccf8c63c28c616fe714d80475ef731f3b13dd21b"
|
||||
checksum = "fef252edff26ddba56bbcdf2ee3307b8129acb86f5749b68990c168a6fcc9c76"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-core",
|
||||
@@ -563,9 +563,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.50"
|
||||
version = "1.2.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c"
|
||||
checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
@@ -983,18 +983,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.1.0"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618"
|
||||
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.1.0"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b"
|
||||
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
@@ -1231,9 +1231,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||
checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
@@ -1761,13 +1761,15 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ical"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/lennart-k/ical-rs#d384dd45495722d69c7f76d62a54a8d6481e90ee"
|
||||
source = "git+https://github.com/lennart-k/ical-rs#5cce57a90a60a28845b1da5df34643663ec63da1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"derive_more",
|
||||
"itertools 0.14.0",
|
||||
"lazy_static",
|
||||
"regex",
|
||||
"rrule",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
@@ -1972,9 +1974,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
@@ -2018,13 +2020,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.11"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50"
|
||||
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"redox_syscall 0.6.0",
|
||||
"redox_syscall 0.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2092,9 +2094,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea5f97102eb9e54ab99fb70bb175589073f554bdadfb74d9bd656482ea73e2a"
|
||||
checksum = "b3eede3bdf92f3b4f9dc04072a9ce5ab557d5ec9038773bf9ffcd5588b3cc05b"
|
||||
|
||||
[[package]]
|
||||
name = "matchit-serde"
|
||||
@@ -2102,7 +2104,7 @@ version = "0.1.0"
|
||||
source = "git+https://github.com/lennart-k/matchit-serde?rev=e18e65d7#e18e65d75cb20ab5f6a193c84a87ee2db8e6ae0b"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"matchit 0.9.0",
|
||||
"matchit 0.9.1",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"thiserror 2.0.17",
|
||||
@@ -2769,9 +2771,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -2981,9 +2983,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5"
|
||||
checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -3045,9 +3047,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.26"
|
||||
version = "0.12.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f"
|
||||
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
@@ -3261,7 +3263,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -3306,7 +3308,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_caldav"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
@@ -3348,7 +3350,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_carddav"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -3381,7 +3383,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_dav"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -3390,9 +3392,10 @@ dependencies = [
|
||||
"futures-util",
|
||||
"headers",
|
||||
"http",
|
||||
"ical",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
"matchit 0.9.0",
|
||||
"matchit 0.9.1",
|
||||
"matchit-serde",
|
||||
"quick-xml",
|
||||
"rustical_xml",
|
||||
@@ -3406,7 +3409,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_dav_push"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -3431,7 +3434,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_frontend"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"askama_web",
|
||||
@@ -3467,7 +3470,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_ical"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"chrono",
|
||||
@@ -3484,7 +3487,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_oidc"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -3500,7 +3503,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_store"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3533,7 +3536,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_store_sqlite"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"chrono",
|
||||
@@ -3556,7 +3559,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_xml"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"quick-xml",
|
||||
"thiserror 2.0.17",
|
||||
@@ -3565,9 +3568,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -3619,9 +3622,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
@@ -3646,9 +3649,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289"
|
||||
checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"ref-cast",
|
||||
@@ -3724,15 +3727,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
version = "1.0.148"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3797,7 +3800,7 @@ dependencies = [
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.12.1",
|
||||
"schemars 0.9.0",
|
||||
"schemars 1.1.0",
|
||||
"schemars 1.2.0",
|
||||
"serde_core",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
@@ -3855,10 +3858,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.7"
|
||||
version = "1.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
|
||||
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -4229,9 +4233,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.23.0"
|
||||
version = "3.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.4",
|
||||
@@ -5378,7 +5382,7 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
||||
|
||||
[[package]]
|
||||
name = "xml_derive"
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
dependencies = [
|
||||
"darling 0.23.0",
|
||||
"heck",
|
||||
@@ -5496,3 +5500,9 @@ dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5858cd3a46fff31e77adea2935e357e3a2538d870741617bfb7c943e218fee6"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.11.5"
|
||||
version = "0.11.7"
|
||||
rust-version = "1.91"
|
||||
edition = "2024"
|
||||
description = "A CalDAV server"
|
||||
@@ -36,7 +36,7 @@ opentelemetry = [
|
||||
debug = 0
|
||||
|
||||
[workspace.dependencies]
|
||||
rustical_dav = { path = "./crates/dav/" }
|
||||
rustical_dav = { path = "./crates/dav/", features = ["ical"] }
|
||||
rustical_dav_push = { path = "./crates/dav_push/" }
|
||||
rustical_store = { path = "./crates/store/" }
|
||||
rustical_store_sqlite = { path = "./crates/store_sqlite/" }
|
||||
|
||||
@@ -29,7 +29,7 @@ base64.workspace = true
|
||||
serde.workspace = true
|
||||
tokio.workspace = true
|
||||
url.workspace = true
|
||||
rustical_dav.workspace = true
|
||||
rustical_dav = { workspace = true, features = ["ical"] }
|
||||
rustical_store.workspace = true
|
||||
chrono.workspace = true
|
||||
chrono-tz.workspace = true
|
||||
|
||||
@@ -137,13 +137,11 @@ impl CompFilterable for CalendarObjectComponent {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chrono::{TimeZone, Utc};
|
||||
use rustical_dav::xml::{NegateCondition, TextCollation, TextMatchElement};
|
||||
use rustical_ical::{CalendarObject, UtcDateTime};
|
||||
|
||||
use crate::calendar::methods::report::calendar_query::{
|
||||
CompFilterable, TextMatchElement, TimeRangeElement,
|
||||
comp_filter::CompFilterElement,
|
||||
prop_filter::PropFilterElement,
|
||||
text_match::{NegateCondition, TextCollation},
|
||||
CompFilterElement, CompFilterable, PropFilterElement, TimeRangeElement,
|
||||
};
|
||||
|
||||
const ICS: &str = r"BEGIN:VCALENDAR
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
use crate::{
|
||||
calendar::methods::report::calendar_query::{
|
||||
TextMatchElement,
|
||||
comp_filter::{CompFilterElement, CompFilterable},
|
||||
},
|
||||
calendar_object::CalendarObjectPropWrapperName,
|
||||
};
|
||||
use rustical_dav::xml::PropfindType;
|
||||
use super::comp_filter::{CompFilterElement, CompFilterable};
|
||||
use crate::calendar_object::CalendarObjectPropWrapperName;
|
||||
use rustical_dav::xml::{PropfindType, TextMatchElement};
|
||||
use rustical_ical::{CalendarObject, UtcDateTime};
|
||||
use rustical_store::calendar_store::CalendarQuery;
|
||||
use rustical_xml::XmlDeserialize;
|
||||
|
||||
@@ -5,14 +5,11 @@ use rustical_store::CalendarStore;
|
||||
mod comp_filter;
|
||||
mod elements;
|
||||
mod prop_filter;
|
||||
pub mod text_match;
|
||||
#[allow(unused_imports)]
|
||||
pub use comp_filter::{CompFilterElement, CompFilterable};
|
||||
pub use elements::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use prop_filter::{PropFilterElement, PropFilterable};
|
||||
#[allow(unused_imports)]
|
||||
pub use text_match::TextMatchElement;
|
||||
|
||||
pub async fn get_objects_calendar_query<C: CalendarStore>(
|
||||
cal_query: &CalendarQueryRequest,
|
||||
@@ -31,21 +28,16 @@ pub async fn get_objects_calendar_query<C: CalendarStore>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rustical_dav::xml::PropElement;
|
||||
use rustical_xml::XmlDocument;
|
||||
|
||||
use super::{
|
||||
CalendarQueryRequest, FilterElement, ParamFilterElement, comp_filter::CompFilterElement,
|
||||
prop_filter::PropFilterElement,
|
||||
};
|
||||
use crate::{
|
||||
calendar::methods::report::{
|
||||
ReportRequest,
|
||||
calendar_query::{
|
||||
CalendarQueryRequest, FilterElement, ParamFilterElement, TextMatchElement,
|
||||
comp_filter::CompFilterElement,
|
||||
prop_filter::PropFilterElement,
|
||||
text_match::{NegateCondition, TextCollation},
|
||||
},
|
||||
},
|
||||
calendar::methods::report::ReportRequest,
|
||||
calendar_object::{CalendarData, CalendarObjectPropName, CalendarObjectPropWrapperName},
|
||||
};
|
||||
use rustical_dav::xml::{NegateCondition, PropElement, TextCollation, TextMatchElement};
|
||||
use rustical_xml::XmlDocument;
|
||||
|
||||
#[test]
|
||||
fn calendar_query_7_8_7() {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{ParamFilterElement, TimeRangeElement};
|
||||
use ical::{
|
||||
generator::{IcalCalendar, IcalEvent},
|
||||
parser::{
|
||||
@@ -8,12 +7,10 @@ use ical::{
|
||||
},
|
||||
property::Property,
|
||||
};
|
||||
use rustical_dav::xml::TextMatchElement;
|
||||
use rustical_ical::{CalDateTime, CalendarObject, CalendarObjectComponent, UtcDateTime};
|
||||
use rustical_xml::XmlDeserialize;
|
||||
|
||||
use crate::calendar::methods::report::calendar_query::{
|
||||
ParamFilterElement, TextMatchElement, TimeRangeElement,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use derive_more::derive::{From, Into};
|
||||
use rustical_dav::xml::TextCollation;
|
||||
use rustical_ical::CalendarObjectType;
|
||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||
use strum_macros::VariantArray;
|
||||
|
||||
use crate::calendar::methods::report::calendar_query::text_match::TextCollation;
|
||||
|
||||
#[derive(Debug, Clone, XmlSerialize, XmlDeserialize, PartialEq, Eq, From, Into)]
|
||||
pub struct SupportedCalendarComponent {
|
||||
#[xml(ty = "attr")]
|
||||
|
||||
@@ -22,7 +22,7 @@ base64.workspace = true
|
||||
serde.workspace = true
|
||||
tokio.workspace = true
|
||||
url.workspace = true
|
||||
rustical_dav.workspace = true
|
||||
rustical_dav = { workspace = true, features = ["ical"] }
|
||||
rustical_store.workspace = true
|
||||
chrono.workspace = true
|
||||
rustical_xml.workspace = true
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
use crate::{
|
||||
address_object::AddressObjectPropWrapperName,
|
||||
addressbook::methods::report::addressbook_query::PropFilterElement,
|
||||
};
|
||||
use rustical_dav::xml::{PropfindType, TextMatchElement};
|
||||
use rustical_ical::{AddressObject, UtcDateTime};
|
||||
use rustical_xml::XmlDeserialize;
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
pub struct TimeRangeElement {
|
||||
#[xml(ty = "attr")]
|
||||
pub(crate) start: Option<UtcDateTime>,
|
||||
#[xml(ty = "attr")]
|
||||
pub(crate) end: Option<UtcDateTime>,
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
// https://www.rfc-editor.org/rfc/rfc4791#section-9.7.3
|
||||
pub struct ParamFilterElement {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
pub(crate) is_not_defined: Option<()>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
pub(crate) text_match: Option<TextMatchElement>,
|
||||
|
||||
#[xml(ty = "attr")]
|
||||
pub(crate) name: String,
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
// <!ELEMENT filter (prop-filter*)>
|
||||
// <!ATTLIST filter test (anyof | allof) "anyof">
|
||||
// <!-- test value:
|
||||
// anyof logical OR for prop-filter matches
|
||||
// allof logical AND for prop-filter matches -->
|
||||
pub struct FilterElement {
|
||||
#[xml(ty = "attr")]
|
||||
pub anyof: Option<String>,
|
||||
#[xml(ty = "attr")]
|
||||
pub allof: Option<String>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", flatten)]
|
||||
pub(crate) prop_filter: Vec<PropFilterElement>,
|
||||
}
|
||||
|
||||
impl FilterElement {
|
||||
#[must_use]
|
||||
pub fn matches(&self, addr_object: &AddressObject) -> bool {
|
||||
let allof = match (self.allof.is_some(), self.anyof.is_some()) {
|
||||
(true, false) => true,
|
||||
(false, _) => false,
|
||||
(true, true) => panic!("wat"),
|
||||
};
|
||||
let mut results = self
|
||||
.prop_filter
|
||||
.iter()
|
||||
.map(|prop_filter| prop_filter.match_component(addr_object));
|
||||
if allof {
|
||||
results.all(|x| x)
|
||||
} else {
|
||||
results.any(|x| x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
// <!ELEMENT addressbook-query ((DAV:allprop |
|
||||
// DAV:propname |
|
||||
// DAV:prop)?, filter, limit?)>
|
||||
pub struct AddressbookQueryRequest {
|
||||
#[xml(ty = "untagged")]
|
||||
pub prop: PropfindType<AddressObjectPropWrapperName>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
pub(crate) filter: FilterElement,
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
use crate::Error;
|
||||
mod elements;
|
||||
mod prop_filter;
|
||||
pub use elements::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use prop_filter::{PropFilterElement, PropFilterable};
|
||||
use rustical_ical::AddressObject;
|
||||
use rustical_store::AddressbookStore;
|
||||
|
||||
pub async fn get_objects_addressbook_query<AS: AddressbookStore>(
|
||||
addr_query: &AddressbookQueryRequest,
|
||||
principal: &str,
|
||||
addressbook_id: &str,
|
||||
store: &AS,
|
||||
) -> Result<Vec<AddressObject>, Error> {
|
||||
let mut objects = store.get_objects(principal, addressbook_id).await?;
|
||||
objects.retain(|object| addr_query.filter.matches(object));
|
||||
Ok(objects)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
use super::ParamFilterElement;
|
||||
use ical::{parser::Component, property::Property};
|
||||
use rustical_dav::xml::TextMatchElement;
|
||||
use rustical_ical::AddressObject;
|
||||
use rustical_xml::XmlDeserialize;
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
// <!ELEMENT prop-filter (is-not-defined |
|
||||
// (text-match*, param-filter*))>
|
||||
//
|
||||
// <!ATTLIST prop-filter name CDATA #REQUIRED
|
||||
// test (anyof | allof) "anyof">
|
||||
// <!-- name value: a vCard property name (e.g., "NICKNAME")
|
||||
// test value:
|
||||
// anyof logical OR for text-match/param-filter matches
|
||||
// allof logical AND for text-match/param-filter matches -->
|
||||
pub struct PropFilterElement {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
pub(crate) is_not_defined: Option<()>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", flatten)]
|
||||
pub(crate) text_match: Vec<TextMatchElement>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", flatten)]
|
||||
pub(crate) param_filter: Vec<ParamFilterElement>,
|
||||
|
||||
#[xml(ty = "attr")]
|
||||
pub(crate) name: String,
|
||||
|
||||
#[xml(ty = "attr")]
|
||||
pub anyof: Option<String>,
|
||||
#[xml(ty = "attr")]
|
||||
pub allof: Option<String>,
|
||||
}
|
||||
|
||||
impl PropFilterElement {
|
||||
pub fn match_component(&self, comp: &impl PropFilterable) -> bool {
|
||||
let property = comp.get_property(&self.name);
|
||||
let _property = match (self.is_not_defined.is_some(), property) {
|
||||
// We are the component that's not supposed to be defined
|
||||
(true, Some(_))
|
||||
// We don't match
|
||||
| (false, None) => return false,
|
||||
// We shall not be and indeed we aren't
|
||||
(true, None) => return true,
|
||||
(false, Some(property)) => property
|
||||
};
|
||||
|
||||
let _allof = match (self.allof.is_some(), self.anyof.is_some()) {
|
||||
(true, false) => true,
|
||||
(false, _) => false,
|
||||
(true, true) => panic!("wat"),
|
||||
};
|
||||
|
||||
// TODO: IMPLEMENT
|
||||
// if let Some(text_match) = &self.text_match
|
||||
// && !text_match.match_property(property)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// TODO: param-filter
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PropFilterable {
|
||||
fn get_property(&self, name: &str) -> Option<&Property>;
|
||||
}
|
||||
|
||||
impl PropFilterable for AddressObject {
|
||||
fn get_property(&self, name: &str) -> Option<&Property> {
|
||||
self.get_vcard().get_property(name)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,14 @@
|
||||
use crate::{
|
||||
CardDavPrincipalUri, Error, address_object::AddressObjectPropWrapperName,
|
||||
addressbook::AddressbookResourceService,
|
||||
CardDavPrincipalUri, Error,
|
||||
address_object::{
|
||||
AddressObjectPropWrapper, AddressObjectPropWrapperName, resource::AddressObjectResource,
|
||||
},
|
||||
addressbook::{
|
||||
AddressbookResourceService,
|
||||
methods::report::addressbook_query::{
|
||||
AddressbookQueryRequest, get_objects_addressbook_query,
|
||||
},
|
||||
},
|
||||
};
|
||||
use addressbook_multiget::{AddressbookMultigetRequest, handle_addressbook_multiget};
|
||||
use axum::{
|
||||
@@ -8,19 +16,30 @@ use axum::{
|
||||
extract::{OriginalUri, Path, State},
|
||||
response::IntoResponse,
|
||||
};
|
||||
use rustical_dav::xml::{PropfindType, sync_collection::SyncCollectionRequest};
|
||||
use http::StatusCode;
|
||||
use rustical_dav::{
|
||||
resource::{PrincipalUri, Resource},
|
||||
xml::{
|
||||
MultistatusElement, PropfindType, multistatus::ResponseElement,
|
||||
sync_collection::SyncCollectionRequest,
|
||||
},
|
||||
};
|
||||
use rustical_ical::AddressObject;
|
||||
use rustical_store::{AddressbookStore, SubscriptionStore, auth::Principal};
|
||||
use rustical_xml::{XmlDeserialize, XmlDocument};
|
||||
use sync_collection::handle_sync_collection;
|
||||
use tracing::instrument;
|
||||
|
||||
mod addressbook_multiget;
|
||||
mod addressbook_query;
|
||||
mod sync_collection;
|
||||
|
||||
#[derive(XmlDeserialize, XmlDocument, Clone, Debug, PartialEq)]
|
||||
pub(crate) enum ReportRequest {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
AddressbookMultiget(AddressbookMultigetRequest),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
AddressbookQuery(AddressbookQueryRequest),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
SyncCollection(SyncCollectionRequest<AddressObjectPropWrapperName>),
|
||||
}
|
||||
@@ -29,11 +48,49 @@ impl ReportRequest {
|
||||
const fn props(&self) -> &PropfindType<AddressObjectPropWrapperName> {
|
||||
match self {
|
||||
Self::AddressbookMultiget(AddressbookMultigetRequest { prop, .. })
|
||||
| Self::SyncCollection(SyncCollectionRequest { prop, .. }) => prop,
|
||||
| Self::SyncCollection(SyncCollectionRequest { prop, .. })
|
||||
| Self::AddressbookQuery(AddressbookQueryRequest { prop, .. }) => prop,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn objects_response(
|
||||
objects: Vec<AddressObject>,
|
||||
not_found: Vec<String>,
|
||||
path: &str,
|
||||
principal: &str,
|
||||
puri: &impl PrincipalUri,
|
||||
user: &Principal,
|
||||
prop: &PropfindType<AddressObjectPropWrapperName>,
|
||||
) -> Result<MultistatusElement<AddressObjectPropWrapper, String>, Error> {
|
||||
let mut responses = Vec::new();
|
||||
for object in objects {
|
||||
let path = format!("{}/{}.vcf", path, object.get_id());
|
||||
responses.push(
|
||||
AddressObjectResource {
|
||||
object,
|
||||
principal: principal.to_owned(),
|
||||
}
|
||||
.propfind(&path, prop, None, puri, user)?,
|
||||
);
|
||||
}
|
||||
|
||||
let not_found_responses = not_found
|
||||
.into_iter()
|
||||
.map(|path| ResponseElement {
|
||||
href: path,
|
||||
status: Some(StatusCode::NOT_FOUND),
|
||||
..Default::default()
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(MultistatusElement {
|
||||
responses,
|
||||
member_responses: not_found_responses,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(skip(addr_store))]
|
||||
pub async fn route_report_addressbook<AS: AddressbookStore, S: SubscriptionStore>(
|
||||
Path((principal, addressbook_id)): Path<(String, String)>,
|
||||
@@ -75,13 +132,34 @@ pub async fn route_report_addressbook<AS: AddressbookStore, S: SubscriptionStore
|
||||
)
|
||||
.await?
|
||||
}
|
||||
ReportRequest::AddressbookQuery(addr_query) => {
|
||||
let objects = get_objects_addressbook_query(
|
||||
addr_query,
|
||||
&principal,
|
||||
&addressbook_id,
|
||||
addr_store.as_ref(),
|
||||
)
|
||||
.await?;
|
||||
objects_response(
|
||||
objects,
|
||||
vec![],
|
||||
uri.path(),
|
||||
&principal,
|
||||
&puri,
|
||||
&user,
|
||||
&addr_query.prop,
|
||||
)?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::address_object::AddressObjectPropName;
|
||||
use crate::{
|
||||
address_object::AddressObjectPropName,
|
||||
addressbook::methods::report::addressbook_query::{FilterElement, PropFilterElement},
|
||||
};
|
||||
use rustical_dav::xml::{PropElement, sync_collection::SyncLevel};
|
||||
|
||||
#[test]
|
||||
@@ -144,4 +222,46 @@ mod tests {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xml_addressbook_query() {
|
||||
let report_request = ReportRequest::parse_str(
|
||||
r#"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<card:addressbook-query xmlns:card="urn:ietf:params:xml:ns:carddav" xmlns:d="DAV:">
|
||||
<d:prop>
|
||||
<d:getetag/>
|
||||
</d:prop>
|
||||
<card:filter>
|
||||
<card:prop-filter name="FN"/>
|
||||
</card:filter>
|
||||
</card:addressbook-query>
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
report_request,
|
||||
ReportRequest::AddressbookQuery(AddressbookQueryRequest {
|
||||
prop: rustical_dav::xml::PropfindType::Prop(PropElement(
|
||||
vec![AddressObjectPropWrapperName::AddressObject(
|
||||
AddressObjectPropName::Getetag
|
||||
),],
|
||||
vec![]
|
||||
)),
|
||||
filter: FilterElement {
|
||||
anyof: None,
|
||||
allof: None,
|
||||
prop_filter: vec![PropFilterElement {
|
||||
name: "FN".to_owned(),
|
||||
is_not_defined: None,
|
||||
text_match: vec![],
|
||||
param_filter: vec![],
|
||||
allof: None,
|
||||
anyof: None
|
||||
}]
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,3 +28,7 @@ headers.workspace = true
|
||||
strum.workspace = true
|
||||
matchit.workspace = true
|
||||
matchit-serde.workspace = true
|
||||
ical = { workspace = true, optional = true }
|
||||
|
||||
[features]
|
||||
ical = ["dep:ical"]
|
||||
|
||||
@@ -15,3 +15,7 @@ mod report_set;
|
||||
pub use report_set::SupportedReportSet;
|
||||
mod group;
|
||||
pub use group::*;
|
||||
#[cfg(feature = "ical")]
|
||||
mod text_match;
|
||||
#[cfg(feature = "ical")]
|
||||
pub use text_match::*;
|
||||
|
||||
@@ -64,9 +64,9 @@ pub struct TextMatchElement {
|
||||
#[xml(ty = "attr", default = "Default::default")]
|
||||
pub collation: TextCollation,
|
||||
#[xml(ty = "attr", default = "Default::default")]
|
||||
pub(crate) negate_condition: NegateCondition,
|
||||
pub negate_condition: NegateCondition,
|
||||
#[xml(ty = "text")]
|
||||
pub(crate) needle: String,
|
||||
pub needle: String,
|
||||
}
|
||||
|
||||
impl TextMatchElement {
|
||||
@@ -90,7 +90,7 @@ impl TextMatchElement {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::calendar::methods::report::calendar_query::text_match::TextCollation;
|
||||
use super::TextCollation;
|
||||
|
||||
#[test]
|
||||
fn test_collation() {
|
||||
@@ -11,7 +11,7 @@
|
||||
]
|
||||
},
|
||||
"imports": {
|
||||
"@deno/vite-plugin": "npm:@deno/vite-plugin@^1.0.5",
|
||||
"@deno/vite-plugin": "npm:@deno/vite-plugin@^1.0.6",
|
||||
"lit": "npm:lit@^3.3.1",
|
||||
"vite": "npm:vite@^7.3.0"
|
||||
}
|
||||
|
||||
8
crates/frontend/js-components/deno.lock
generated
8
crates/frontend/js-components/deno.lock
generated
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"version": "5",
|
||||
"specifiers": {
|
||||
"npm:@deno/vite-plugin@^1.0.5": "1.0.5_vite@7.3.0__picomatch@4.0.3",
|
||||
"npm:@deno/vite-plugin@^1.0.6": "1.0.6_vite@7.3.0__picomatch@4.0.3",
|
||||
"npm:lit@^3.3.1": "3.3.1",
|
||||
"npm:vite@*": "7.3.0_picomatch@4.0.3",
|
||||
"npm:vite@^7.3.0": "7.3.0_picomatch@4.0.3"
|
||||
},
|
||||
"npm": {
|
||||
"@deno/vite-plugin@1.0.5_vite@7.3.0__picomatch@4.0.3": {
|
||||
"integrity": "sha512-tLja5n4dyMhcze1NzvSs2iiriBymfBlDCZIrjMTxb9O2ru0gvmV6mn5oBD2teNw5Sd92cj3YJzKwsAs8tMJXlg==",
|
||||
"@deno/vite-plugin@1.0.6_vite@7.3.0__picomatch@4.0.3": {
|
||||
"integrity": "sha512-Sh5XqvFuKAwjARTesi0n6xRpEXm1V0UeqKh+SxIrexCofxOaieNDMqXZD02RiZCg0mrJ43V8eCMuVrDfq6mLmg==",
|
||||
"dependencies": [
|
||||
"vite"
|
||||
]
|
||||
@@ -415,7 +415,7 @@
|
||||
},
|
||||
"workspace": {
|
||||
"dependencies": [
|
||||
"npm:@deno/vite-plugin@^1.0.5",
|
||||
"npm:@deno/vite-plugin@^1.0.6",
|
||||
"npm:lit@^3.3.1",
|
||||
"npm:vite@^7.3.0"
|
||||
]
|
||||
|
||||
@@ -179,4 +179,9 @@ END:VCALENDAR",
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_vcard(&self) -> &VcardContact {
|
||||
&self.vcard
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user