mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 19:22:26 +00:00
WIP: Complete work of propfind parsing
This commit is contained in:
346
Cargo.lock
generated
346
Cargo.lock
generated
@@ -353,12 +353,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-wincon"
|
name = "anstyle-wincon"
|
||||||
version = "3.0.7"
|
version = "3.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
|
checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"once_cell",
|
"once_cell_polyfill",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -427,9 +427,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "askama_web"
|
name = "askama_web"
|
||||||
version = "0.14.2"
|
version = "0.14.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06f7c6ff1e5cb12c48c877eee90b58b5473705632ca74e66419d0d91e38fe402"
|
checksum = "1a91fdeb04bf77d96234780cdd58fc221eb10de7031e1782a22f40fc8ac1a313"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"askama",
|
"askama",
|
||||||
@@ -530,9 +530,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64ct"
|
name = "base64ct"
|
||||||
version = "1.7.3"
|
version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3"
|
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "basic-toml"
|
name = "basic-toml"
|
||||||
@@ -626,9 +626,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.23"
|
version = "1.2.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
|
checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -704,9 +704,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.38"
|
version = "4.5.39"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -714,9 +714,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.38"
|
version = "4.5.39"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -1110,16 +1110,6 @@ 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 = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "errno"
|
|
||||||
version = "0.3.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "etcetera"
|
name = "etcetera"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -1142,12 +1132,6 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fastrand"
|
|
||||||
version = "2.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ff"
|
name = "ff"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
@@ -1590,11 +1574,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-rustls"
|
name = "hyper-rustls"
|
||||||
version = "0.27.5"
|
version = "0.27.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
|
checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
@@ -1603,7 +1586,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"webpki-roots 0.26.11",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1621,17 +1604,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-util"
|
name = "hyper-util"
|
||||||
version = "0.1.11"
|
version = "0.1.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
|
checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"http-body",
|
"http-body",
|
||||||
"hyper",
|
"hyper",
|
||||||
|
"ipnet",
|
||||||
"libc",
|
"libc",
|
||||||
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -1722,9 +1709,9 @@ checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_properties"
|
name = "icu_properties"
|
||||||
version = "2.0.0"
|
version = "2.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a"
|
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"icu_collections",
|
"icu_collections",
|
||||||
@@ -1738,9 +1725,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_properties_data"
|
name = "icu_properties_data"
|
||||||
version = "2.0.0"
|
version = "2.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04"
|
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_provider"
|
name = "icu_provider"
|
||||||
@@ -1835,6 +1822,16 @@ version = "2.11.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iri-string"
|
||||||
|
version = "0.7.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_terminal_polyfill"
|
name = "is_terminal_polyfill"
|
||||||
version = "1.70.1"
|
version = "1.70.1"
|
||||||
@@ -1923,12 +1920,6 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linux-raw-sys"
|
|
||||||
version = "0.9.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -1954,9 +1945,9 @@ checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.12"
|
version = "0.4.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
@@ -2026,14 +2017,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.0.3"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2140,6 +2131,12 @@ version = "1.21.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-debug"
|
name = "opaque-debug"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -2310,9 +2307,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.3"
|
version = "0.12.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
|
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"parking_lot_core",
|
"parking_lot_core",
|
||||||
@@ -2320,9 +2317,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.9.10"
|
version = "0.9.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -2815,9 +2812,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.15"
|
version = "0.12.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
|
checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -2841,7 +2838,6 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"quinn",
|
"quinn",
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pemfile",
|
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -2850,13 +2846,13 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tower 0.5.2",
|
"tower 0.5.2",
|
||||||
|
"tower-http",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"webpki-roots 0.26.11",
|
"webpki-roots",
|
||||||
"windows-registry",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3210,8 +3206,6 @@ dependencies = [
|
|||||||
"rustical_xml",
|
"rustical_xml",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"strum",
|
|
||||||
"strum_macros",
|
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3254,8 +3248,6 @@ dependencies = [
|
|||||||
"rustical_xml",
|
"rustical_xml",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"strum",
|
|
||||||
"strum_macros",
|
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -3292,19 +3284,6 @@ dependencies = [
|
|||||||
"xml_derive",
|
"xml_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustix"
|
|
||||||
version = "1.0.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"errno",
|
|
||||||
"libc",
|
|
||||||
"linux-raw-sys",
|
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.27"
|
version = "0.23.27"
|
||||||
@@ -3319,15 +3298,6 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls-pemfile"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
|
|
||||||
dependencies = [
|
|
||||||
"rustls-pki-types",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
@@ -3351,9 +3321,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.20"
|
version = "1.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
@@ -3590,9 +3560,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.5.9"
|
version = "0.5.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
|
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
@@ -3619,9 +3589,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx"
|
name = "sqlx"
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3c3a85280daca669cfd3bcb68a337882a8bc57ec882f72c5d13a430613a738e"
|
checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-macros",
|
"sqlx-macros",
|
||||||
@@ -3632,9 +3602,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-core"
|
name = "sqlx-core"
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f743f2a3cea30a58cd479013f75550e879009e3a02f616f18ca699335aa248c3"
|
checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -3668,9 +3638,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-macros"
|
name = "sqlx-macros"
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f4200e0fde19834956d4252347c12a083bdcb237d7a1a1446bffd8768417dce"
|
checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -3681,9 +3651,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-macros-core"
|
name = "sqlx-macros-core"
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "882ceaa29cade31beca7129b6beeb05737f44f82dbe2a9806ecea5a7093d00b7"
|
checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"either",
|
"either",
|
||||||
@@ -3700,16 +3670,15 @@ dependencies = [
|
|||||||
"sqlx-postgres",
|
"sqlx-postgres",
|
||||||
"sqlx-sqlite",
|
"sqlx-sqlite",
|
||||||
"syn",
|
"syn",
|
||||||
"tempfile",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-mysql"
|
name = "sqlx-mysql"
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0afdd3aa7a629683c2d750c2df343025545087081ab5942593a5288855b1b7a7"
|
checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
@@ -3751,9 +3720,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-postgres"
|
name = "sqlx-postgres"
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0bedbe1bbb5e2615ef347a5e9d8cd7680fb63e77d9dafc0f29be15e53f1ebe6"
|
checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
@@ -3790,9 +3759,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-sqlite"
|
name = "sqlx-sqlite"
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c26083e9a520e8eb87a06b12347679b142dc2ea29e6e409f805644a7a979a5bc"
|
checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -3837,25 +3806,6 @@ version = "0.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strum"
|
|
||||||
version = "0.27.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strum_macros"
|
|
||||||
version = "0.27.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
|
|
||||||
dependencies = [
|
|
||||||
"heck",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"rustversion",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
@@ -3893,19 +3843,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tempfile"
|
|
||||||
version = "3.20.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
|
||||||
dependencies = [
|
|
||||||
"fastrand",
|
|
||||||
"getrandom 0.3.3",
|
|
||||||
"once_cell",
|
|
||||||
"rustix",
|
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
@@ -4014,9 +3951,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.45.0"
|
version = "1.45.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -4178,6 +4115,24 @@ dependencies = [
|
|||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-http"
|
||||||
|
version = "0.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http 1.3.1",
|
||||||
|
"http-body",
|
||||||
|
"iri-string",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tower 0.5.2",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-layer"
|
name = "tower-layer"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@@ -4385,12 +4340,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.16.0"
|
version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
|
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
|
"js-sys",
|
||||||
"rand 0.9.1",
|
"rand 0.9.1",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4542,15 +4499,6 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webpki-roots"
|
|
||||||
version = "0.26.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
|
||||||
dependencies = [
|
|
||||||
"webpki-roots 1.0.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -4603,15 +4551,15 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.61.1"
|
version = "0.61.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46ec44dc15085cea82cf9c78f85a9114c463a369786585ad2882d1ff0b0acf40"
|
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-implement",
|
"windows-implement",
|
||||||
"windows-interface",
|
"windows-interface",
|
||||||
"windows-link",
|
"windows-link",
|
||||||
"windows-result",
|
"windows-result",
|
||||||
"windows-strings 0.4.1",
|
"windows-strings",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4642,40 +4590,20 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-registry"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
|
|
||||||
dependencies = [
|
|
||||||
"windows-result",
|
|
||||||
"windows-strings 0.3.1",
|
|
||||||
"windows-targets 0.53.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.3.3"
|
version = "0.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4b895b5356fc36103d0f64dd1e94dfa7ac5633f1c9dd6e80fe9ec4adef69e09d"
|
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-strings"
|
name = "windows-strings"
|
||||||
version = "0.3.1"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
|
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
||||||
dependencies = [
|
|
||||||
"windows-link",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-strings"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2a7ab927b2637c19b3dbe0965e75d8f2d30bdd697a1516191cad2ec4df8fb28a"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
@@ -4731,29 +4659,13 @@ dependencies = [
|
|||||||
"windows_aarch64_gnullvm 0.52.6",
|
"windows_aarch64_gnullvm 0.52.6",
|
||||||
"windows_aarch64_msvc 0.52.6",
|
"windows_aarch64_msvc 0.52.6",
|
||||||
"windows_i686_gnu 0.52.6",
|
"windows_i686_gnu 0.52.6",
|
||||||
"windows_i686_gnullvm 0.52.6",
|
"windows_i686_gnullvm",
|
||||||
"windows_i686_msvc 0.52.6",
|
"windows_i686_msvc 0.52.6",
|
||||||
"windows_x86_64_gnu 0.52.6",
|
"windows_x86_64_gnu 0.52.6",
|
||||||
"windows_x86_64_gnullvm 0.52.6",
|
"windows_x86_64_gnullvm 0.52.6",
|
||||||
"windows_x86_64_msvc 0.52.6",
|
"windows_x86_64_msvc 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.53.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm 0.53.0",
|
|
||||||
"windows_aarch64_msvc 0.53.0",
|
|
||||||
"windows_i686_gnu 0.53.0",
|
|
||||||
"windows_i686_gnullvm 0.53.0",
|
|
||||||
"windows_i686_msvc 0.53.0",
|
|
||||||
"windows_x86_64_gnu 0.53.0",
|
|
||||||
"windows_x86_64_gnullvm 0.53.0",
|
|
||||||
"windows_x86_64_msvc 0.53.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -4766,12 +4678,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.53.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -4784,12 +4690,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.53.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -4802,24 +4702,12 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.53.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnullvm"
|
name = "windows_i686_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnullvm"
|
|
||||||
version = "0.53.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -4832,12 +4720,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.53.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -4850,12 +4732,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.53.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -4868,12 +4744,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.53.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.48.5"
|
||||||
@@ -4886,12 +4756,6 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.53.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.10"
|
version = "0.7.10"
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ rustical_ical = { path = "./crates/ical/", features = ["actix"] }
|
|||||||
chrono-tz = "0.10"
|
chrono-tz = "0.10"
|
||||||
chrono-humanize = "0.2"
|
chrono-humanize = "0.2"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
rrule = "0.14"
|
||||||
argon2 = "0.5"
|
argon2 = "0.5"
|
||||||
rpassword = "7.3"
|
rpassword = "7.3"
|
||||||
password-hash = { version = "0.5" }
|
password-hash = { version = "0.5" }
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use super::ReportPropName;
|
use crate::{Error, calendar_object::resource::CalendarObjectPropWrapperName};
|
||||||
use crate::Error;
|
|
||||||
use actix_web::dev::{Path, ResourceDef};
|
use actix_web::dev::{Path, ResourceDef};
|
||||||
use rustical_dav::xml::PropfindType;
|
use rustical_dav::xml::PropfindType;
|
||||||
use rustical_ical::CalendarObject;
|
use rustical_ical::CalendarObject;
|
||||||
@@ -11,7 +10,7 @@ use rustical_xml::XmlDeserialize;
|
|||||||
// <!ELEMENT calendar-query ((DAV:allprop | DAV:propname | DAV:prop)?, href+)>
|
// <!ELEMENT calendar-query ((DAV:allprop | DAV:propname | DAV:prop)?, href+)>
|
||||||
pub(crate) struct CalendarMultigetRequest {
|
pub(crate) struct CalendarMultigetRequest {
|
||||||
#[xml(ty = "untagged")]
|
#[xml(ty = "untagged")]
|
||||||
pub(crate) prop: PropfindType<ReportPropName>,
|
pub(crate) prop: PropfindType<CalendarObjectPropWrapperName>,
|
||||||
#[xml(flatten)]
|
#[xml(flatten)]
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
pub(crate) href: Vec<String>,
|
pub(crate) href: Vec<String>,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use super::ReportPropName;
|
use crate::{Error, calendar_object::resource::CalendarObjectPropWrapperName};
|
||||||
use crate::Error;
|
|
||||||
use rustical_dav::xml::PropfindType;
|
use rustical_dav::xml::PropfindType;
|
||||||
use rustical_ical::{CalendarObject, UtcDateTime};
|
use rustical_ical::{CalendarObject, UtcDateTime};
|
||||||
use rustical_store::{CalendarStore, calendar_store::CalendarQuery};
|
use rustical_store::{CalendarStore, calendar_store::CalendarQuery};
|
||||||
@@ -171,7 +170,7 @@ impl From<&FilterElement> for CalendarQuery {
|
|||||||
// <!ELEMENT calendar-query ((DAV:allprop | DAV:propname | DAV:prop)?, filter, timezone?)>
|
// <!ELEMENT calendar-query ((DAV:allprop | DAV:propname | DAV:prop)?, filter, timezone?)>
|
||||||
pub struct CalendarQueryRequest {
|
pub struct CalendarQueryRequest {
|
||||||
#[xml(ty = "untagged")]
|
#[xml(ty = "untagged")]
|
||||||
pub prop: PropfindType<ReportPropName>,
|
pub prop: PropfindType<CalendarObjectPropWrapperName>,
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
pub(crate) filter: Option<FilterElement>,
|
pub(crate) filter: Option<FilterElement>,
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
CalDavPrincipalUri, Error,
|
CalDavPrincipalUri, Error,
|
||||||
calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource},
|
calendar_object::resource::{
|
||||||
|
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, CalendarObjectResource,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
HttpRequest, Responder,
|
HttpRequest, Responder,
|
||||||
@@ -12,11 +14,11 @@ use calendar_query::{CalendarQueryRequest, get_objects_calendar_query};
|
|||||||
use rustical_dav::{
|
use rustical_dav::{
|
||||||
resource::{PrincipalUri, Resource},
|
resource::{PrincipalUri, Resource},
|
||||||
xml::{
|
xml::{
|
||||||
MultistatusElement, PropElement, PropfindType, Propname, multistatus::ResponseElement,
|
MultistatusElement, PropfindType, multistatus::ResponseElement,
|
||||||
sync_collection::SyncCollectionRequest,
|
sync_collection::SyncCollectionRequest,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use rustical_ical::{CalendarObject, UtcDateTime};
|
use rustical_ical::CalendarObject;
|
||||||
use rustical_store::{CalendarStore, auth::User};
|
use rustical_store::{CalendarStore, auth::User};
|
||||||
use rustical_xml::{XmlDeserialize, XmlDocument};
|
use rustical_xml::{XmlDeserialize, XmlDocument};
|
||||||
use sync_collection::handle_sync_collection;
|
use sync_collection::handle_sync_collection;
|
||||||
@@ -26,34 +28,6 @@ mod calendar_multiget;
|
|||||||
mod calendar_query;
|
mod calendar_query;
|
||||||
mod sync_collection;
|
mod sync_collection;
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
|
||||||
pub(crate) struct ExpandElement {
|
|
||||||
#[xml(ty = "attr")]
|
|
||||||
start: UtcDateTime,
|
|
||||||
#[xml(ty = "attr")]
|
|
||||||
end: UtcDateTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
|
||||||
pub struct CalendarData {
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
comp: Option<()>,
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
expand: Option<ExpandElement>,
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
limit_recurrence_set: Option<()>,
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
limit_freebusy_set: Option<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
|
||||||
pub enum ReportPropName {
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
CalendarData(CalendarData),
|
|
||||||
#[xml(other)]
|
|
||||||
Propname(Propname),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlDocument, Clone, Debug, PartialEq)]
|
#[derive(XmlDeserialize, XmlDocument, Clone, Debug, PartialEq)]
|
||||||
pub(crate) enum ReportRequest {
|
pub(crate) enum ReportRequest {
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
@@ -61,31 +35,15 @@ pub(crate) enum ReportRequest {
|
|||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
CalendarQuery(CalendarQueryRequest),
|
CalendarQuery(CalendarQueryRequest),
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
SyncCollection(SyncCollectionRequest<ReportPropName>),
|
SyncCollection(SyncCollectionRequest<CalendarObjectPropWrapperName>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReportRequest {
|
impl ReportRequest {
|
||||||
fn props(&self) -> Vec<&str> {
|
fn props(&self) -> &PropfindType<CalendarObjectPropWrapperName> {
|
||||||
let prop_element = match self {
|
match &self {
|
||||||
ReportRequest::CalendarMultiget(CalendarMultigetRequest { prop, .. }) => prop,
|
ReportRequest::CalendarMultiget(CalendarMultigetRequest { prop, .. }) => prop,
|
||||||
ReportRequest::CalendarQuery(CalendarQueryRequest { prop, .. }) => prop,
|
ReportRequest::CalendarQuery(CalendarQueryRequest { prop, .. }) => prop,
|
||||||
ReportRequest::SyncCollection(SyncCollectionRequest { prop, .. }) => prop,
|
ReportRequest::SyncCollection(SyncCollectionRequest { prop, .. }) => prop,
|
||||||
};
|
|
||||||
|
|
||||||
match prop_element {
|
|
||||||
PropfindType::Allprop => {
|
|
||||||
vec!["allprop"]
|
|
||||||
}
|
|
||||||
PropfindType::Propname => {
|
|
||||||
vec!["propname"]
|
|
||||||
}
|
|
||||||
PropfindType::Prop(PropElement(prop_tags)) => prop_tags
|
|
||||||
.iter()
|
|
||||||
.map(|propname| match propname {
|
|
||||||
ReportPropName::Propname(propname) => propname.name.as_str(),
|
|
||||||
ReportPropName::CalendarData(_) => "calendar-data",
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +55,7 @@ fn objects_response(
|
|||||||
principal: &str,
|
principal: &str,
|
||||||
puri: &impl PrincipalUri,
|
puri: &impl PrincipalUri,
|
||||||
user: &User,
|
user: &User,
|
||||||
props: &[&str],
|
prop: &PropfindType<CalendarObjectPropWrapperName>,
|
||||||
) -> Result<MultistatusElement<CalendarObjectPropWrapper, String>, Error> {
|
) -> Result<MultistatusElement<CalendarObjectPropWrapper, String>, Error> {
|
||||||
let mut responses = Vec::new();
|
let mut responses = Vec::new();
|
||||||
for object in objects {
|
for object in objects {
|
||||||
@@ -107,7 +65,7 @@ fn objects_response(
|
|||||||
object,
|
object,
|
||||||
principal: principal.to_owned(),
|
principal: principal.to_owned(),
|
||||||
}
|
}
|
||||||
.propfind(&path, props, puri, user)?,
|
.propfind_typed(&path, prop, puri, user)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +114,7 @@ pub async fn route_report_calendar<C: CalendarStore>(
|
|||||||
&principal,
|
&principal,
|
||||||
puri.as_ref(),
|
puri.as_ref(),
|
||||||
&user,
|
&user,
|
||||||
&props,
|
props,
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
ReportRequest::CalendarMultiget(cal_multiget) => {
|
ReportRequest::CalendarMultiget(cal_multiget) => {
|
||||||
@@ -175,13 +133,12 @@ pub async fn route_report_calendar<C: CalendarStore>(
|
|||||||
&principal,
|
&principal,
|
||||||
puri.as_ref(),
|
puri.as_ref(),
|
||||||
&user,
|
&user,
|
||||||
&props,
|
props,
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
ReportRequest::SyncCollection(sync_collection) => {
|
ReportRequest::SyncCollection(sync_collection) => {
|
||||||
handle_sync_collection(
|
handle_sync_collection(
|
||||||
sync_collection,
|
sync_collection,
|
||||||
&props,
|
|
||||||
req.path(),
|
req.path(),
|
||||||
puri.as_ref(),
|
puri.as_ref(),
|
||||||
&user,
|
&user,
|
||||||
@@ -197,10 +154,11 @@ pub async fn route_report_calendar<C: CalendarStore>(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::calendar_object::resource::{CalendarData, CalendarObjectPropName, ExpandElement};
|
||||||
use calendar_query::{CompFilterElement, FilterElement, TimeRangeElement};
|
use calendar_query::{CompFilterElement, FilterElement, TimeRangeElement};
|
||||||
use rustical_dav::xml::{PropElement, PropfindType, Propname};
|
use rustical_dav::xml::PropElement;
|
||||||
use rustical_ical::UtcDateTime;
|
use rustical_ical::UtcDateTime;
|
||||||
use rustical_xml::ValueDeserialize;
|
use rustical_xml::{NamespaceOwned, ValueDeserialize};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_xml_calendar_data() {
|
fn test_xml_calendar_data() {
|
||||||
@@ -222,13 +180,14 @@ mod tests {
|
|||||||
report_request,
|
report_request,
|
||||||
ReportRequest::CalendarMultiget(CalendarMultigetRequest {
|
ReportRequest::CalendarMultiget(CalendarMultigetRequest {
|
||||||
prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![
|
prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![
|
||||||
ReportPropName::Propname(Propname{name: "getetag".to_owned(), ns: Some("DAV:".into())}),
|
CalendarObjectPropWrapperName::CalendarObject(CalendarObjectPropName::Getetag),
|
||||||
ReportPropName::Propname(Propname{name: "displayname".to_owned(), ns: Some("DAV:".into())}),
|
CalendarObjectPropWrapperName::CalendarObject(CalendarObjectPropName::CalendarData(
|
||||||
ReportPropName::CalendarData(CalendarData { comp: None, expand: Some(ExpandElement {
|
CalendarData { comp: None, expand: Some(ExpandElement {
|
||||||
start: <UtcDateTime as ValueDeserialize>::deserialize("20250426T220000Z").unwrap(),
|
start: <UtcDateTime as ValueDeserialize>::deserialize("20250426T220000Z").unwrap(),
|
||||||
end: <UtcDateTime as ValueDeserialize>::deserialize("20250503T220000Z").unwrap(),
|
end: <UtcDateTime as ValueDeserialize>::deserialize("20250503T220000Z").unwrap(),
|
||||||
}), limit_recurrence_set: None, limit_freebusy_set: None })
|
}), limit_recurrence_set: None, limit_freebusy_set: None }
|
||||||
])),
|
)),
|
||||||
|
], vec![(Some(NamespaceOwned(Vec::from("DAV:"))), "displayname".to_string())])),
|
||||||
href: vec![
|
href: vec![
|
||||||
"/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned()
|
"/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned()
|
||||||
]
|
]
|
||||||
@@ -258,10 +217,12 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
report_request,
|
report_request,
|
||||||
ReportRequest::CalendarQuery(CalendarQueryRequest {
|
ReportRequest::CalendarQuery(CalendarQueryRequest {
|
||||||
prop: PropfindType::Prop(PropElement(vec![ReportPropName::Propname(Propname {
|
prop: rustical_dav::xml::PropfindType::Prop(PropElement(
|
||||||
name: "getetag".to_owned(),
|
vec![CalendarObjectPropWrapperName::CalendarObject(
|
||||||
ns: Some("DAV:".into())
|
CalendarObjectPropName::Getetag
|
||||||
})])),
|
),],
|
||||||
|
vec![]
|
||||||
|
)),
|
||||||
filter: Some(FilterElement {
|
filter: Some(FilterElement {
|
||||||
comp_filter: CompFilterElement {
|
comp_filter: CompFilterElement {
|
||||||
is_not_defined: None,
|
is_not_defined: None,
|
||||||
@@ -308,9 +269,8 @@ mod tests {
|
|||||||
report_request,
|
report_request,
|
||||||
ReportRequest::CalendarMultiget(CalendarMultigetRequest {
|
ReportRequest::CalendarMultiget(CalendarMultigetRequest {
|
||||||
prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![
|
prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![
|
||||||
ReportPropName::Propname(Propname{name: "getetag".to_owned(), ns: Some("DAV:".into())}),
|
CalendarObjectPropWrapperName::CalendarObject(CalendarObjectPropName::Getetag),
|
||||||
ReportPropName::Propname(Propname{name: "displayname".to_owned(), ns: Some("DAV:".into())})
|
], vec![(Some(NamespaceOwned(Vec::from("DAV:"))), "displayname".to_string())])),
|
||||||
])),
|
|
||||||
href: vec![
|
href: vec![
|
||||||
"/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned()
|
"/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned()
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use super::ReportPropName;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Error,
|
Error,
|
||||||
calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource},
|
calendar_object::resource::{
|
||||||
|
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, CalendarObjectResource,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
use rustical_dav::{
|
use rustical_dav::{
|
||||||
@@ -17,8 +18,7 @@ use rustical_store::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub async fn handle_sync_collection<C: CalendarStore>(
|
pub async fn handle_sync_collection<C: CalendarStore>(
|
||||||
sync_collection: &SyncCollectionRequest<ReportPropName>,
|
sync_collection: &SyncCollectionRequest<CalendarObjectPropWrapperName>,
|
||||||
props: &[&str],
|
|
||||||
path: &str,
|
path: &str,
|
||||||
puri: &impl PrincipalUri,
|
puri: &impl PrincipalUri,
|
||||||
user: &User,
|
user: &User,
|
||||||
@@ -39,7 +39,7 @@ pub async fn handle_sync_collection<C: CalendarStore>(
|
|||||||
object,
|
object,
|
||||||
principal: principal.to_owned(),
|
principal: principal.to_owned(),
|
||||||
}
|
}
|
||||||
.propfind(&path, props, puri, user)?,
|
.propfind_typed(&path, &sync_collection.prop, puri, user)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
|
|||||||
use rustical_ical::CalDateTime;
|
use rustical_ical::CalDateTime;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
|
use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants};
|
use rustical_xml::{EnumVariants, PropName};
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "CalendarPropName")]
|
#[xml(unit_variants_ident = "CalendarPropName")]
|
||||||
pub enum CalendarProp {
|
pub enum CalendarProp {
|
||||||
// WebDAV (RFC 2518)
|
// WebDAV (RFC 2518)
|
||||||
@@ -64,7 +64,7 @@ pub enum CalendarProp {
|
|||||||
MaxDateTime(String),
|
MaxDateTime(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "CalendarPropWrapperName", untagged)]
|
#[xml(unit_variants_ident = "CalendarPropWrapperName", untagged)]
|
||||||
pub enum CalendarPropWrapper {
|
pub enum CalendarPropWrapper {
|
||||||
Calendar(CalendarProp),
|
Calendar(CalendarProp),
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ use rustical_dav::{
|
|||||||
resource::{PrincipalUri, Resource, ResourceService},
|
resource::{PrincipalUri, Resource, ResourceService},
|
||||||
xml::Resourcetype,
|
xml::Resourcetype,
|
||||||
};
|
};
|
||||||
use rustical_ical::CalendarObject;
|
use rustical_ical::{CalendarObject, UtcDateTime};
|
||||||
use rustical_store::{CalendarStore, auth::User};
|
use rustical_store::{CalendarStore, auth::User};
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -25,7 +25,27 @@ impl<C: CalendarStore> CalendarObjectResourceService<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub(crate) struct ExpandElement {
|
||||||
|
#[xml(ty = "attr")]
|
||||||
|
pub(crate) start: UtcDateTime,
|
||||||
|
#[xml(ty = "attr")]
|
||||||
|
pub(crate) end: UtcDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Default, Eq, Hash)]
|
||||||
|
pub struct CalendarData {
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
pub(crate) comp: Option<()>,
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
pub(crate) expand: Option<ExpandElement>,
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
pub(crate) limit_recurrence_set: Option<()>,
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
pub(crate) limit_freebusy_set: Option<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "CalendarObjectPropName")]
|
#[xml(unit_variants_ident = "CalendarObjectPropName")]
|
||||||
pub enum CalendarObjectProp {
|
pub enum CalendarObjectProp {
|
||||||
// WebDAV (RFC 2518)
|
// WebDAV (RFC 2518)
|
||||||
@@ -36,10 +56,11 @@ pub enum CalendarObjectProp {
|
|||||||
|
|
||||||
// CalDAV (RFC 4791)
|
// CalDAV (RFC 4791)
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
#[xml(prop = "CalendarData")]
|
||||||
CalendarData(String),
|
CalendarData(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "CalendarObjectPropWrapperName", untagged)]
|
#[xml(unit_variants_ident = "CalendarObjectPropWrapperName", untagged)]
|
||||||
pub enum CalendarObjectPropWrapper {
|
pub enum CalendarObjectPropWrapper {
|
||||||
CalendarObject(CalendarObjectProp),
|
CalendarObject(CalendarObjectProp),
|
||||||
@@ -73,8 +94,15 @@ impl Resource for CalendarObjectResource {
|
|||||||
CalendarObjectPropName::Getetag => {
|
CalendarObjectPropName::Getetag => {
|
||||||
CalendarObjectProp::Getetag(self.object.get_etag())
|
CalendarObjectProp::Getetag(self.object.get_etag())
|
||||||
}
|
}
|
||||||
CalendarObjectPropName::CalendarData => {
|
CalendarObjectPropName::CalendarData(CalendarData { expand, .. }) => {
|
||||||
CalendarObjectProp::CalendarData(self.object.get_ics().to_owned())
|
CalendarObjectProp::CalendarData(if let Some(expand) = expand.as_ref() {
|
||||||
|
self.object.expand_recurrence(
|
||||||
|
Some(expand.start.to_utc()),
|
||||||
|
Some(expand.end.to_utc()),
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
self.object.get_ics().to_owned()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
CalendarObjectPropName::Getcontenttype => {
|
CalendarObjectPropName::Getcontenttype => {
|
||||||
CalendarObjectProp::Getcontenttype("text/calendar;charset=utf-8")
|
CalendarObjectProp::Getcontenttype("text/calendar;charset=utf-8")
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use rustical_dav::resource::{PrincipalUri, Resource, ResourceService};
|
|||||||
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -17,7 +17,7 @@ pub struct CalendarSetResource {
|
|||||||
pub(crate) read_only: bool,
|
pub(crate) read_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||||
pub enum PrincipalPropWrapper {
|
pub enum PrincipalPropWrapper {
|
||||||
Common(CommonPropertiesProp),
|
Common(CommonPropertiesProp),
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
|||||||
use rustical_store::auth::user::PrincipalType;
|
use rustical_store::auth::user::PrincipalType;
|
||||||
use rustical_store::auth::{AuthenticationProvider, User};
|
use rustical_store::auth::{AuthenticationProvider, User};
|
||||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -21,7 +21,7 @@ pub struct PrincipalResource {
|
|||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
||||||
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
|
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "PrincipalPropName")]
|
#[xml(unit_variants_ident = "PrincipalPropName")]
|
||||||
pub enum PrincipalProp {
|
pub enum PrincipalProp {
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
@@ -42,7 +42,7 @@ pub enum PrincipalProp {
|
|||||||
CalendarHomeSet(CalendarHomeSet),
|
CalendarHomeSet(CalendarHomeSet),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||||
pub enum PrincipalPropWrapper {
|
pub enum PrincipalPropWrapper {
|
||||||
Principal(PrincipalProp),
|
Principal(PrincipalProp),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use rustical_dav::{
|
|||||||
};
|
};
|
||||||
use rustical_ical::AddressObject;
|
use rustical_ical::AddressObject;
|
||||||
use rustical_store::{AddressbookStore, auth::User};
|
use rustical_store::{AddressbookStore, auth::User};
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ pub struct AddressObjectResourceService<AS: AddressbookStore> {
|
|||||||
addr_store: Arc<AS>,
|
addr_store: Arc<AS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "AddressObjectPropName")]
|
#[xml(unit_variants_ident = "AddressObjectPropName")]
|
||||||
pub enum AddressObjectProp {
|
pub enum AddressObjectProp {
|
||||||
// WebDAV (RFC 2518)
|
// WebDAV (RFC 2518)
|
||||||
@@ -35,7 +35,7 @@ pub enum AddressObjectProp {
|
|||||||
AddressData(String),
|
AddressData(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "AddressObjectPropWrapperName", untagged)]
|
#[xml(unit_variants_ident = "AddressObjectPropWrapperName", untagged)]
|
||||||
pub enum AddressObjectPropWrapper {
|
pub enum AddressObjectPropWrapper {
|
||||||
AddressObject(AddressObjectProp),
|
AddressObject(AddressObjectProp),
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
Error,
|
Error,
|
||||||
address_object::resource::{AddressObjectPropWrapper, AddressObjectResource},
|
address_object::resource::{
|
||||||
|
AddressObjectPropWrapper, AddressObjectPropWrapperName, AddressObjectResource,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
dev::{Path, ResourceDef},
|
dev::{Path, ResourceDef},
|
||||||
@@ -19,7 +21,7 @@ use rustical_xml::XmlDeserialize;
|
|||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
pub struct AddressbookMultigetRequest {
|
pub struct AddressbookMultigetRequest {
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", ty = "untagged")]
|
#[xml(ns = "rustical_dav::namespace::NS_DAV", ty = "untagged")]
|
||||||
pub(crate) prop: PropfindType,
|
pub(crate) prop: PropfindType<AddressObjectPropWrapperName>,
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", flatten)]
|
#[xml(ns = "rustical_dav::namespace::NS_DAV", flatten)]
|
||||||
pub(crate) href: Vec<String>,
|
pub(crate) href: Vec<String>,
|
||||||
}
|
}
|
||||||
@@ -59,7 +61,7 @@ pub async fn get_objects_addressbook_multiget<AS: AddressbookStore>(
|
|||||||
|
|
||||||
pub async fn handle_addressbook_multiget<AS: AddressbookStore>(
|
pub async fn handle_addressbook_multiget<AS: AddressbookStore>(
|
||||||
addr_multiget: &AddressbookMultigetRequest,
|
addr_multiget: &AddressbookMultigetRequest,
|
||||||
props: &[&str],
|
prop: &PropfindType<AddressObjectPropWrapperName>,
|
||||||
path: &str,
|
path: &str,
|
||||||
puri: &impl PrincipalUri,
|
puri: &impl PrincipalUri,
|
||||||
user: &User,
|
user: &User,
|
||||||
@@ -79,7 +81,7 @@ pub async fn handle_addressbook_multiget<AS: AddressbookStore>(
|
|||||||
object,
|
object,
|
||||||
principal: principal.to_owned(),
|
principal: principal.to_owned(),
|
||||||
}
|
}
|
||||||
.propfind(&path, props, puri, user)?,
|
.propfind_typed(&path, prop, puri, user)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use crate::{CardDavPrincipalUri, Error};
|
use crate::{CardDavPrincipalUri, Error, address_object::resource::AddressObjectPropWrapperName};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
HttpRequest, Responder,
|
HttpRequest, Responder,
|
||||||
web::{Data, Path},
|
web::{Data, Path},
|
||||||
};
|
};
|
||||||
use addressbook_multiget::{AddressbookMultigetRequest, handle_addressbook_multiget};
|
use addressbook_multiget::{AddressbookMultigetRequest, handle_addressbook_multiget};
|
||||||
use rustical_dav::xml::{PropElement, PropfindType, sync_collection::SyncCollectionRequest};
|
use rustical_dav::xml::{PropfindType, sync_collection::SyncCollectionRequest};
|
||||||
use rustical_store::{AddressbookStore, auth::User};
|
use rustical_store::{AddressbookStore, auth::User};
|
||||||
use rustical_xml::{XmlDeserialize, XmlDocument};
|
use rustical_xml::{XmlDeserialize, XmlDocument};
|
||||||
use sync_collection::handle_sync_collection;
|
use sync_collection::handle_sync_collection;
|
||||||
@@ -18,27 +18,14 @@ pub(crate) enum ReportRequest {
|
|||||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||||
AddressbookMultiget(AddressbookMultigetRequest),
|
AddressbookMultiget(AddressbookMultigetRequest),
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
SyncCollection(SyncCollectionRequest),
|
SyncCollection(SyncCollectionRequest<AddressObjectPropWrapperName>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReportRequest {
|
impl ReportRequest {
|
||||||
fn props(&self) -> Vec<&str> {
|
fn props(&self) -> &PropfindType<AddressObjectPropWrapperName> {
|
||||||
let prop_element = match self {
|
match self {
|
||||||
ReportRequest::AddressbookMultiget(AddressbookMultigetRequest { prop, .. }) => prop,
|
ReportRequest::AddressbookMultiget(AddressbookMultigetRequest { prop, .. }) => prop,
|
||||||
ReportRequest::SyncCollection(SyncCollectionRequest { prop, .. }) => prop,
|
ReportRequest::SyncCollection(SyncCollectionRequest { prop, .. }) => prop,
|
||||||
};
|
|
||||||
|
|
||||||
match prop_element {
|
|
||||||
PropfindType::Allprop => {
|
|
||||||
vec!["allprop"]
|
|
||||||
}
|
|
||||||
PropfindType::Propname => {
|
|
||||||
vec!["propname"]
|
|
||||||
}
|
|
||||||
PropfindType::Prop(PropElement(prop_tags)) => prop_tags
|
|
||||||
.iter()
|
|
||||||
.map(|propname| propname.name.as_str())
|
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,13 +45,12 @@ pub async fn route_report_addressbook<AS: AddressbookStore>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let request = ReportRequest::parse_str(&body)?;
|
let request = ReportRequest::parse_str(&body)?;
|
||||||
let props = request.props();
|
|
||||||
|
|
||||||
Ok(match &request {
|
Ok(match &request {
|
||||||
ReportRequest::AddressbookMultiget(addr_multiget) => {
|
ReportRequest::AddressbookMultiget(addr_multiget) => {
|
||||||
handle_addressbook_multiget(
|
handle_addressbook_multiget(
|
||||||
addr_multiget,
|
addr_multiget,
|
||||||
&props,
|
request.props(),
|
||||||
req.path(),
|
req.path(),
|
||||||
puri.as_ref(),
|
puri.as_ref(),
|
||||||
&user,
|
&user,
|
||||||
@@ -77,7 +63,6 @@ pub async fn route_report_addressbook<AS: AddressbookStore>(
|
|||||||
ReportRequest::SyncCollection(sync_collection) => {
|
ReportRequest::SyncCollection(sync_collection) => {
|
||||||
handle_sync_collection(
|
handle_sync_collection(
|
||||||
sync_collection,
|
sync_collection,
|
||||||
&props,
|
|
||||||
req.path(),
|
req.path(),
|
||||||
puri.as_ref(),
|
puri.as_ref(),
|
||||||
&user,
|
&user,
|
||||||
@@ -92,9 +77,9 @@ pub async fn route_report_addressbook<AS: AddressbookStore>(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use rustical_dav::xml::{PropElement, Propname, sync_collection::SyncLevel};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::address_object::resource::AddressObjectPropName;
|
||||||
|
use rustical_dav::xml::{PropElement, sync_collection::SyncLevel};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_xml_sync_collection() {
|
fn test_xml_sync_collection() {
|
||||||
@@ -115,10 +100,12 @@ mod tests {
|
|||||||
ReportRequest::SyncCollection(SyncCollectionRequest {
|
ReportRequest::SyncCollection(SyncCollectionRequest {
|
||||||
sync_token: "".to_owned(),
|
sync_token: "".to_owned(),
|
||||||
sync_level: SyncLevel::One,
|
sync_level: SyncLevel::One,
|
||||||
prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![Propname {
|
prop: rustical_dav::xml::PropfindType::Prop(PropElement(
|
||||||
name: "getetag".to_owned(),
|
vec![AddressObjectPropWrapperName::AddressObject(
|
||||||
ns: Some("DAV:".into())
|
AddressObjectPropName::Getetag
|
||||||
}])),
|
)],
|
||||||
|
vec![]
|
||||||
|
)),
|
||||||
limit: None
|
limit: None
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -141,9 +128,13 @@ mod tests {
|
|||||||
report_request,
|
report_request,
|
||||||
ReportRequest::AddressbookMultiget(AddressbookMultigetRequest {
|
ReportRequest::AddressbookMultiget(AddressbookMultigetRequest {
|
||||||
prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![
|
prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![
|
||||||
Propname{name: "getetag".to_owned(), ns: Some("DAV:".into())},
|
AddressObjectPropWrapperName::AddressObject(
|
||||||
Propname{name: "address-data".to_owned(), ns: Some("urn:ietf:params:xml:ns:carddav".into())}
|
AddressObjectPropName::Getetag
|
||||||
])),
|
),
|
||||||
|
AddressObjectPropWrapperName::AddressObject(
|
||||||
|
AddressObjectPropName::AddressData
|
||||||
|
),
|
||||||
|
], vec![])),
|
||||||
href: vec![
|
href: vec![
|
||||||
"/carddav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned()
|
"/carddav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned()
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
Error,
|
Error,
|
||||||
address_object::resource::{AddressObjectPropWrapper, AddressObjectResource},
|
address_object::resource::{
|
||||||
|
AddressObjectPropWrapper, AddressObjectPropWrapperName, AddressObjectResource,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
use rustical_dav::{
|
use rustical_dav::{
|
||||||
@@ -16,8 +18,7 @@ use rustical_store::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub async fn handle_sync_collection<AS: AddressbookStore>(
|
pub async fn handle_sync_collection<AS: AddressbookStore>(
|
||||||
sync_collection: &SyncCollectionRequest,
|
sync_collection: &SyncCollectionRequest<AddressObjectPropWrapperName>,
|
||||||
props: &[&str],
|
|
||||||
path: &str,
|
path: &str,
|
||||||
puri: &impl PrincipalUri,
|
puri: &impl PrincipalUri,
|
||||||
user: &User,
|
user: &User,
|
||||||
@@ -38,7 +39,7 @@ pub async fn handle_sync_collection<AS: AddressbookStore>(
|
|||||||
object,
|
object,
|
||||||
principal: principal.to_owned(),
|
principal: principal.to_owned(),
|
||||||
}
|
}
|
||||||
.propfind(&path, props, puri, user)?,
|
.propfind_typed(&path, &sync_collection.prop, puri, user)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
|||||||
use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
|
use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore};
|
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore};
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ impl<A: AddressbookStore, S: SubscriptionStore> AddressbookResourceService<A, S>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "AddressbookPropName")]
|
#[xml(unit_variants_ident = "AddressbookPropName")]
|
||||||
pub enum AddressbookProp {
|
pub enum AddressbookProp {
|
||||||
// WebDAV (RFC 2518)
|
// WebDAV (RFC 2518)
|
||||||
@@ -53,7 +53,7 @@ pub enum AddressbookProp {
|
|||||||
MaxResourceSize(i64),
|
MaxResourceSize(i64),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "AddressbookPropWrapperName", untagged)]
|
#[xml(unit_variants_ident = "AddressbookPropWrapperName", untagged)]
|
||||||
pub enum AddressbookPropWrapper {
|
pub enum AddressbookPropWrapper {
|
||||||
Addressbook(AddressbookProp),
|
Addressbook(AddressbookProp),
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use rustical_dav::resource::{PrincipalUri, Resource, ResourceService};
|
|||||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::{AuthenticationProvider, User};
|
use rustical_store::auth::{AuthenticationProvider, User};
|
||||||
use rustical_store::{AddressbookStore, SubscriptionStore};
|
use rustical_store::{AddressbookStore, SubscriptionStore};
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct PrincipalResourceService<
|
pub struct PrincipalResourceService<
|
||||||
@@ -53,7 +53,7 @@ pub struct PrincipalResource {
|
|||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
||||||
pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
|
pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "PrincipalPropName")]
|
#[xml(unit_variants_ident = "PrincipalPropName")]
|
||||||
pub enum PrincipalProp {
|
pub enum PrincipalProp {
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
@@ -71,7 +71,7 @@ pub enum PrincipalProp {
|
|||||||
PrincipalAddress(Option<HrefElement>),
|
PrincipalAddress(Option<HrefElement>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||||
pub enum PrincipalPropWrapper {
|
pub enum PrincipalPropWrapper {
|
||||||
Principal(PrincipalProp),
|
Principal(PrincipalProp),
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ use crate::{
|
|||||||
resource::{PrincipalUri, Resource},
|
resource::{PrincipalUri, Resource},
|
||||||
xml::{HrefElement, Resourcetype},
|
xml::{HrefElement, Resourcetype},
|
||||||
};
|
};
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumUnitVariants, EnumVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, PropName, EnumVariants)]
|
||||||
#[xml(unit_variants_ident = "CommonPropertiesPropName")]
|
#[xml(unit_variants_ident = "CommonPropertiesPropName")]
|
||||||
pub enum CommonPropertiesProp {
|
pub enum CommonPropertiesProp {
|
||||||
// WebDAV (RFC 2518)
|
// WebDAV (RFC 2518)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumUnitVariants, EnumVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, PropName, EnumVariants)]
|
||||||
#[xml(unit_variants_ident = "SyncTokenExtensionPropName")]
|
#[xml(unit_variants_ident = "SyncTokenExtensionPropName")]
|
||||||
pub enum SyncTokenExtensionProp {
|
pub enum SyncTokenExtensionProp {
|
||||||
// Collection Synchronization (RFC 6578)
|
// Collection Synchronization (RFC 6578)
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ use crate::resource::PrincipalUri;
|
|||||||
use crate::resource::Resource;
|
use crate::resource::Resource;
|
||||||
use crate::resource::ResourceService;
|
use crate::resource::ResourceService;
|
||||||
use crate::xml::MultistatusElement;
|
use crate::xml::MultistatusElement;
|
||||||
use crate::xml::PropElement;
|
|
||||||
use crate::xml::PropfindElement;
|
use crate::xml::PropfindElement;
|
||||||
use crate::xml::PropfindType;
|
use crate::xml::PropfindType;
|
||||||
|
use rustical_xml::PropName;
|
||||||
use rustical_xml::XmlDocument;
|
use rustical_xml::XmlDocument;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
@@ -58,37 +58,36 @@ pub(crate) async fn route_propfind<R: ResourceService>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A request body is optional. If empty we MUST return all props
|
// A request body is optional. If empty we MUST return all props
|
||||||
let propfind: PropfindElement = if !body.is_empty() {
|
let propfind_self: PropfindElement<<<R::Resource as Resource>::Prop as PropName>::Names> =
|
||||||
PropfindElement::parse_str(&body).map_err(Error::XmlError)?
|
if !body.is_empty() {
|
||||||
} else {
|
PropfindElement::parse_str(&body).map_err(Error::XmlError)?
|
||||||
PropfindElement {
|
} else {
|
||||||
prop: PropfindType::Allprop,
|
PropfindElement {
|
||||||
}
|
prop: PropfindType::Allprop,
|
||||||
};
|
}
|
||||||
|
};
|
||||||
// TODO: respect namespaces?
|
let propfind_member: PropfindElement<<<R::MemberType as Resource>::Prop as PropName>::Names> =
|
||||||
let props = match &propfind.prop {
|
if !body.is_empty() {
|
||||||
PropfindType::Allprop => vec!["allprop"],
|
PropfindElement::parse_str(&body).map_err(Error::XmlError)?
|
||||||
PropfindType::Propname => vec!["propname"],
|
} else {
|
||||||
PropfindType::Prop(PropElement(prop_tags)) => prop_tags
|
PropfindElement {
|
||||||
.iter()
|
prop: PropfindType::Allprop,
|
||||||
.map(|propname| propname.name.as_str())
|
}
|
||||||
.collect(),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let mut member_responses = Vec::new();
|
let mut member_responses = Vec::new();
|
||||||
if depth != Depth::Zero {
|
if depth != Depth::Zero {
|
||||||
for (subpath, member) in resource_service.get_members(path_components).await? {
|
for (subpath, member) in resource_service.get_members(path_components).await? {
|
||||||
member_responses.push(member.propfind(
|
member_responses.push(member.propfind_typed(
|
||||||
&format!("{}/{}", path.trim_end_matches('/'), subpath),
|
&format!("{}/{}", path.trim_end_matches('/'), subpath),
|
||||||
&props,
|
&propfind_member.prop,
|
||||||
puri,
|
puri,
|
||||||
&user,
|
&user,
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = resource.propfind(path, &props, puri, &user)?;
|
let response = resource.propfind_typed(path, &propfind_self.prop, puri, &user)?;
|
||||||
|
|
||||||
Ok(MultistatusElement {
|
Ok(MultistatusElement {
|
||||||
responses: vec![response],
|
responses: vec![response],
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ use crate::xml::TagList;
|
|||||||
use crate::xml::multistatus::{PropstatElement, PropstatWrapper, ResponseElement};
|
use crate::xml::multistatus::{PropstatElement, PropstatWrapper, ResponseElement};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use quick_xml::name::Namespace;
|
use quick_xml::name::Namespace;
|
||||||
use rustical_xml::EnumUnitVariants;
|
use rustical_xml::NamespaceOwned;
|
||||||
|
use rustical_xml::PropName;
|
||||||
use rustical_xml::Unparsed;
|
use rustical_xml::Unparsed;
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::XmlDeserialize;
|
||||||
use rustical_xml::XmlDocument;
|
use rustical_xml::XmlDocument;
|
||||||
@@ -111,13 +112,15 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
|
|||||||
}) => {
|
}) => {
|
||||||
match property {
|
match property {
|
||||||
SetPropertyPropWrapper::Valid(prop) => {
|
SetPropertyPropWrapper::Valid(prop) => {
|
||||||
let propname: <<R::Resource as Resource>::Prop as EnumUnitVariants>::UnitVariants = prop.clone().into();
|
let propname: <<R::Resource as Resource>::Prop as PropName>::Names =
|
||||||
|
prop.clone().into();
|
||||||
let (ns, propname): (Option<Namespace>, &str) = propname.into();
|
let (ns, propname): (Option<Namespace>, &str) = propname.into();
|
||||||
match resource.set_prop(prop) {
|
match resource.set_prop(prop) {
|
||||||
Ok(()) => props_ok.push((ns, propname.to_owned())),
|
Ok(()) => {
|
||||||
Err(Error::PropReadOnly) => {
|
props_ok.push((ns.map(NamespaceOwned::from), propname.to_owned()))
|
||||||
props_conflict.push((ns, propname.to_owned()))
|
|
||||||
}
|
}
|
||||||
|
Err(Error::PropReadOnly) => props_conflict
|
||||||
|
.push((ns.map(NamespaceOwned::from), propname.to_owned())),
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -128,7 +131,7 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.find_map(|(ns, tag)| {
|
.find_map(|(ns, tag)| {
|
||||||
if tag == propname.as_str() {
|
if tag == propname.as_str() {
|
||||||
Some((ns, tag.to_owned()))
|
Some((ns.map(NamespaceOwned::from), tag.to_owned()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -146,14 +149,12 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
|
|||||||
}
|
}
|
||||||
Operation::Remove(remove_el) => {
|
Operation::Remove(remove_el) => {
|
||||||
let propname = remove_el.prop.0.0;
|
let propname = remove_el.prop.0.0;
|
||||||
match <<R::Resource as Resource>::Prop as EnumUnitVariants>::UnitVariants::from_str(
|
match <<R::Resource as Resource>::Prop as PropName>::Names::from_str(&propname) {
|
||||||
&propname,
|
|
||||||
) {
|
|
||||||
Ok(prop) => match resource.remove_prop(&prop) {
|
Ok(prop) => match resource.remove_prop(&prop) {
|
||||||
Ok(()) => props_ok.push((None, propname)),
|
Ok(()) => props_ok.push((None, propname)),
|
||||||
Err(Error::PropReadOnly) => props_conflict.push({
|
Err(Error::PropReadOnly) => props_conflict.push({
|
||||||
let (ns, tag) = prop.into();
|
let (ns, tag) = prop.into();
|
||||||
(ns, tag.to_owned())
|
(ns.map(NamespaceOwned::from), tag.to_owned())
|
||||||
}),
|
}),
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
|
use crate::Principal;
|
||||||
use crate::privileges::UserPrivilegeSet;
|
use crate::privileges::UserPrivilegeSet;
|
||||||
use crate::xml::Resourcetype;
|
|
||||||
use crate::xml::multistatus::{PropTagWrapper, PropstatElement, PropstatWrapper};
|
use crate::xml::multistatus::{PropTagWrapper, PropstatElement, PropstatWrapper};
|
||||||
|
use crate::xml::{PropElement, PropfindType, Resourcetype};
|
||||||
use crate::xml::{TagList, multistatus::ResponseElement};
|
use crate::xml::{TagList, multistatus::ResponseElement};
|
||||||
use crate::{Error, Principal};
|
|
||||||
use headers::{ETag, IfMatch, IfNoneMatch};
|
use headers::{ETag, IfMatch, IfNoneMatch};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use quick_xml::name::Namespace;
|
use quick_xml::name::Namespace;
|
||||||
pub use resource_service::ResourceService;
|
pub use resource_service::ResourceService;
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, NamespaceOwned, PropName, XmlDeserialize, XmlSerialize};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ pub trait ResourcePropName: FromStr {}
|
|||||||
impl<T: FromStr> ResourcePropName for T {}
|
impl<T: FromStr> ResourcePropName for T {}
|
||||||
|
|
||||||
pub trait Resource: Clone + 'static {
|
pub trait Resource: Clone + 'static {
|
||||||
type Prop: ResourceProp + PartialEq + Clone + EnumVariants + EnumUnitVariants;
|
type Prop: ResourceProp + PartialEq + Clone + EnumVariants + PropName;
|
||||||
type Error: From<crate::Error>;
|
type Error: From<crate::Error>;
|
||||||
type Principal: Principal;
|
type Principal: Principal;
|
||||||
|
|
||||||
@@ -40,17 +40,14 @@ pub trait Resource: Clone + 'static {
|
|||||||
&self,
|
&self,
|
||||||
principal_uri: &impl PrincipalUri,
|
principal_uri: &impl PrincipalUri,
|
||||||
principal: &Self::Principal,
|
principal: &Self::Principal,
|
||||||
prop: &<Self::Prop as EnumUnitVariants>::UnitVariants,
|
prop: &<Self::Prop as PropName>::Names,
|
||||||
) -> Result<Self::Prop, Self::Error>;
|
) -> Result<Self::Prop, Self::Error>;
|
||||||
|
|
||||||
fn set_prop(&mut self, _prop: Self::Prop) -> Result<(), crate::Error> {
|
fn set_prop(&mut self, _prop: Self::Prop) -> Result<(), crate::Error> {
|
||||||
Err(crate::Error::PropReadOnly)
|
Err(crate::Error::PropReadOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_prop(
|
fn remove_prop(&mut self, _prop: &<Self::Prop as PropName>::Names) -> Result<(), crate::Error> {
|
||||||
&mut self,
|
|
||||||
_prop: &<Self::Prop as EnumUnitVariants>::UnitVariants,
|
|
||||||
) -> Result<(), crate::Error> {
|
|
||||||
Err(crate::Error::PropReadOnly)
|
Err(crate::Error::PropReadOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,62 +88,45 @@ pub trait Resource: Clone + 'static {
|
|||||||
principal: &Self::Principal,
|
principal: &Self::Principal,
|
||||||
) -> Result<UserPrivilegeSet, Self::Error>;
|
) -> Result<UserPrivilegeSet, Self::Error>;
|
||||||
|
|
||||||
fn propfind(
|
fn propfind_typed(
|
||||||
&self,
|
&self,
|
||||||
path: &str,
|
path: &str,
|
||||||
props: &[&str],
|
prop: &PropfindType<<Self::Prop as PropName>::Names>,
|
||||||
principal_uri: &impl PrincipalUri,
|
principal_uri: &impl PrincipalUri,
|
||||||
principal: &Self::Principal,
|
principal: &Self::Principal,
|
||||||
) -> Result<ResponseElement<Self::Prop>, Self::Error> {
|
) -> Result<ResponseElement<Self::Prop>, Self::Error> {
|
||||||
let mut props: HashSet<&str> = props.iter().cloned().collect();
|
// TODO: Support include element
|
||||||
|
let (props, invalid_props): (HashSet<<Self::Prop as PropName>::Names>, Vec<_>) = match prop
|
||||||
|
{
|
||||||
|
PropfindType::Propname => {
|
||||||
|
let props = Self::list_props()
|
||||||
|
.into_iter()
|
||||||
|
.map(|(ns, tag)| (ns.map(NamespaceOwned::from), tag.to_string()))
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
if props.contains(&"propname") {
|
return Ok(ResponseElement {
|
||||||
if props.len() != 1 {
|
href: path.to_owned(),
|
||||||
// propname MUST be the only queried prop per spec
|
propstat: vec![PropstatWrapper::TagList(PropstatElement {
|
||||||
return Err(
|
prop: TagList::from(props),
|
||||||
Error::BadRequest("propname MUST be the only queried prop".to_owned()).into(),
|
status: StatusCode::OK,
|
||||||
);
|
})],
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
PropfindType::Allprop => (
|
||||||
|
Self::list_props()
|
||||||
|
.iter()
|
||||||
|
.map(|(_ns, name)| <Self::Prop as PropName>::Names::from_str(name).unwrap())
|
||||||
|
.collect(),
|
||||||
|
vec![],
|
||||||
|
),
|
||||||
|
PropfindType::Prop(PropElement(valid_tags, invalid_tags)) => (
|
||||||
|
valid_tags.iter().cloned().collect(),
|
||||||
|
invalid_tags.to_owned(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
let props = Self::list_props()
|
let prop_responses = props
|
||||||
.into_iter()
|
|
||||||
.map(|(ns, tag)| (ns.to_owned(), tag.to_string()))
|
|
||||||
.collect_vec();
|
|
||||||
|
|
||||||
return Ok(ResponseElement {
|
|
||||||
href: path.to_owned(),
|
|
||||||
propstat: vec![PropstatWrapper::TagList(PropstatElement {
|
|
||||||
prop: TagList::from(props),
|
|
||||||
status: StatusCode::OK,
|
|
||||||
})],
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if props.contains(&"allprop") {
|
|
||||||
if props.len() != 1 {
|
|
||||||
// allprop MUST be the only queried prop per spec
|
|
||||||
return Err(
|
|
||||||
Error::BadRequest("allprop MUST be the only queried prop".to_owned()).into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
props = Self::list_props()
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_ns, tag)| tag)
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut valid_props = vec![];
|
|
||||||
let mut invalid_props = vec![];
|
|
||||||
for prop in props {
|
|
||||||
if let Ok(valid_prop) = <Self::Prop as EnumUnitVariants>::UnitVariants::from_str(prop) {
|
|
||||||
valid_props.push(valid_prop);
|
|
||||||
} else {
|
|
||||||
invalid_props.push(prop.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let prop_responses = valid_props
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|prop| self.get_prop(principal_uri, principal, &prop))
|
.map(|prop| self.get_prop(principal_uri, principal, &prop))
|
||||||
.collect::<Result<Vec<_>, Self::Error>>()?;
|
.collect::<Result<Vec<_>, Self::Error>>()?;
|
||||||
@@ -158,11 +138,7 @@ pub trait Resource: Clone + 'static {
|
|||||||
if !invalid_props.is_empty() {
|
if !invalid_props.is_empty() {
|
||||||
propstats.push(PropstatWrapper::TagList(PropstatElement {
|
propstats.push(PropstatWrapper::TagList(PropstatElement {
|
||||||
status: StatusCode::NOT_FOUND,
|
status: StatusCode::NOT_FOUND,
|
||||||
prop: invalid_props
|
prop: invalid_props.into(),
|
||||||
.into_iter()
|
|
||||||
.map(|tag| (None, tag))
|
|
||||||
.collect_vec()
|
|
||||||
.into(),
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
Ok(ResponseElement {
|
Ok(ResponseElement {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ mod resourcetype;
|
|||||||
pub mod tag_list;
|
pub mod tag_list;
|
||||||
use derive_more::derive::From;
|
use derive_more::derive::From;
|
||||||
pub use multistatus::MultistatusElement;
|
pub use multistatus::MultistatusElement;
|
||||||
pub use propfind::{PropElement, PropfindElement, PropfindType, Propname};
|
pub use propfind::{PropElement, PropfindElement, PropfindType};
|
||||||
pub use resourcetype::{Resourcetype, ResourcetypeInner};
|
pub use resourcetype::{Resourcetype, ResourcetypeInner};
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
pub use tag_list::TagList;
|
pub use tag_list::TagList;
|
||||||
|
|||||||
@@ -1,27 +1,85 @@
|
|||||||
|
use quick_xml::events::Event;
|
||||||
|
use quick_xml::name::ResolveResult;
|
||||||
use rustical_xml::NamespaceOwned;
|
use rustical_xml::NamespaceOwned;
|
||||||
|
use rustical_xml::Unparsed;
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::XmlDeserialize;
|
||||||
|
use rustical_xml::XmlError;
|
||||||
use rustical_xml::XmlRootTag;
|
use rustical_xml::XmlRootTag;
|
||||||
|
|
||||||
#[derive(Debug, Clone, XmlDeserialize, XmlRootTag, PartialEq)]
|
#[derive(Debug, Clone, XmlDeserialize, XmlRootTag, PartialEq)]
|
||||||
#[xml(root = b"propfind", ns = "crate::namespace::NS_DAV")]
|
#[xml(root = b"propfind", ns = "crate::namespace::NS_DAV")]
|
||||||
pub struct PropfindElement {
|
pub struct PropfindElement<PN: XmlDeserialize> {
|
||||||
#[xml(ty = "untagged")]
|
#[xml(ty = "untagged")]
|
||||||
pub prop: PropfindType,
|
pub prop: PropfindType<PN>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
// pub struct PropElement<PN: XmlDeserialize = Propname>(#[xml(ty = "untagged", flatten)] pub Vec<PN>);
|
||||||
|
pub struct PropElement<PN: XmlDeserialize>(
|
||||||
|
// valid
|
||||||
|
pub Vec<PN>,
|
||||||
|
// invalid
|
||||||
|
pub Vec<(Option<NamespaceOwned>, String)>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<PN: XmlDeserialize> XmlDeserialize for PropElement<PN> {
|
||||||
|
fn deserialize<R: std::io::BufRead>(
|
||||||
|
reader: &mut quick_xml::NsReader<R>,
|
||||||
|
start: &quick_xml::events::BytesStart,
|
||||||
|
empty: bool,
|
||||||
|
) -> Result<Self, XmlError> {
|
||||||
|
if empty {
|
||||||
|
return Ok(Self(vec![], vec![]));
|
||||||
|
}
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let mut valid_props = vec![];
|
||||||
|
let mut invalid_props = vec![];
|
||||||
|
loop {
|
||||||
|
let event = reader.read_event_into(&mut buf)?;
|
||||||
|
match &event {
|
||||||
|
Event::End(e) if e.name() == start.name() => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Event::Eof => return Err(XmlError::Eof),
|
||||||
|
// start of a child element
|
||||||
|
Event::Start(start) | Event::Empty(start) => {
|
||||||
|
let empty = matches!(event, Event::Empty(_));
|
||||||
|
let (ns, name) = reader.resolve_element(start.name());
|
||||||
|
let ns = match ns {
|
||||||
|
ResolveResult::Bound(ns) => Some(NamespaceOwned::from(ns)),
|
||||||
|
ResolveResult::Unknown(_ns) => todo!("handle error"),
|
||||||
|
ResolveResult::Unbound => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
match PN::deserialize(reader, start, empty) {
|
||||||
|
Ok(propname) => valid_props.push(propname),
|
||||||
|
Err(XmlError::InvalidVariant(_)) => {
|
||||||
|
invalid_props
|
||||||
|
.push((ns, String::from_utf8_lossy(name.as_ref()).to_string()));
|
||||||
|
// Consume content
|
||||||
|
Unparsed::deserialize(reader, start, empty)?;
|
||||||
|
}
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Text(_) | Event::CData(_) => {
|
||||||
|
return Err(XmlError::UnsupportedEvent("Not expecting text here"));
|
||||||
|
}
|
||||||
|
Event::Decl(_) | Event::Comment(_) | Event::DocType(_) | Event::PI(_) => { /* ignore */
|
||||||
|
}
|
||||||
|
Event::End(_end) => {
|
||||||
|
unreachable!(
|
||||||
|
"Unexpected closing tag for wrong element, should be handled by quick_xml"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self(valid_props, invalid_props))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, XmlDeserialize, PartialEq)]
|
#[derive(Debug, Clone, XmlDeserialize, PartialEq)]
|
||||||
pub struct PropElement<PN: XmlDeserialize = Propname>(#[xml(ty = "untagged", flatten)] pub Vec<PN>);
|
pub enum PropfindType<PN: XmlDeserialize> {
|
||||||
|
|
||||||
#[derive(Debug, Clone, XmlDeserialize, PartialEq)]
|
|
||||||
pub struct Propname {
|
|
||||||
#[xml(ty = "namespace")]
|
|
||||||
pub ns: Option<NamespaceOwned>,
|
|
||||||
#[xml(ty = "tag_name")]
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, XmlDeserialize, PartialEq)]
|
|
||||||
pub enum PropfindType<PN: XmlDeserialize = Propname> {
|
|
||||||
#[xml(ns = "crate::namespace::NS_DAV")]
|
#[xml(ns = "crate::namespace::NS_DAV")]
|
||||||
Propname,
|
Propname,
|
||||||
#[xml(ns = "crate::namespace::NS_DAV")]
|
#[xml(ns = "crate::namespace::NS_DAV")]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use rustical_xml::{ValueDeserialize, ValueSerialize, XmlDeserialize};
|
use rustical_xml::{ValueDeserialize, ValueSerialize, XmlDeserialize};
|
||||||
|
|
||||||
use super::{PropfindType, Propname};
|
use super::PropfindType;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum SyncLevel {
|
pub enum SyncLevel {
|
||||||
@@ -37,7 +37,7 @@ impl ValueSerialize for SyncLevel {
|
|||||||
// <!-- DAV:limit defined in RFC 5323, Section 5.17 -->
|
// <!-- DAV:limit defined in RFC 5323, Section 5.17 -->
|
||||||
// <!-- DAV:prop defined in RFC 4918, Section 14.18 -->
|
// <!-- DAV:prop defined in RFC 4918, Section 14.18 -->
|
||||||
#[xml(ns = "crate::namespace::NS_DAV")]
|
#[xml(ns = "crate::namespace::NS_DAV")]
|
||||||
pub struct SyncCollectionRequest<PN: XmlDeserialize = Propname> {
|
pub struct SyncCollectionRequest<PN: XmlDeserialize> {
|
||||||
#[xml(ns = "crate::namespace::NS_DAV")]
|
#[xml(ns = "crate::namespace::NS_DAV")]
|
||||||
pub sync_token: String,
|
pub sync_token: String,
|
||||||
#[xml(ns = "crate::namespace::NS_DAV")]
|
#[xml(ns = "crate::namespace::NS_DAV")]
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
use derive_more::derive::From;
|
use derive_more::derive::From;
|
||||||
use quick_xml::name::Namespace;
|
use quick_xml::{
|
||||||
use rustical_xml::XmlSerialize;
|
events::{BytesStart, Event},
|
||||||
|
name::Namespace,
|
||||||
|
};
|
||||||
|
use rustical_xml::{NamespaceOwned, XmlSerialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, From)]
|
#[derive(Clone, Debug, PartialEq, From)]
|
||||||
pub struct TagList(Vec<(Option<Namespace<'static>>, String)>);
|
pub struct TagList(Vec<(Option<NamespaceOwned>, String)>);
|
||||||
|
|
||||||
impl XmlSerialize for TagList {
|
impl XmlSerialize for TagList {
|
||||||
fn serialize<W: std::io::Write>(
|
fn serialize<W: std::io::Write>(
|
||||||
@@ -14,22 +17,10 @@ impl XmlSerialize for TagList {
|
|||||||
namespaces: &HashMap<Namespace, &[u8]>,
|
namespaces: &HashMap<Namespace, &[u8]>,
|
||||||
writer: &mut quick_xml::Writer<W>,
|
writer: &mut quick_xml::Writer<W>,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
#[derive(Debug, XmlSerialize, PartialEq)]
|
for (_ns, tag) in &self.0 {
|
||||||
struct Inner(#[xml(ty = "untagged", flatten)] Vec<Tag>);
|
writer.write_event(Event::Empty(BytesStart::new(tag)))?;
|
||||||
|
}
|
||||||
#[derive(Debug, XmlSerialize, PartialEq)]
|
Ok(())
|
||||||
struct Tag(
|
|
||||||
#[xml(ty = "namespace")] Option<Namespace<'static>>,
|
|
||||||
#[xml(ty = "tag_name")] String,
|
|
||||||
);
|
|
||||||
|
|
||||||
Inner(
|
|
||||||
self.0
|
|
||||||
.iter()
|
|
||||||
.map(|(ns, tag)| Tag(ns.to_owned(), tag.to_owned()))
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
.serialize(ns, tag, namespaces, writer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(refining_impl_trait)]
|
#[allow(refining_impl_trait)]
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
use rustical_dav::xml::{PropElement, PropfindElement, PropfindType, Propname};
|
|
||||||
use rustical_xml::de::XmlDocument;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn propfind_allprop() {
|
|
||||||
let propfind = PropfindElement::parse_str(
|
|
||||||
r#"
|
|
||||||
<propfind xmlns="DAV:">
|
|
||||||
<allprop />
|
|
||||||
</propfind>
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
propfind,
|
|
||||||
PropfindElement {
|
|
||||||
prop: PropfindType::Allprop
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn propfind_propname() {
|
|
||||||
let propfind = PropfindElement::parse_str(
|
|
||||||
r#"
|
|
||||||
<propfind xmlns="DAV:">
|
|
||||||
<propname />
|
|
||||||
</propfind>
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
propfind,
|
|
||||||
PropfindElement {
|
|
||||||
prop: PropfindType::Propname
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn propfind_prop() {
|
|
||||||
let propfind = PropfindElement::parse_str(
|
|
||||||
r#"
|
|
||||||
<propfind xmlns="DAV:">
|
|
||||||
<prop>
|
|
||||||
<displayname />
|
|
||||||
<color />
|
|
||||||
</prop>
|
|
||||||
</propfind>
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
propfind,
|
|
||||||
PropfindElement {
|
|
||||||
prop: PropfindType::Prop(PropElement(vec![
|
|
||||||
Propname {
|
|
||||||
name: "displayname".to_owned(),
|
|
||||||
ns: Some("DAV:".to_owned().into())
|
|
||||||
},
|
|
||||||
Propname {
|
|
||||||
name: "color".to_owned(),
|
|
||||||
ns: Some("DAV:".to_owned().into())
|
|
||||||
},
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Example taken from DAVx5
|
|
||||||
#[test]
|
|
||||||
fn propfind_decl() {
|
|
||||||
let propfind = PropfindElement::parse_str(
|
|
||||||
r#"
|
|
||||||
<?xml version='1.0' encoding='UTF-8' ?>
|
|
||||||
<propfind xmlns="DAV:" xmlns:CAL="urn:ietf:params:xml:ns:caldav" xmlns:CARD="urn:ietf:params:xml:ns:carddav">
|
|
||||||
<prop>
|
|
||||||
<CARD:max-resource-size />
|
|
||||||
<CARD:supported-address-data />
|
|
||||||
<supported-report-set />
|
|
||||||
<n0:getctag xmlns:n0="http://calendarserver.org/ns/" />
|
|
||||||
<sync-token />
|
|
||||||
</prop>
|
|
||||||
</propfind>
|
|
||||||
"#
|
|
||||||
).unwrap();
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::{ContentUpdate, PropertyUpdate, SupportedTriggers, Transports, Trigger};
|
use crate::{ContentUpdate, PropertyUpdate, SupportedTriggers, Transports, Trigger};
|
||||||
use rustical_dav::header::Depth;
|
use rustical_dav::header::Depth;
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumUnitVariants, EnumVariants)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, PropName, EnumVariants)]
|
||||||
#[xml(unit_variants_ident = "DavPushExtensionPropName")]
|
#[xml(unit_variants_ident = "DavPushExtensionPropName")]
|
||||||
pub enum DavPushExtensionProp {
|
pub enum DavPushExtensionProp {
|
||||||
// WebDav Push
|
// WebDav Push
|
||||||
|
|||||||
@@ -17,9 +17,7 @@ rustical_xml.workspace = true
|
|||||||
ical.workspace = true
|
ical.workspace = true
|
||||||
lazy_static.workspace = true
|
lazy_static.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
strum.workspace = true
|
rrule.workspace = true
|
||||||
strum_macros.workspace = true
|
|
||||||
rrule = "0.14"
|
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
actix-web = { workspace = true, optional = true }
|
actix-web = { workspace = true, optional = true }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::Error;
|
use crate::Error;
|
||||||
use crate::{CalDateTime, ComponentMut, parse_duration};
|
use crate::{CalDateTime, ComponentMut, parse_duration};
|
||||||
use chrono::{DateTime, Duration};
|
use chrono::{DateTime, Duration, Utc};
|
||||||
use ical::{
|
use ical::{
|
||||||
generator::IcalEvent,
|
generator::IcalEvent,
|
||||||
parser::{Component, ical::component::IcalTimeZone},
|
parser::{Component, ical::component::IcalTimeZone},
|
||||||
@@ -89,8 +89,18 @@ impl EventObject {
|
|||||||
Ok(Some(rrule_set))
|
Ok(Some(rrule_set))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_recurrence(&self) -> Result<Vec<IcalEvent>, Error> {
|
pub fn expand_recurrence(
|
||||||
if let Some(rrule_set) = self.recurrence_ruleset()? {
|
&self,
|
||||||
|
start: Option<DateTime<Utc>>,
|
||||||
|
end: Option<DateTime<Utc>>,
|
||||||
|
) -> Result<Vec<IcalEvent>, Error> {
|
||||||
|
if let Some(mut rrule_set) = self.recurrence_ruleset()? {
|
||||||
|
if let Some(start) = start {
|
||||||
|
rrule_set = rrule_set.after(start.with_timezone(&rrule::Tz::UTC));
|
||||||
|
}
|
||||||
|
if let Some(end) = end {
|
||||||
|
rrule_set = rrule_set.before(end.with_timezone(&rrule::Tz::UTC));
|
||||||
|
}
|
||||||
let mut events = vec![];
|
let mut events = vec![];
|
||||||
let dates = rrule_set.all(2048).dates;
|
let dates = rrule_set.all(2048).dates;
|
||||||
|
|
||||||
@@ -205,7 +215,7 @@ END:VEVENT\r\n",
|
|||||||
let event = event.event().unwrap();
|
let event = event.event().unwrap();
|
||||||
|
|
||||||
let events: Vec<String> = event
|
let events: Vec<String> = event
|
||||||
.expand_recurrence()
|
.expand_recurrence(None, None)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|event| Emitter::generate(&event))
|
.map(|event| Emitter::generate(&event))
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use super::{EventObject, JournalObject, TodoObject};
|
use super::{EventObject, JournalObject, TodoObject};
|
||||||
use crate::CalDateTime;
|
use crate::CalDateTime;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
use chrono::DateTime;
|
||||||
|
use chrono::Utc;
|
||||||
use ical::{
|
use ical::{
|
||||||
generator::{Emitter, IcalCalendar},
|
generator::{Emitter, IcalCalendar},
|
||||||
parser::{Component, ical::component::IcalTimeZone},
|
parser::{Component, ical::component::IcalTimeZone},
|
||||||
@@ -188,12 +190,16 @@ impl CalendarObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expand_recurrence(&self) -> Result<String, Error> {
|
pub fn expand_recurrence(
|
||||||
|
&self,
|
||||||
|
start: Option<DateTime<Utc>>,
|
||||||
|
end: Option<DateTime<Utc>>,
|
||||||
|
) -> Result<String, Error> {
|
||||||
// Only events can be expanded
|
// Only events can be expanded
|
||||||
match &self.data {
|
match &self.data {
|
||||||
CalendarObjectComponent::Event(event) => {
|
CalendarObjectComponent::Event(event) => {
|
||||||
let mut cal = self.cal.clone();
|
let mut cal = self.cal.clone();
|
||||||
cal.events = event.expand_recurrence()?;
|
cal.events = event.expand_recurrence(start, end)?;
|
||||||
Ok(cal.generate())
|
Ok(cal.generate())
|
||||||
}
|
}
|
||||||
_ => Ok(self.get_ics().to_string()),
|
_ => Ok(self.get_ics().to_string()),
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ pub enum CalDateTimeError {
|
|||||||
InvalidDurationFormat(String),
|
InvalidDurationFormat(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deref, PartialEq)]
|
#[derive(Debug, Clone, Deref, PartialEq, Eq, Hash)]
|
||||||
pub struct UtcDateTime(pub DateTime<Utc>);
|
pub struct UtcDateTime(pub DateTime<Utc>);
|
||||||
|
|
||||||
impl ValueDeserialize for UtcDateTime {
|
impl ValueDeserialize for UtcDateTime {
|
||||||
|
|||||||
@@ -28,10 +28,8 @@ rand.workspace = true
|
|||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
rustical_dav.workspace = true
|
rustical_dav.workspace = true
|
||||||
strum.workspace = true
|
|
||||||
strum_macros.workspace = true
|
|
||||||
rustical_ical.workspace = true
|
rustical_ical.workspace = true
|
||||||
rrule = "0.14"
|
rrule.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rstest = { workspace = true }
|
rstest = { workspace = true }
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ impl SqliteCalendarStore {
|
|||||||
.fetch_all(executor)
|
.fetch_all(executor)
|
||||||
.await.map_err(crate::Error::from)?
|
.await.map_err(crate::Error::from)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|row| row.try_into().map_err(rustical_store::Error::from))
|
.map(|row| row.try_into())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,7 +286,7 @@ impl SqliteCalendarStore {
|
|||||||
.await
|
.await
|
||||||
.map_err(crate::Error::from)?
|
.map_err(crate::Error::from)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|row| row.try_into().map_err(rustical_store::Error::from))
|
.map(|row| row.try_into())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use darling::{FromDeriveInput, FromField, FromMeta, FromVariant, util::Flag};
|
use darling::{FromDeriveInput, FromField, FromMeta, FromVariant, util::Flag};
|
||||||
use syn::LitByteStr;
|
use syn::{Ident, LitByteStr};
|
||||||
|
|
||||||
#[derive(Debug, Default, FromMeta, Clone)]
|
#[derive(Debug, Default, FromMeta, Clone)]
|
||||||
pub struct TagAttrs {
|
pub struct TagAttrs {
|
||||||
@@ -16,6 +16,8 @@ pub struct VariantAttrs {
|
|||||||
pub common: TagAttrs,
|
pub common: TagAttrs,
|
||||||
pub other: Flag,
|
pub other: Flag,
|
||||||
pub skip_deserializing: Flag,
|
pub skip_deserializing: Flag,
|
||||||
|
// This is actually only for the PropName trait
|
||||||
|
pub prop: Option<Ident>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, FromDeriveInput, Clone)]
|
#[derive(Default, FromDeriveInput, Clone)]
|
||||||
|
|||||||
@@ -74,13 +74,13 @@ pub fn derive_enum_variants(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(EnumUnitVariants, attributes(xml))]
|
#[proc_macro_derive(PropName, attributes(xml))]
|
||||||
pub fn derive_enum_unit_variants(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_enum_prop_name(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
match &input.data {
|
match &input.data {
|
||||||
syn::Data::Struct(_) => panic!("Struct not supported"),
|
syn::Data::Struct(_) => panic!("Struct not supported"),
|
||||||
syn::Data::Enum(e) => Enum::parse(&input, e).impl_enum_unit_variants(),
|
syn::Data::Enum(e) => Enum::parse(&input, e).impl_enum_prop_name(),
|
||||||
syn::Data::Union(_) => panic!("Union not supported"),
|
syn::Data::Union(_) => panic!("Union not supported"),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ impl Variant {
|
|||||||
) {
|
) {
|
||||||
(_, Fields::Named(_), _) => {
|
(_, Fields::Named(_), _) => {
|
||||||
panic!(
|
panic!(
|
||||||
"struct variants are not supported, please use a tuple variant with a struct"
|
"struct variants are not supported, please use a tuple variant with a struct"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(false, Fields::Unnamed(FieldsUnnamed { unnamed, .. }), true) => {
|
(false, Fields::Unnamed(FieldsUnnamed { unnamed, .. }), true) => {
|
||||||
if unnamed.len() != 1 {
|
if unnamed.len() != 1 {
|
||||||
@@ -165,16 +165,20 @@ impl Variant {
|
|||||||
}
|
}
|
||||||
let field = unnamed.iter().next().unwrap();
|
let field = unnamed.iter().next().unwrap();
|
||||||
quote! {
|
quote! {
|
||||||
if let Ok(val) = <#field as ::rustical_xml::XmlDeserialize>::deserialize(reader, start, empty) {
|
match <#field as ::rustical_xml::XmlDeserialize>::deserialize(reader, start, empty) {
|
||||||
return Ok(Self::#ident(val));
|
Ok(val) => { return Ok(Self::#ident(val)) }
|
||||||
|
Err(::rustical_xml::XmlError::InvalidVariant(..)) => {}
|
||||||
|
Err(err) => { return Err(err) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Fields::Unit => {
|
Fields::Unit => {
|
||||||
quote! {
|
quote! {
|
||||||
// Make sure that content is still consumed
|
// Make sure that content is still consumed
|
||||||
if let Ok(_) = <() as ::rustical_xml::XmlDeserialize>::deserialize(reader, start, empty) {
|
match <() as ::rustical_xml::XmlDeserialize>::deserialize(reader, start, empty) {
|
||||||
return Ok(Self::#ident);
|
Ok(val) => { return Ok(Self::#ident(val)) }
|
||||||
|
Err(::rustical_xml::XmlError::InvalidVariant(..)) => {}
|
||||||
|
Err(err) => { return Err(err) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,400 +0,0 @@
|
|||||||
use super::{attrs::EnumAttrs, Variant};
|
|
||||||
use crate::attrs::VariantAttrs;
|
|
||||||
use core::panic;
|
|
||||||
use darling::{FromDeriveInput, FromVariant};
|
|
||||||
use quote::quote;
|
|
||||||
use syn::{DataEnum, DeriveInput};
|
|
||||||
|
|
||||||
pub struct Enum {
|
|
||||||
attrs: EnumAttrs,
|
|
||||||
variants: Vec<Variant>,
|
|
||||||
ident: syn::Ident,
|
|
||||||
generics: syn::Generics,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Enum {
|
|
||||||
fn impl_de_untagged(&self) -> proc_macro2::TokenStream {
|
|
||||||
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
|
|
||||||
let name = &self.ident;
|
|
||||||
|
|
||||||
let variant_branches = self
|
|
||||||
.variants
|
|
||||||
.iter()
|
|
||||||
.filter_map(|variant| variant.untagged_branch());
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
impl #impl_generics ::rustical_xml::XmlDeserialize for #name #type_generics #where_clause {
|
|
||||||
fn deserialize<R: ::std::io::BufRead>(
|
|
||||||
reader: &mut quick_xml::NsReader<R>,
|
|
||||||
start: &quick_xml::events::BytesStart,
|
|
||||||
empty: bool
|
|
||||||
) -> Result<Self, rustical_xml::XmlError> {
|
|
||||||
#(#variant_branches);*
|
|
||||||
|
|
||||||
Err(rustical_xml::XmlError::InvalidVariant("could not match".to_owned()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn impl_de_tagged(&self) -> proc_macro2::TokenStream {
|
|
||||||
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
|
|
||||||
let name = &self.ident;
|
|
||||||
|
|
||||||
let variant_branches = self.variants.iter().filter_map(Variant::tagged_branch);
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
impl #impl_generics ::rustical_xml::XmlDeserialize for #name #type_generics #where_clause {
|
|
||||||
fn deserialize<R: std::io::BufRead>(
|
|
||||||
reader: &mut quick_xml::NsReader<R>,
|
|
||||||
start: &quick_xml::events::BytesStart,
|
|
||||||
empty: bool
|
|
||||||
) -> Result<Self, rustical_xml::XmlError> {
|
|
||||||
let (_ns, name) = reader.resolve_element(start.name());
|
|
||||||
|
|
||||||
match name.as_ref() {
|
|
||||||
#(#variant_branches),*
|
|
||||||
name => {
|
|
||||||
// Handle invalid variant name
|
|
||||||
Err(rustical_xml::XmlError::InvalidVariant(String::from_utf8_lossy(name).to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn impl_de(&self) -> proc_macro2::TokenStream {
|
|
||||||
match self.attrs.untagged.is_present() {
|
|
||||||
true => self.impl_de_untagged(),
|
|
||||||
false => self.impl_de_tagged(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse(input: &DeriveInput, data: &DataEnum) -> Self {
|
|
||||||
let attrs = EnumAttrs::from_derive_input(input).unwrap();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
variants: data
|
|
||||||
.variants
|
|
||||||
.iter()
|
|
||||||
.map(|variant| Variant {
|
|
||||||
attrs: VariantAttrs::from_variant(variant).unwrap(),
|
|
||||||
variant: variant.to_owned(),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
attrs,
|
|
||||||
ident: input.ident.to_owned(),
|
|
||||||
generics: input.generics.to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn impl_se(&self) -> proc_macro2::TokenStream {
|
|
||||||
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
|
|
||||||
let ident = &self.ident;
|
|
||||||
let enum_untagged = self.attrs.untagged.is_present();
|
|
||||||
let variant_serializers = self.variants.iter().map(Variant::se_branch);
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
impl #impl_generics ::rustical_xml::XmlSerialize for #ident #type_generics #where_clause {
|
|
||||||
fn serialize<W: ::std::io::Write>(
|
|
||||||
&self,
|
|
||||||
ns: Option<::quick_xml::name::Namespace>,
|
|
||||||
tag: Option<&[u8]>,
|
|
||||||
namespaces: &::std::collections::HashMap<::quick_xml::name::Namespace, &[u8]>,
|
|
||||||
writer: &mut ::quick_xml::Writer<W>
|
|
||||||
) -> ::std::io::Result<()> {
|
|
||||||
use ::quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
|
|
||||||
|
|
||||||
let prefix = ns
|
|
||||||
.map(|ns| namespaces.get(&ns))
|
|
||||||
.unwrap_or(None)
|
|
||||||
.map(|prefix| [*prefix, b":"].concat());
|
|
||||||
let has_prefix = prefix.is_some();
|
|
||||||
let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat());
|
|
||||||
let qname = tagname.as_ref().map(|tagname| ::quick_xml::name::QName(tagname));
|
|
||||||
|
|
||||||
const enum_untagged: bool = #enum_untagged;
|
|
||||||
|
|
||||||
if let Some(qname) = &qname {
|
|
||||||
let mut bytes_start = BytesStart::from(qname.to_owned());
|
|
||||||
if !has_prefix {
|
|
||||||
if let Some(ns) = &ns {
|
|
||||||
bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writer.write_event(Event::Start(bytes_start))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#(#variant_serializers);*
|
|
||||||
|
|
||||||
if let Some(qname) = &qname {
|
|
||||||
writer.write_event(Event::End(BytesEnd::from(qname.to_owned())))?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn attributes<'a>(&self) -> Option<Vec<::quick_xml::events::attributes::Attribute<'a>>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn impl_xml_document(&self) -> proc_macro2::TokenStream {
|
|
||||||
if self.attrs.untagged.is_present() {
|
|
||||||
panic!("XmlDocument only supported for untagged enums");
|
|
||||||
}
|
|
||||||
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
|
|
||||||
let ident = &self.ident;
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
impl #impl_generics ::rustical_xml::XmlDocument for #ident #type_generics #where_clause {
|
|
||||||
fn parse<R: ::std::io::BufRead>(mut reader: ::quick_xml::NsReader<R>) -> Result<Self, ::rustical_xml::XmlError>
|
|
||||||
where
|
|
||||||
Self: ::rustical_xml::XmlDeserialize
|
|
||||||
{
|
|
||||||
use ::quick_xml::events::Event;
|
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
loop {
|
|
||||||
let event = reader.read_event_into(&mut buf)?;
|
|
||||||
let empty = matches!(event, Event::Empty(_));
|
|
||||||
|
|
||||||
match event {
|
|
||||||
Event::Start(start) | Event::Empty(start) => {
|
|
||||||
return <Self as ::rustical_xml::XmlDeserialize>::deserialize(&mut reader, &start, empty);
|
|
||||||
}
|
|
||||||
Event::Eof => return Err(::rustical_xml::XmlError::Eof),
|
|
||||||
Event::Text(bytes_text) => {
|
|
||||||
return Err(::rustical_xml::XmlError::UnsupportedEvent("Text"));
|
|
||||||
}
|
|
||||||
Event::CData(cdata) => {
|
|
||||||
return Err(::rustical_xml::XmlError::UnsupportedEvent("CDATA"));
|
|
||||||
}
|
|
||||||
Event::Decl(_) => { /* <?xml ... ?> ignore this */ }
|
|
||||||
Event::Comment(_) => { /* ignore */ }
|
|
||||||
Event::DocType(_) => { /* ignore */ }
|
|
||||||
Event::PI(_) => {
|
|
||||||
return Err(::rustical_xml::XmlError::UnsupportedEvent("Processing instruction"));
|
|
||||||
}
|
|
||||||
Event::End(end) => {
|
|
||||||
unreachable!("Premature end of xml document, should be handled by quick_xml");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn impl_enum_variants(&self) -> proc_macro2::TokenStream {
|
|
||||||
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
|
|
||||||
let ident = &self.ident;
|
|
||||||
|
|
||||||
if self.attrs.untagged.is_present() {
|
|
||||||
let untagged_variants = self.variants.iter().map(|variant| {
|
|
||||||
let ty = &variant.deserializer_type();
|
|
||||||
quote! { #ty::variant_names() }
|
|
||||||
});
|
|
||||||
quote! {
|
|
||||||
impl #impl_generics ::rustical_xml::EnumVariants for #ident #type_generics #where_clause {
|
|
||||||
const TAGGED_VARIANTS: &'static [(Option<::quick_xml::name::Namespace<'static>>, &'static str)] = &[];
|
|
||||||
|
|
||||||
fn variant_names() -> Vec<(Option<::quick_xml::name::Namespace<'static>>, &'static str)> {
|
|
||||||
[
|
|
||||||
#(#untagged_variants),*
|
|
||||||
].concat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let tagged_variants = self.variants.iter().map(|variant| {
|
|
||||||
let ns = match &variant.attrs.common.ns {
|
|
||||||
Some(ns) => quote! { Some(#ns) },
|
|
||||||
None => quote! { None },
|
|
||||||
};
|
|
||||||
let b_xml_name = variant.xml_name().value();
|
|
||||||
let xml_name = String::from_utf8_lossy(&b_xml_name);
|
|
||||||
quote! {(#ns, #xml_name)}
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
impl #impl_generics ::rustical_xml::EnumVariants for #ident #type_generics #where_clause {
|
|
||||||
const TAGGED_VARIANTS: &'static [(Option<::quick_xml::name::Namespace<'static>>, &'static str)] = &[
|
|
||||||
#(#tagged_variants),*
|
|
||||||
];
|
|
||||||
|
|
||||||
fn variant_names() -> Vec<(Option<::quick_xml::name::Namespace<'static>>, &'static str)> {
|
|
||||||
[Self::TAGGED_VARIANTS,].concat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn impl_enum_unit_variants(&self) -> proc_macro2::TokenStream {
|
|
||||||
let unit_enum_ident = self
|
|
||||||
.attrs
|
|
||||||
.unit_variants_ident
|
|
||||||
.as_ref()
|
|
||||||
.expect("unit_variants_ident no set");
|
|
||||||
let ident = &self.ident;
|
|
||||||
|
|
||||||
if self.attrs.untagged.is_present() {
|
|
||||||
let variant_branches: Vec<_> = self
|
|
||||||
.variants
|
|
||||||
.iter()
|
|
||||||
.map(|variant| {
|
|
||||||
let variant_type = variant.deserializer_type();
|
|
||||||
let variant_ident = &variant.variant.ident;
|
|
||||||
quote! {
|
|
||||||
#variant_ident (<#variant_type as ::rustical_xml::EnumUnitVariants>::UnitVariants)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let variant_idents: Vec<_> = self
|
|
||||||
.variants
|
|
||||||
.iter()
|
|
||||||
.map(|variant| &variant.variant.ident)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let unit_to_output_branches = variant_idents.iter().map(|variant_ident| {
|
|
||||||
quote! { #unit_enum_ident::#variant_ident(val) => val.into() }
|
|
||||||
});
|
|
||||||
|
|
||||||
let str_to_unit_branches = self.variants.iter().map(|variant| {
|
|
||||||
let variant_type = variant.deserializer_type();
|
|
||||||
let variant_ident = &variant.variant.ident;
|
|
||||||
quote! {
|
|
||||||
if let Ok(name) = <#variant_type as ::rustical_xml::EnumUnitVariants>::UnitVariants::from_str(val) {
|
|
||||||
return Ok(Self::#variant_ident(name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let from_enum_to_unit_branches = variant_idents.iter().map(|variant_ident| {
|
|
||||||
quote! { #ident::#variant_ident(val) => #unit_enum_ident::#variant_ident(val.into()) }
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum #unit_enum_ident {
|
|
||||||
#(#variant_branches),*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::rustical_xml::EnumUnitVariants for #ident {
|
|
||||||
type UnitVariants = #unit_enum_ident;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<#unit_enum_ident> for (Option<::quick_xml::name::Namespace<'static>>, &'static str) {
|
|
||||||
fn from(val: #unit_enum_ident) -> Self {
|
|
||||||
match val {
|
|
||||||
#(#unit_to_output_branches),*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<#ident> for #unit_enum_ident {
|
|
||||||
fn from(val: #ident) -> Self {
|
|
||||||
match val {
|
|
||||||
#(#from_enum_to_unit_branches),*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::str::FromStr for #unit_enum_ident {
|
|
||||||
type Err = ::rustical_xml::FromStrError;
|
|
||||||
|
|
||||||
fn from_str(val: &str) -> Result<Self, Self::Err> {
|
|
||||||
#(#str_to_unit_branches);*
|
|
||||||
Err(::rustical_xml::FromStrError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let tagged_variants: Vec<_> = self
|
|
||||||
.variants
|
|
||||||
.iter()
|
|
||||||
.filter(|variant| !variant.attrs.other.is_present())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let variant_outputs: Vec<_> = tagged_variants
|
|
||||||
.iter()
|
|
||||||
.map(|variant| {
|
|
||||||
let ns = match &variant.attrs.common.ns {
|
|
||||||
Some(ns) => quote! { Some(#ns) },
|
|
||||||
None => quote! { None },
|
|
||||||
};
|
|
||||||
let b_xml_name = variant.xml_name().value();
|
|
||||||
let xml_name = String::from_utf8_lossy(&b_xml_name);
|
|
||||||
quote! {(#ns, #xml_name)}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let variant_idents: Vec<_> = tagged_variants
|
|
||||||
.iter()
|
|
||||||
.map(|variant| &variant.variant.ident)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let unit_to_output_branches =
|
|
||||||
variant_idents
|
|
||||||
.iter()
|
|
||||||
.zip(&variant_outputs)
|
|
||||||
.map(|(variant_ident, out)| {
|
|
||||||
quote! { #unit_enum_ident::#variant_ident => #out }
|
|
||||||
});
|
|
||||||
|
|
||||||
let from_enum_to_unit_branches = variant_idents.iter().map(|variant_ident| {
|
|
||||||
quote! { #ident::#variant_ident { .. } => #unit_enum_ident::#variant_ident }
|
|
||||||
});
|
|
||||||
|
|
||||||
let str_to_unit_branches = tagged_variants.iter().map(|variant| {
|
|
||||||
let variant_ident = &variant.variant.ident;
|
|
||||||
let b_xml_name = variant.xml_name().value();
|
|
||||||
let xml_name = String::from_utf8_lossy(&b_xml_name);
|
|
||||||
quote! { #xml_name => Ok(#unit_enum_ident::#variant_ident) }
|
|
||||||
});
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum #unit_enum_ident {
|
|
||||||
#(#variant_idents),*
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl ::rustical_xml::EnumUnitVariants for #ident {
|
|
||||||
type UnitVariants = #unit_enum_ident;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<#unit_enum_ident> for (Option<::quick_xml::name::Namespace<'static>>, &'static str) {
|
|
||||||
fn from(val: #unit_enum_ident) -> Self {
|
|
||||||
match val {
|
|
||||||
#(#unit_to_output_branches),*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<#ident> for #unit_enum_ident {
|
|
||||||
fn from(val: #ident) -> Self {
|
|
||||||
match val {
|
|
||||||
#(#from_enum_to_unit_branches),*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::str::FromStr for #unit_enum_ident {
|
|
||||||
type Err = ::rustical_xml::FromStrError;
|
|
||||||
|
|
||||||
fn from_str(val: &str) -> Result<Self, Self::Err> {
|
|
||||||
match val {
|
|
||||||
#(#str_to_unit_branches),*,
|
|
||||||
_ => Err(::rustical_xml::FromStrError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
64
crates/xml/derive/src/xml_enum/impl_de.rs
Normal file
64
crates/xml/derive/src/xml_enum/impl_de.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
use crate::Variant;
|
||||||
|
|
||||||
|
use super::Enum;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
impl Enum {
|
||||||
|
fn impl_de_untagged(&self) -> proc_macro2::TokenStream {
|
||||||
|
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
|
||||||
|
let name = &self.ident;
|
||||||
|
|
||||||
|
let variant_branches = self
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.filter_map(|variant| variant.untagged_branch());
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl #impl_generics ::rustical_xml::XmlDeserialize for #name #type_generics #where_clause {
|
||||||
|
fn deserialize<R: ::std::io::BufRead>(
|
||||||
|
reader: &mut quick_xml::NsReader<R>,
|
||||||
|
start: &quick_xml::events::BytesStart,
|
||||||
|
empty: bool
|
||||||
|
) -> Result<Self, rustical_xml::XmlError> {
|
||||||
|
#(#variant_branches);*
|
||||||
|
|
||||||
|
Err(rustical_xml::XmlError::InvalidVariant("could not match".to_owned()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_de_tagged(&self) -> proc_macro2::TokenStream {
|
||||||
|
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
|
||||||
|
let name = &self.ident;
|
||||||
|
|
||||||
|
let variant_branches = self.variants.iter().filter_map(Variant::tagged_branch);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl #impl_generics ::rustical_xml::XmlDeserialize for #name #type_generics #where_clause {
|
||||||
|
fn deserialize<R: std::io::BufRead>(
|
||||||
|
reader: &mut quick_xml::NsReader<R>,
|
||||||
|
start: &quick_xml::events::BytesStart,
|
||||||
|
empty: bool
|
||||||
|
) -> Result<Self, rustical_xml::XmlError> {
|
||||||
|
let (_ns, name) = reader.resolve_element(start.name());
|
||||||
|
|
||||||
|
match name.as_ref() {
|
||||||
|
#(#variant_branches),*
|
||||||
|
name => {
|
||||||
|
// Handle invalid variant name
|
||||||
|
Err(rustical_xml::XmlError::InvalidVariant(String::from_utf8_lossy(name).to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn impl_de(&self) -> proc_macro2::TokenStream {
|
||||||
|
match self.attrs.untagged.is_present() {
|
||||||
|
true => self.impl_de_untagged(),
|
||||||
|
false => self.impl_de_tagged(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
179
crates/xml/derive/src/xml_enum/impl_prop_name.rs
Normal file
179
crates/xml/derive/src/xml_enum/impl_prop_name.rs
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
use super::Enum;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
impl Enum {
|
||||||
|
pub fn impl_enum_prop_name(&self) -> proc_macro2::TokenStream {
|
||||||
|
let unit_enum_ident = self
|
||||||
|
.attrs
|
||||||
|
.unit_variants_ident
|
||||||
|
.as_ref()
|
||||||
|
.expect("unit_variants_ident no set");
|
||||||
|
let ident = &self.ident;
|
||||||
|
|
||||||
|
if self.attrs.untagged.is_present() {
|
||||||
|
let variant_branches: Vec<_> = self
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.map(|variant| {
|
||||||
|
let variant_type = variant.deserializer_type();
|
||||||
|
let variant_ident = &variant.variant.ident;
|
||||||
|
quote! {
|
||||||
|
#variant_ident (<#variant_type as ::rustical_xml::PropName>::Names)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let variant_idents: Vec<_> = self
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.map(|variant| &variant.variant.ident)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let unit_to_output_branches = variant_idents.iter().map(|variant_ident| {
|
||||||
|
quote! { #unit_enum_ident::#variant_ident(val) => val.into() }
|
||||||
|
});
|
||||||
|
|
||||||
|
let str_to_unit_branches = self.variants.iter().map(|variant| {
|
||||||
|
let variant_type = variant.deserializer_type();
|
||||||
|
let variant_ident = &variant.variant.ident;
|
||||||
|
quote! {
|
||||||
|
if let Ok(name) = <#variant_type as ::rustical_xml::PropName>::Names::from_str(val) {
|
||||||
|
return Ok(Self::#variant_ident(name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let from_enum_to_unit_branches = variant_idents.iter().map(|variant_ident| {
|
||||||
|
quote! { #ident::#variant_ident(val) => #unit_enum_ident::#variant_ident(val.into()) }
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[derive(Clone, Debug, PartialEq, Hash, Eq, ::rustical_xml::XmlDeserialize)]
|
||||||
|
#[xml(untagged)]
|
||||||
|
pub enum #unit_enum_ident {
|
||||||
|
#(#variant_branches),*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::rustical_xml::PropName for #ident {
|
||||||
|
type Names = #unit_enum_ident;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<#unit_enum_ident> for (Option<::quick_xml::name::Namespace<'static>>, &'static str) {
|
||||||
|
fn from(val: #unit_enum_ident) -> Self {
|
||||||
|
match val {
|
||||||
|
#(#unit_to_output_branches),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<#ident> for #unit_enum_ident {
|
||||||
|
fn from(val: #ident) -> Self {
|
||||||
|
match val {
|
||||||
|
#(#from_enum_to_unit_branches),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::str::FromStr for #unit_enum_ident {
|
||||||
|
type Err = ::rustical_xml::FromStrError;
|
||||||
|
|
||||||
|
fn from_str(val: &str) -> Result<Self, Self::Err> {
|
||||||
|
#(#str_to_unit_branches);*
|
||||||
|
Err(::rustical_xml::FromStrError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let tagged_variants: Vec<_> = self
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.filter(|variant| !variant.attrs.other.is_present())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let prop_name_variants = tagged_variants.iter().map(|variant| {
|
||||||
|
let ident = &variant.variant.ident;
|
||||||
|
if let Some(proptype) = &variant.attrs.prop {
|
||||||
|
quote! {#ident(#proptype)}
|
||||||
|
} else {
|
||||||
|
quote! {#ident}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let unit_to_output_branches = tagged_variants.iter().map(|variant| {
|
||||||
|
let ns = match &variant.attrs.common.ns {
|
||||||
|
Some(ns) => quote! { Some(#ns) },
|
||||||
|
None => quote! { None },
|
||||||
|
};
|
||||||
|
let b_xml_name = variant.xml_name().value();
|
||||||
|
let xml_name = String::from_utf8_lossy(&b_xml_name);
|
||||||
|
let out = quote! {(#ns, #xml_name)};
|
||||||
|
|
||||||
|
let ident = &variant.variant.ident;
|
||||||
|
if variant.attrs.prop.is_some() {
|
||||||
|
quote! { #unit_enum_ident::#ident(..) => #out }
|
||||||
|
} else {
|
||||||
|
quote! { #unit_enum_ident::#ident => #out }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let from_enum_to_unit_branches = tagged_variants.iter().map(|variant| {
|
||||||
|
let variant_ident = &variant.variant.ident;
|
||||||
|
if variant.attrs.prop.is_some() {
|
||||||
|
quote! { #ident::#variant_ident { .. } => Self::#variant_ident (Default::default()) }
|
||||||
|
} else {
|
||||||
|
quote! { #ident::#variant_ident { .. } => Self::#variant_ident }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let str_to_unit_branches = tagged_variants.iter().map(|variant| {
|
||||||
|
let ident = &variant.variant.ident;
|
||||||
|
let b_xml_name = variant.xml_name().value();
|
||||||
|
let xml_name = String::from_utf8_lossy(&b_xml_name);
|
||||||
|
if variant.attrs.prop.is_some() {
|
||||||
|
quote! { #xml_name => Ok(Self::#ident (Default::default())) }
|
||||||
|
} else {
|
||||||
|
quote! { #xml_name => Ok(Self::#ident) }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, ::rustical_xml::XmlDeserialize)]
|
||||||
|
pub enum #unit_enum_ident {
|
||||||
|
#(#prop_name_variants),*
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ::rustical_xml::PropName for #ident {
|
||||||
|
type Names = #unit_enum_ident;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<#unit_enum_ident> for (Option<::quick_xml::name::Namespace<'static>>, &'static str) {
|
||||||
|
fn from(val: #unit_enum_ident) -> Self {
|
||||||
|
match val {
|
||||||
|
#(#unit_to_output_branches),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<#ident> for #unit_enum_ident {
|
||||||
|
fn from(val: #ident) -> Self {
|
||||||
|
match val {
|
||||||
|
#(#from_enum_to_unit_branches),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::str::FromStr for #unit_enum_ident {
|
||||||
|
type Err = ::rustical_xml::FromStrError;
|
||||||
|
|
||||||
|
fn from_str(val: &str) -> Result<Self, Self::Err> {
|
||||||
|
match val {
|
||||||
|
#(#str_to_unit_branches),*,
|
||||||
|
_ => Err(::rustical_xml::FromStrError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
crates/xml/derive/src/xml_enum/impl_se.rs
Normal file
59
crates/xml/derive/src/xml_enum/impl_se.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
use crate::Variant;
|
||||||
|
|
||||||
|
use super::Enum;
|
||||||
|
|
||||||
|
impl Enum {
|
||||||
|
pub fn impl_se(&self) -> proc_macro2::TokenStream {
|
||||||
|
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
|
||||||
|
let ident = &self.ident;
|
||||||
|
let enum_untagged = self.attrs.untagged.is_present();
|
||||||
|
let variant_serializers = self.variants.iter().map(Variant::se_branch);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl #impl_generics ::rustical_xml::XmlSerialize for #ident #type_generics #where_clause {
|
||||||
|
fn serialize<W: ::std::io::Write>(
|
||||||
|
&self,
|
||||||
|
ns: Option<::quick_xml::name::Namespace>,
|
||||||
|
tag: Option<&[u8]>,
|
||||||
|
namespaces: &::std::collections::HashMap<::quick_xml::name::Namespace, &[u8]>,
|
||||||
|
writer: &mut ::quick_xml::Writer<W>
|
||||||
|
) -> ::std::io::Result<()> {
|
||||||
|
use ::quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
|
||||||
|
|
||||||
|
let prefix = ns
|
||||||
|
.map(|ns| namespaces.get(&ns))
|
||||||
|
.unwrap_or(None)
|
||||||
|
.map(|prefix| [*prefix, b":"].concat());
|
||||||
|
let has_prefix = prefix.is_some();
|
||||||
|
let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat());
|
||||||
|
let qname = tagname.as_ref().map(|tagname| ::quick_xml::name::QName(tagname));
|
||||||
|
|
||||||
|
const enum_untagged: bool = #enum_untagged;
|
||||||
|
|
||||||
|
if let Some(qname) = &qname {
|
||||||
|
let mut bytes_start = BytesStart::from(qname.to_owned());
|
||||||
|
if !has_prefix {
|
||||||
|
if let Some(ns) = &ns {
|
||||||
|
bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.write_event(Event::Start(bytes_start))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#(#variant_serializers);*
|
||||||
|
|
||||||
|
if let Some(qname) = &qname {
|
||||||
|
writer.write_event(Event::End(BytesEnd::from(qname.to_owned())))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attributes<'a>(&self) -> Option<Vec<::quick_xml::events::attributes::Attribute<'a>>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
129
crates/xml/derive/src/xml_enum/mod.rs
Normal file
129
crates/xml/derive/src/xml_enum/mod.rs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
use super::{Variant, attrs::EnumAttrs};
|
||||||
|
use crate::attrs::VariantAttrs;
|
||||||
|
use core::panic;
|
||||||
|
use darling::{FromDeriveInput, FromVariant};
|
||||||
|
use quote::quote;
|
||||||
|
use syn::{DataEnum, DeriveInput};
|
||||||
|
|
||||||
|
mod impl_de;
|
||||||
|
mod impl_prop_name;
|
||||||
|
mod impl_se;
|
||||||
|
|
||||||
|
pub struct Enum {
|
||||||
|
attrs: EnumAttrs,
|
||||||
|
variants: Vec<Variant>,
|
||||||
|
ident: syn::Ident,
|
||||||
|
generics: syn::Generics,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Enum {
|
||||||
|
pub fn parse(input: &DeriveInput, data: &DataEnum) -> Self {
|
||||||
|
let attrs = EnumAttrs::from_derive_input(input).unwrap();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
variants: data
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.map(|variant| Variant {
|
||||||
|
attrs: VariantAttrs::from_variant(variant).unwrap(),
|
||||||
|
variant: variant.to_owned(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
attrs,
|
||||||
|
ident: input.ident.to_owned(),
|
||||||
|
generics: input.generics.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn impl_xml_document(&self) -> proc_macro2::TokenStream {
|
||||||
|
if self.attrs.untagged.is_present() {
|
||||||
|
panic!("XmlDocument only supported for untagged enums");
|
||||||
|
}
|
||||||
|
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
|
||||||
|
let ident = &self.ident;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl #impl_generics ::rustical_xml::XmlDocument for #ident #type_generics #where_clause {
|
||||||
|
fn parse<R: ::std::io::BufRead>(mut reader: ::quick_xml::NsReader<R>) -> Result<Self, ::rustical_xml::XmlError>
|
||||||
|
where
|
||||||
|
Self: ::rustical_xml::XmlDeserialize
|
||||||
|
{
|
||||||
|
use ::quick_xml::events::Event;
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
loop {
|
||||||
|
let event = reader.read_event_into(&mut buf)?;
|
||||||
|
let empty = matches!(event, Event::Empty(_));
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::Start(start) | Event::Empty(start) => {
|
||||||
|
return <Self as ::rustical_xml::XmlDeserialize>::deserialize(&mut reader, &start, empty);
|
||||||
|
}
|
||||||
|
Event::Eof => return Err(::rustical_xml::XmlError::Eof),
|
||||||
|
Event::Text(bytes_text) => {
|
||||||
|
return Err(::rustical_xml::XmlError::UnsupportedEvent("Text"));
|
||||||
|
}
|
||||||
|
Event::CData(cdata) => {
|
||||||
|
return Err(::rustical_xml::XmlError::UnsupportedEvent("CDATA"));
|
||||||
|
}
|
||||||
|
Event::Decl(_) => { /* <?xml ... ?> ignore this */ }
|
||||||
|
Event::Comment(_) => { /* ignore */ }
|
||||||
|
Event::DocType(_) => { /* ignore */ }
|
||||||
|
Event::PI(_) => {
|
||||||
|
return Err(::rustical_xml::XmlError::UnsupportedEvent("Processing instruction"));
|
||||||
|
}
|
||||||
|
Event::End(end) => {
|
||||||
|
unreachable!("Premature end of xml document, should be handled by quick_xml");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn impl_enum_variants(&self) -> proc_macro2::TokenStream {
|
||||||
|
let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl();
|
||||||
|
let ident = &self.ident;
|
||||||
|
|
||||||
|
if self.attrs.untagged.is_present() {
|
||||||
|
let untagged_variants = self.variants.iter().map(|variant| {
|
||||||
|
let ty = &variant.deserializer_type();
|
||||||
|
quote! { #ty::variant_names() }
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
impl #impl_generics ::rustical_xml::EnumVariants for #ident #type_generics #where_clause {
|
||||||
|
const TAGGED_VARIANTS: &'static [(Option<::quick_xml::name::Namespace<'static>>, &'static str)] = &[];
|
||||||
|
|
||||||
|
fn variant_names() -> Vec<(Option<::quick_xml::name::Namespace<'static>>, &'static str)> {
|
||||||
|
[
|
||||||
|
#(#untagged_variants),*
|
||||||
|
].concat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let tagged_variants = self.variants.iter().map(|variant| {
|
||||||
|
let ns = match &variant.attrs.common.ns {
|
||||||
|
Some(ns) => quote! { Some(#ns) },
|
||||||
|
None => quote! { None },
|
||||||
|
};
|
||||||
|
let b_xml_name = variant.xml_name().value();
|
||||||
|
let xml_name = String::from_utf8_lossy(&b_xml_name);
|
||||||
|
quote! {(#ns, #xml_name)}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl #impl_generics ::rustical_xml::EnumVariants for #ident #type_generics #where_clause {
|
||||||
|
const TAGGED_VARIANTS: &'static [(Option<::quick_xml::name::Namespace<'static>>, &'static str)] = &[
|
||||||
|
#(#tagged_variants),*
|
||||||
|
];
|
||||||
|
|
||||||
|
fn variant_names() -> Vec<(Option<::quick_xml::name::Namespace<'static>>, &'static str)> {
|
||||||
|
[Self::TAGGED_VARIANTS,].concat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
use quick_xml::name::Namespace;
|
use quick_xml::name::Namespace;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::hash::Hash;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
pub mod de;
|
pub mod de;
|
||||||
@@ -17,8 +18,8 @@ pub use se::XmlSerialize;
|
|||||||
pub use se::XmlSerializeRoot;
|
pub use se::XmlSerializeRoot;
|
||||||
pub use unparsed::Unparsed;
|
pub use unparsed::Unparsed;
|
||||||
pub use value::{ParseValueError, ValueDeserialize, ValueSerialize};
|
pub use value::{ParseValueError, ValueDeserialize, ValueSerialize};
|
||||||
pub use xml_derive::EnumUnitVariants;
|
|
||||||
pub use xml_derive::EnumVariants;
|
pub use xml_derive::EnumVariants;
|
||||||
|
pub use xml_derive::PropName;
|
||||||
pub use xml_derive::XmlRootTag;
|
pub use xml_derive::XmlRootTag;
|
||||||
|
|
||||||
pub trait XmlRootTag {
|
pub trait XmlRootTag {
|
||||||
@@ -37,6 +38,12 @@ pub trait EnumVariants {
|
|||||||
fn variant_names() -> Vec<(Option<Namespace<'static>>, &'static str)>;
|
fn variant_names() -> Vec<(Option<Namespace<'static>>, &'static str)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EnumUnitVariants: Sized {
|
pub trait PropName: Sized {
|
||||||
type UnitVariants: Into<(Option<Namespace<'static>>, &'static str)> + From<Self> + FromStr;
|
type Names: Into<(Option<Namespace<'static>>, &'static str)>
|
||||||
|
+ Clone
|
||||||
|
+ From<Self>
|
||||||
|
+ FromStr<Err: std::fmt::Debug>
|
||||||
|
+ Hash
|
||||||
|
+ Eq
|
||||||
|
+ XmlDeserialize;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::str::FromStr;
|
|||||||
|
|
||||||
use quick_xml::name::Namespace;
|
use quick_xml::name::Namespace;
|
||||||
use rustical_xml::EnumVariants;
|
use rustical_xml::EnumVariants;
|
||||||
use xml_derive::EnumUnitVariants;
|
use xml_derive::PropName;
|
||||||
|
|
||||||
pub const NS_DAV: Namespace = Namespace(b"DAV:");
|
pub const NS_DAV: Namespace = Namespace(b"DAV:");
|
||||||
pub const NS_DAVPUSH: Namespace = Namespace(b"https://bitfire.at/webdav-push");
|
pub const NS_DAVPUSH: Namespace = Namespace(b"https://bitfire.at/webdav-push");
|
||||||
@@ -12,13 +12,13 @@ pub const NS_ICAL: Namespace = Namespace(b"http://apple.com/ns/ical/");
|
|||||||
pub const NS_CALENDARSERVER: Namespace = Namespace(b"http://calendarserver.org/ns/");
|
pub const NS_CALENDARSERVER: Namespace = Namespace(b"http://calendarserver.org/ns/");
|
||||||
pub const NS_NEXTCLOUD: Namespace = Namespace(b"http://nextcloud.com/ns");
|
pub const NS_NEXTCLOUD: Namespace = Namespace(b"http://nextcloud.com/ns");
|
||||||
|
|
||||||
#[derive(EnumVariants, EnumUnitVariants)]
|
#[derive(EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "ExtensionsPropName")]
|
#[xml(unit_variants_ident = "ExtensionsPropName")]
|
||||||
enum ExtensionProp {
|
enum ExtensionProp {
|
||||||
Hello,
|
Hello,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(EnumVariants, EnumUnitVariants)]
|
#[derive(EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "CalendarPropName")]
|
#[xml(unit_variants_ident = "CalendarPropName")]
|
||||||
enum CalendarProp {
|
enum CalendarProp {
|
||||||
// WebDAV (RFC 2518)
|
// WebDAV (RFC 2518)
|
||||||
@@ -45,7 +45,7 @@ fn test_enum_tagged_variants() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(EnumVariants, EnumUnitVariants)]
|
#[derive(EnumVariants, PropName)]
|
||||||
#[xml(untagged, unit_variants_ident = "UnionPropName")]
|
#[xml(untagged, unit_variants_ident = "UnionPropName")]
|
||||||
enum UnionProp {
|
enum UnionProp {
|
||||||
Calendar(CalendarProp),
|
Calendar(CalendarProp),
|
||||||
|
|||||||
Reference in New Issue
Block a user