Compare commits

...

10 Commits

Author SHA1 Message Date
Lennart
96b63848f0 version 0.9.2 2025-08-30 00:41:50 +02:00
Lennart
16e5cacefe Docker: Target Rust 1.89
fixes #116
2025-08-30 00:21:41 +02:00
Lennart
3819f623a6 update dependencies 2025-08-30 00:20:51 +02:00
Lennart
c4604d4376 xml: Comprehensive refactoring from byte strings to strings 2025-08-28 18:01:41 +02:00
Lennart K
85787e69bc xml: tiny refactoring 2025-08-28 15:24:19 +02:00
Lennart K
43b4150e28 xml: Change ns_prefix from LitByteStr to LitStr 2025-08-28 15:19:27 +02:00
Lennart K
c38fbe004f clippy appeasement 2025-08-28 15:09:01 +02:00
Lennart
bf5d874481 frontend tweaks 2025-08-28 14:53:17 +02:00
Lennart
c648ed315d version 0.9.1 2025-08-25 19:09:48 +02:00
Lennart
2cf481d4e6 make session cookie samesite=lax by default 2025-08-25 19:09:24 +02:00
48 changed files with 269 additions and 312 deletions

176
Cargo.lock generated
View File

@@ -219,9 +219,9 @@ dependencies = [
[[package]]
name = "async-executor"
version = "1.13.2"
version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa"
checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8"
dependencies = [
"async-task",
"concurrent-queue",
@@ -595,9 +595,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.45"
version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318"
checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57"
dependencies = [
"clap_builder",
"clap_derive",
@@ -605,9 +605,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.44"
version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8"
checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41"
dependencies = [
"anstream",
"anstyle",
@@ -1288,7 +1288,7 @@ dependencies = [
"js-sys",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
"wasi 0.14.3+wasi-0.2.4",
"wasm-bindgen",
]
@@ -1567,7 +1567,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite",
"socket2 0.6.0",
"socket2",
"tokio",
"tower-service",
"tracing",
@@ -1911,11 +1911,11 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "matchers"
version = "0.1.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
"regex-automata 0.1.10",
"regex-automata",
]
[[package]]
@@ -1990,12 +1990,11 @@ dependencies = [
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
version = "0.50.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
dependencies = [
"overload",
"winapi",
"windows-sys 0.52.0",
]
[[package]]
@@ -2262,12 +2261,6 @@ dependencies = [
"num-traits",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "p256"
version = "0.13.2"
@@ -2519,9 +2512,9 @@ dependencies = [
[[package]]
name = "potential_utf"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a"
dependencies = [
"zerovec",
]
@@ -2615,9 +2608,9 @@ dependencies = [
[[package]]
name = "quinn"
version = "0.11.8"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8"
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
dependencies = [
"bytes",
"cfg_aliases",
@@ -2626,7 +2619,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls",
"socket2 0.5.10",
"socket2",
"thiserror 2.0.16",
"tokio",
"tracing",
@@ -2635,9 +2628,9 @@ dependencies = [
[[package]]
name = "quinn-proto"
version = "0.11.12"
version = "0.11.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e"
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
dependencies = [
"bytes",
"getrandom 0.3.3",
@@ -2656,16 +2649,16 @@ dependencies = [
[[package]]
name = "quinn-udp"
version = "0.5.13"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2 0.5.10",
"socket2",
"tracing",
"windows-sys 0.59.0",
"windows-sys 0.60.2",
]
[[package]]
@@ -2773,47 +2766,32 @@ dependencies = [
[[package]]
name = "regex"
version = "1.11.1"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.9",
"regex-syntax 0.8.5",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.5",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
[[package]]
name = "relative-path"
@@ -2934,21 +2912,20 @@ dependencies = [
[[package]]
name = "rstest"
version = "0.25.0"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d"
checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49"
dependencies = [
"futures-timer",
"futures-util",
"rstest_macros",
"rustc_version",
]
[[package]]
name = "rstest_macros"
version = "0.25.0"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746"
checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0"
dependencies = [
"cfg-if",
"glob",
@@ -3040,7 +3017,7 @@ dependencies = [
[[package]]
name = "rustical"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"anyhow",
"argon2",
@@ -3083,7 +3060,7 @@ dependencies = [
[[package]]
name = "rustical_caldav"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"async-std",
"async-trait",
@@ -3123,7 +3100,7 @@ dependencies = [
[[package]]
name = "rustical_carddav"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"async-trait",
"axum",
@@ -3155,7 +3132,7 @@ dependencies = [
[[package]]
name = "rustical_dav"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"async-trait",
"axum",
@@ -3180,7 +3157,7 @@ dependencies = [
[[package]]
name = "rustical_dav_push"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"async-trait",
"axum",
@@ -3205,7 +3182,7 @@ dependencies = [
[[package]]
name = "rustical_frontend"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"askama",
"askama_web",
@@ -3238,7 +3215,7 @@ dependencies = [
[[package]]
name = "rustical_ical"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"axum",
"chrono",
@@ -3256,7 +3233,7 @@ dependencies = [
[[package]]
name = "rustical_oidc"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"async-trait",
"axum",
@@ -3271,7 +3248,7 @@ dependencies = [
[[package]]
name = "rustical_store"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"anyhow",
"async-trait",
@@ -3305,7 +3282,7 @@ dependencies = [
[[package]]
name = "rustical_store_sqlite"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"async-trait",
"chrono",
@@ -3326,7 +3303,7 @@ dependencies = [
[[package]]
name = "rustical_xml"
version = "0.9.0"
version = "0.9.2"
dependencies = [
"quick-xml",
"thiserror 2.0.16",
@@ -3652,16 +3629,6 @@ dependencies = [
"serde",
]
[[package]]
name = "socket2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "socket2"
version = "0.6.0"
@@ -4096,7 +4063,7 @@ dependencies = [
"pin-project-lite",
"signal-hook-registry",
"slab",
"socket2 0.6.0",
"socket2",
"tokio-macros",
"tracing",
"windows-sys 0.59.0",
@@ -4443,14 +4410,14 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.19"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex",
"regex-automata",
"sharded-slab",
"smallvec",
"thread_local",
@@ -4623,11 +4590,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
version = "0.14.3+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95"
dependencies = [
"wit-bindgen-rt",
"wit-bindgen",
]
[[package]]
@@ -4746,22 +4713,6 @@ dependencies = [
"wasite",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.10"
@@ -4771,12 +4722,6 @@ dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.61.2"
@@ -5068,13 +5013,10 @@ dependencies = [
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
name = "wit-bindgen"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
[[package]]
name = "writeable"

View File

@@ -2,7 +2,7 @@
members = ["crates/*"]
[workspace.package]
version = "0.9.0"
version = "0.9.2"
edition = "2024"
description = "A CalDAV server"
repository = "https://github.com/lennart-k/rustical"
@@ -48,7 +48,7 @@ rand_core = { version = "0.9", features = ["std"] }
chrono = { version = "0.4", features = ["serde"] }
regex = "1.10"
lazy_static = "1.5"
rstest = "0.25"
rstest = "0.26"
rstest_reuse = "0.7"
sha2 = "0.10"
tokio = { version = "1", features = [

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM rust:1.88-alpine AS chef
FROM --platform=$BUILDPLATFORM rust:1.89-alpine AS chef
ARG TARGETPLATFORM
ARG BUILDPLATFORM

View File

@@ -46,7 +46,7 @@ pub struct PropElement {
}
#[derive(XmlDeserialize, XmlRootTag, Clone, Debug)]
#[xml(root = b"mkcalendar")]
#[xml(root = "mkcalendar")]
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
struct MkcalendarRequest {
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
@@ -54,7 +54,7 @@ struct MkcalendarRequest {
}
#[derive(XmlDeserialize, XmlRootTag, Clone, Debug)]
#[xml(root = b"mkcol")]
#[xml(root = "mkcol")]
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
struct MkcolRequest {
#[xml(ns = "rustical_dav::namespace::NS_DAV")]

View File

@@ -225,12 +225,12 @@ impl Resource for CalendarResource {
}
CalendarProp::TimezoneServiceSet(_) => Err(rustical_dav::Error::PropReadOnly),
CalendarProp::CalendarTimezoneId(timezone_id) => {
if let Some(tzid) = &timezone_id {
if !vtimezones_rs::VTIMEZONES.contains_key(tzid) {
return Err(rustical_dav::Error::BadRequest(format!(
"Invalid timezone-id: {tzid}"
)));
}
if let Some(tzid) = &timezone_id
&& !vtimezones_rs::VTIMEZONES.contains_key(tzid)
{
return Err(rustical_dav::Error::BadRequest(format!(
"Invalid timezone-id: {tzid}"
)));
}
self.cal.timezone_id = timezone_id;
Ok(())

View File

@@ -16,13 +16,13 @@ pub enum PrincipalProp {
CalendarUserAddressSet(HrefElement),
// WebDAV Access Control (RFC 3744)
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"principal-URL")]
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = "principal-URL")]
PrincipalUrl(HrefElement),
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
GroupMembership(GroupMembership),
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
GroupMemberSet(GroupMemberSet),
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"alternate-URI-set")]
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = "alternate-URI-set")]
AlternateUriSet,
// #[xml(ns = "rustical_dav::namespace::NS_DAV")]
// PrincipalCollectionSet(HrefElement),

View File

@@ -22,7 +22,7 @@ pub struct MkcolAddressbookProp {
resourcetype: Option<Resourcetype>,
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
displayname: Option<String>,
#[xml(rename = b"addressbook-description")]
#[xml(rename = "addressbook-description")]
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
description: Option<String>,
}
@@ -34,7 +34,7 @@ pub struct PropElement<T: XmlDeserialize> {
}
#[derive(XmlDeserialize, XmlRootTag, Clone, Debug, PartialEq)]
#[xml(root = b"mkcol")]
#[xml(root = "mkcol")]
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
struct MkcolRequest {
#[xml(ns = "rustical_dav::namespace::NS_DAV")]

View File

@@ -8,14 +8,14 @@ use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
#[xml(unit_variants_ident = "PrincipalPropName")]
pub enum PrincipalProp {
// WebDAV Access Control (RFC 3744)
#[xml(rename = b"principal-URL")]
#[xml(rename = "principal-URL")]
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
PrincipalUrl(HrefElement),
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
GroupMembership(GroupMembership),
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
GroupMemberSet(GroupMemberSet),
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"alternate-URI-set")]
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = "alternate-URI-set")]
AlternateUriSet,
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
PrincipalCollectionSet(HrefElement),

View File

@@ -20,13 +20,13 @@ impl XmlSerialize for UserPrivilegeSet {
fn serialize(
&self,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
tag: Option<&str>,
namespaces: &HashMap<Namespace, &str>,
writer: &mut quick_xml::Writer<&mut Vec<u8>>,
) -> std::io::Result<()> {
#[derive(XmlSerialize)]
pub struct FakeUserPrivilegeSet {
#[xml(rename = b"privilege", flatten)]
#[xml(rename = "privilege", flatten)]
privileges: Vec<UserPrivilege>,
}

View File

@@ -60,11 +60,11 @@ pub async fn route_delete<R: ResourceService>(
return Err(crate::Error::PreconditionFailed.into());
}
}
if let Some(if_none_match) = if_none_match {
if resource.satisfies_if_none_match(&if_none_match) {
// Precondition failed
return Err(crate::Error::PreconditionFailed.into());
}
if let Some(if_none_match) = if_none_match
&& resource.satisfies_if_none_match(&if_none_match)
{
// Precondition failed
return Err(crate::Error::PreconditionFailed.into());
}
resource_service
.delete_resource(path_components, !no_trash)

View File

@@ -57,7 +57,7 @@ enum Operation<T: XmlDeserialize> {
}
#[derive(XmlDeserialize, XmlRootTag, Clone, Debug)]
#[xml(root = b"propertyupdate")]
#[xml(root = "propertyupdate")]
#[xml(ns = "crate::namespace::NS_DAV")]
struct PropertyupdateElement<T: XmlDeserialize>(#[xml(ty = "untagged", flatten)] Vec<Operation<T>>);

View File

@@ -1,12 +1,12 @@
use rustical_xml::{XmlRootTag, XmlSerialize};
#[derive(XmlSerialize, XmlRootTag)]
#[xml(ns = "crate::namespace::NS_DAV", root = b"error")]
#[xml(ns = "crate::namespace::NS_DAV", root = "error")]
#[xml(ns_prefix(
crate::namespace::NS_DAV = b"",
crate::namespace::NS_CARDDAV = b"CARD",
crate::namespace::NS_CALDAV = b"CAL",
crate::namespace::NS_CALENDARSERVER = b"CS",
crate::namespace::NS_DAVPUSH = b"PUSH"
crate::namespace::NS_DAV = "",
crate::namespace::NS_CARDDAV = "CARD",
crate::namespace::NS_CALDAV = "CAL",
crate::namespace::NS_CALENDARSERVER = "CS",
crate::namespace::NS_DAVPUSH = "PUSH"
))]
pub struct ErrorElement<'t, T: XmlSerialize>(#[xml(ty = "untagged")] pub &'t T);

View File

@@ -22,8 +22,8 @@ pub struct PropstatElement<PropType: XmlSerialize> {
fn xml_serialize_status(
status: &StatusCode,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
tag: Option<&str>,
namespaces: &HashMap<Namespace, &str>,
writer: &mut quick_xml::Writer<&mut Vec<u8>>,
) -> std::io::Result<()> {
XmlSerialize::serialize(&format!("HTTP/1.1 {}", status), ns, tag, namespaces, writer)
@@ -40,13 +40,13 @@ pub enum PropstatWrapper<T: XmlSerialize> {
// <!ELEMENT response (href, ((href*, status)|(propstat+)),
// responsedescription?) >
#[derive(XmlSerialize, XmlRootTag)]
#[xml(ns = "crate::namespace::NS_DAV", root = b"response")]
#[xml(ns = "crate::namespace::NS_DAV", root = "response")]
#[xml(ns_prefix(
crate::namespace::NS_DAV = b"",
crate::namespace::NS_CARDDAV = b"CARD",
crate::namespace::NS_CALDAV = b"CAL",
crate::namespace::NS_CALENDARSERVER = b"CS",
crate::namespace::NS_DAVPUSH = b"PUSH"
crate::namespace::NS_DAV = "",
crate::namespace::NS_CARDDAV = "CARD",
crate::namespace::NS_CALDAV = "CAL",
crate::namespace::NS_CALENDARSERVER = "CS",
crate::namespace::NS_DAVPUSH = "PUSH"
))]
pub struct ResponseElement<PropstatType: XmlSerialize> {
pub href: String,
@@ -59,8 +59,8 @@ pub struct ResponseElement<PropstatType: XmlSerialize> {
fn xml_serialize_optional_status(
val: &Option<StatusCode>,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
tag: Option<&str>,
namespaces: &HashMap<Namespace, &str>,
writer: &mut quick_xml::Writer<&mut Vec<u8>>,
) -> std::io::Result<()> {
XmlSerialize::serialize(
@@ -86,18 +86,18 @@ impl<PT: XmlSerialize> Default for ResponseElement<PT> {
// <!ELEMENT multistatus (response+, responsedescription?) >
// Extended by sync-token as specified in RFC 6578
#[derive(XmlSerialize, XmlRootTag)]
#[xml(root = b"multistatus", ns = "crate::namespace::NS_DAV")]
#[xml(root = "multistatus", ns = "crate::namespace::NS_DAV")]
#[xml(ns_prefix(
crate::namespace::NS_DAV = b"",
crate::namespace::NS_CARDDAV = b"CARD",
crate::namespace::NS_CALDAV = b"CAL",
crate::namespace::NS_CALENDARSERVER = b"CS",
crate::namespace::NS_DAVPUSH = b"PUSH"
crate::namespace::NS_DAV = "",
crate::namespace::NS_CARDDAV = "CARD",
crate::namespace::NS_CALDAV = "CAL",
crate::namespace::NS_CALENDARSERVER = "CS",
crate::namespace::NS_DAVPUSH = "PUSH"
))]
pub struct MultistatusElement<PropType: XmlSerialize, MemberPropType: XmlSerialize> {
#[xml(rename = b"response", flatten)]
#[xml(rename = "response", flatten)]
pub responses: Vec<ResponseElement<PropType>>,
#[xml(rename = b"response", flatten)]
#[xml(rename = "response", flatten)]
pub member_responses: Vec<ResponseElement<MemberPropType>>,
pub sync_token: Option<String>,
}

View File

@@ -7,7 +7,7 @@ use rustical_xml::XmlError;
use rustical_xml::XmlRootTag;
#[derive(Debug, Clone, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"propfind", ns = "crate::namespace::NS_DAV")]
#[xml(root = "propfind", ns = "crate::namespace::NS_DAV")]
pub struct PropfindElement<PN: XmlDeserialize> {
#[xml(ty = "untagged")]
pub prop: PropfindType<PN>,

View File

@@ -16,7 +16,7 @@ mod tests {
use super::{Resourcetype, ResourcetypeInner};
#[derive(XmlSerialize, XmlRootTag)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
resourcetype: Resourcetype,
}

View File

@@ -60,7 +60,7 @@ pub struct NresultsElement(#[xml(ty = "text")] u64);
// <!ELEMENT sync-collection (sync-token, sync-level, limit?, prop)>
// <!-- DAV:limit defined in RFC 5323, Section 5.17 -->
// <!-- DAV:prop defined in RFC 4918, Section 14.18 -->
#[xml(ns = "crate::namespace::NS_DAV", root = b"sync-collection")]
#[xml(ns = "crate::namespace::NS_DAV", root = "sync-collection")]
pub struct SyncCollectionRequest<PN: XmlDeserialize> {
#[xml(ns = "crate::namespace::NS_DAV")]
pub sync_token: String,

View File

@@ -13,8 +13,8 @@ impl XmlSerialize for TagList {
fn serialize(
&self,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
tag: Option<&str>,
namespaces: &HashMap<Namespace, &str>,
writer: &mut quick_xml::Writer<&mut Vec<u8>>,
) -> std::io::Result<()> {
let prefix = ns
@@ -22,16 +22,16 @@ impl XmlSerialize for TagList {
.unwrap_or(None)
.map(|prefix| {
if !prefix.is_empty() {
[*prefix, b":"].concat()
format!("{prefix}:")
} else {
Vec::new()
String::new()
}
});
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));
.map(|tagname| ::quick_xml::name::QName(tagname.as_bytes()));
if let Some(qname) = &qname {
let mut bytes_start = BytesStart::from(qname.to_owned());

View File

@@ -25,10 +25,10 @@ pub struct ContentUpdate {
}
#[derive(XmlSerialize, XmlRootTag, Debug)]
#[xml(root = b"push-message", ns = "rustical_dav::namespace::NS_DAVPUSH")]
#[xml(root = "push-message", ns = "rustical_dav::namespace::NS_DAVPUSH")]
#[xml(ns_prefix(
rustical_dav::namespace::NS_DAVPUSH = b"",
rustical_dav::namespace::NS_DAV = b"D",
rustical_dav::namespace::NS_DAVPUSH = "",
rustical_dav::namespace::NS_DAV = "D",
))]
struct PushMessage {
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]

View File

@@ -35,12 +35,12 @@ pub enum Trigger {
#[derive(XmlSerialize, XmlDeserialize, PartialEq, Clone, Debug)]
pub struct ContentUpdate(
#[xml(rename = b"depth", ns = "rustical_dav::namespace::NS_DAV")] pub Depth,
#[xml(rename = "depth", ns = "rustical_dav::namespace::NS_DAV")] pub Depth,
);
#[derive(XmlSerialize, PartialEq, Clone, Debug)]
pub struct PropertyUpdate(
#[xml(rename = b"depth", ns = "rustical_dav::namespace::NS_DAV")] pub Depth,
#[xml(rename = "depth", ns = "rustical_dav::namespace::NS_DAV")] pub Depth,
);
impl XmlDeserialize for PropertyUpdate {
@@ -51,8 +51,8 @@ impl XmlDeserialize for PropertyUpdate {
) -> Result<Self, rustical_xml::XmlError> {
#[derive(XmlDeserialize, PartialEq, Clone, Debug)]
struct FakePropertyUpdate(
#[xml(rename = b"depth", ns = "rustical_dav::namespace::NS_DAV")] pub Depth,
#[xml(rename = b"prop", ns = "rustical_dav::namespace::NS_DAV")] pub Unparsed,
#[xml(rename = "depth", ns = "rustical_dav::namespace::NS_DAV")] pub Depth,
#[xml(rename = "prop", ns = "rustical_dav::namespace::NS_DAV")] pub Unparsed,
);
let FakePropertyUpdate(depth, _) = FakePropertyUpdate::deserialize(reader, start, empty)?;
Ok(Self(depth))

View File

@@ -17,7 +17,7 @@ pub struct WebPushSubscription {
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
pub struct SubscriptionPublicKey {
#[xml(ty = "attr", rename = b"type")]
#[xml(ty = "attr", rename = "type")]
pub ty: String,
#[xml(ty = "text")]
pub key: String,
@@ -33,7 +33,7 @@ pub struct SubscriptionElement {
pub struct TriggerElement(#[xml(ty = "untagged", flatten)] Vec<Trigger>);
#[derive(XmlDeserialize, XmlRootTag, Clone, Debug, PartialEq)]
#[xml(root = b"push-register")]
#[xml(root = "push-register")]
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub struct PushRegister {
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]

View File

@@ -30,7 +30,7 @@ export class EditAddressbookForm extends LitElement {
return html`
<button @click=${() => this.dialog.value.showModal()}>Edit addressbook</button>
<dialog ${ref(this.dialog)}>
<h3>Create addressbook</h3>
<h3>Edit addressbook</h3>
<form @submit=${this.submit} ${ref(this.form)}>
<label>
Displayname

View File

@@ -42,7 +42,7 @@ export class EditCalendarForm extends LitElement {
return html`
<button @click=${() => this.dialog.value.showModal()}>Edit calendar</button>
<dialog ${ref(this.dialog)}>
<h3>Create calendar</h3>
<h3>Edit calendar</h3>
<form @submit=${this.submit} ${ref(this.form)}>
<label>
Displayname

View File

@@ -29,7 +29,7 @@ let EditAddressbookForm = class extends i {
return x`
<button @click=${() => this.dialog.value.showModal()}>Edit addressbook</button>
<dialog ${n(this.dialog)}>
<h3>Create addressbook</h3>
<h3>Edit addressbook</h3>
<form @submit=${this.submit} ${n(this.form)}>
<label>
Displayname

View File

@@ -30,7 +30,7 @@ let EditCalendarForm = class extends i {
return x`
<button @click=${() => this.dialog.value.showModal()}>Edit calendar</button>
<dialog ${n(this.dialog)}>
<h3>Create calendar</h3>
<h3>Edit calendar</h3>
<form @submit=${this.submit} ${n(this.form)}>
<label>
Displayname

View File

@@ -290,6 +290,7 @@ ul.collection-list {
.color-chip {
background: var(--color);
grid-area: color-chip;
margin-left: 8px;
}
.actions {
@@ -346,6 +347,17 @@ select {
}
}
form {
input[type="text"],
input[type="password"],
input[type="color"],
textarea,
select {
width: 100%;
}
}
svg.icon {
stroke-width: 2px;
color: var(--text-on-background-color);

View File

@@ -1,11 +1,11 @@
use std::collections::HashMap;
use darling::{FromDeriveInput, FromField, FromMeta, FromVariant, util::Flag};
use syn::{Ident, LitByteStr};
use syn::{Ident, LitStr};
#[derive(Debug, Default, FromMeta, Clone)]
pub struct TagAttrs {
pub rename: Option<LitByteStr>,
pub rename: Option<LitStr>,
pub ns: Option<syn::Path>,
}
@@ -30,10 +30,10 @@ pub struct EnumAttrs {
#[derive(Default, FromDeriveInput, Clone)]
#[darling(attributes(xml))]
pub struct StructAttrs {
pub root: Option<LitByteStr>,
pub root: Option<LitStr>,
pub ns: Option<syn::Path>,
#[darling(default)]
pub ns_prefix: HashMap<syn::Path, LitByteStr>,
pub ns_prefix: HashMap<syn::Path, LitStr>,
pub allow_invalid: Flag,
}

View File

@@ -35,7 +35,7 @@ impl Field {
}
/// Field name in XML
pub fn xml_name(&self) -> syn::LitByteStr {
pub fn xml_name(&self) -> syn::LitStr {
if let Some(rename) = self.attrs.common.rename.to_owned() {
rename
} else {
@@ -43,7 +43,7 @@ impl Field {
.field_ident()
.as_ref()
.expect("unnamed tag fields need a rename attribute");
syn::LitByteStr::new(ident.to_string().to_kebab_case().as_bytes(), ident.span())
syn::LitStr::new(ident.to_string().to_kebab_case().as_str(), ident.span())
}
}
@@ -174,6 +174,8 @@ impl Field {
.map(|ns| quote! { if ns == #ns });
let field_name = self.xml_name();
let b_field_name =
syn::LitByteStr::new(self.xml_name().value().as_bytes(), field_name.span());
let builder_field_ident = self.builder_field_ident();
let deserializer = self.deserializer_type();
let value = quote! { <#deserializer as rustical_xml::XmlDeserialize>::deserialize(reader, &start, empty)? };
@@ -186,7 +188,7 @@ impl Field {
};
Some(quote! {
(#namespace_match, #field_name) #namespace_condition => { #assignment; }
(#namespace_match, #b_field_name) #namespace_condition => { #assignment; }
})
}
@@ -231,6 +233,8 @@ impl Field {
}
let builder_field_ident = self.builder_field_ident();
let field_name = self.xml_name();
let b_field_name =
syn::LitByteStr::new(self.xml_name().value().as_bytes(), field_name.span());
let value = wrap_option_if_no_default(
quote! {
@@ -240,7 +244,7 @@ impl Field {
);
Some(quote! {
#field_name => {
#b_field_name => {
builder.#builder_field_ident = #value;
}
})
@@ -255,7 +259,6 @@ impl Field {
let value = quote! {
if let ::quick_xml::name::ResolveResult::Bound(ns) = &ns {
Some(ns.into())
// Some(rustical_xml::ValueDeserialize::deserialize(&String::from_utf8_lossy(ns.0.as_ref()))?)
} else {
None
}

View File

@@ -1,16 +1,12 @@
pub(crate) fn get_generic_type(ty: &syn::Type) -> Option<&syn::Type> {
if let syn::Type::Path(syn::TypePath { path, .. }) = ty {
if let Some(seg) = path.segments.last() {
if let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
args,
..
}) = &seg.arguments
{
if let Some(syn::GenericArgument::Type(t)) = &args.first() {
return Some(t);
}
}
}
if let syn::Type::Path(syn::TypePath { path, .. }) = ty
&& let Some(seg) = path.segments.last()
&& let syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
args, ..
}) = &seg.arguments
&& let Some(syn::GenericArgument::Type(t)) = &args.first()
{
return Some(t);
}
None
}

View File

@@ -14,13 +14,13 @@ impl Variant {
&self.variant.ident
}
pub fn xml_name(&self) -> syn::LitByteStr {
pub fn xml_name(&self) -> syn::LitStr {
self.attrs
.common
.rename
.to_owned()
.unwrap_or(syn::LitByteStr::new(
self.ident().to_string().to_kebab_case().as_bytes(),
.unwrap_or(syn::LitStr::new(
self.ident().to_string().to_kebab_case().as_str(),
self.ident().span(),
))
}
@@ -75,6 +75,8 @@ impl Variant {
}
let ident = self.ident();
let variant_name = self.xml_name();
let b_variant_name =
syn::LitByteStr::new(self.xml_name().value().as_bytes(), variant_name.span());
let deserializer_type = self.deserializer_type();
Some(
@@ -93,7 +95,7 @@ impl Variant {
panic!("tuple variants should contain exactly one element");
}
quote! {
#variant_name => {
#b_variant_name => {
let val = Some(<#deserializer_type as ::rustical_xml::XmlDeserialize>::deserialize(reader, start, empty)?);
Ok(Self::#ident(val))
}
@@ -104,7 +106,7 @@ impl Variant {
panic!("tuple variants should contain exactly one element");
}
quote! {
#variant_name => {
#b_variant_name => {
let val = <#deserializer_type as ::rustical_xml::XmlDeserialize>::deserialize(reader, start, empty)?;
Ok(Self::#ident(val))
}
@@ -112,7 +114,7 @@ impl Variant {
}
(false, Fields::Unit, _) => {
quote! {
#variant_name => {
#b_variant_name => {
// Make sure that content is still consumed
<() as ::rustical_xml::XmlDeserialize>::deserialize(reader, start, empty)?;
Ok(Self::#ident)

View File

@@ -111,8 +111,7 @@ impl Enum {
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 xml_name = variant.xml_name().value();
let out = quote! {(#ns, #xml_name)};
let ident = &variant.variant.ident;
@@ -134,8 +133,7 @@ impl Enum {
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);
let xml_name = variant.xml_name().value();
if variant.attrs.prop.is_some() {
quote! { #xml_name => Ok(Self::#ident (Default::default())) }
} else {

View File

@@ -16,8 +16,8 @@ impl Enum {
fn serialize(
&self,
ns: Option<::quick_xml::name::Namespace>,
tag: Option<&[u8]>,
namespaces: &::std::collections::HashMap<::quick_xml::name::Namespace, &[u8]>,
tag: Option<&str>,
namespaces: &::std::collections::HashMap<::quick_xml::name::Namespace, &str>,
writer: &mut ::quick_xml::Writer<&mut Vec<u8>>
) -> ::std::io::Result<()> {
use ::quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
@@ -25,14 +25,16 @@ impl Enum {
let prefix = ns
.map(|ns| namespaces.get(&ns))
.unwrap_or(None)
.map(|prefix| if !prefix.is_empty() {
[*prefix, b":"].concat()
} else {
vec![]
});
.map(|prefix| {
if !prefix.is_empty() {
format!("{prefix}:")
} else {
String::new()
}
});
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));
let qname = tagname.as_ref().map(|tagname| ::quick_xml::name::QName(tagname.as_bytes()));
const enum_untagged: bool = #enum_untagged;

View File

@@ -108,8 +108,7 @@ impl Enum {
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 xml_name = variant.xml_name().value();
quote! {(#ns, #xml_name)}
});

View File

@@ -33,7 +33,7 @@ impl NamedStruct {
let field_index = field.target_field_index();
quote! {
::quick_xml::events::attributes::Attribute {
key: ::quick_xml::name::QName(#field_name),
key: ::quick_xml::name::QName(#field_name.as_bytes()),
value: ::std::borrow::Cow::from(::rustical_xml::ValueSerialize::serialize(&self.#field_index).into_bytes())
}
}
@@ -47,7 +47,7 @@ impl NamedStruct {
let field_index = field.target_field_index();
quote! {
let tag_str = self.#field_index.to_string();
let tag = Some(tag.unwrap_or(tag_str.as_bytes()));
let tag = Some(tag.unwrap_or(tag_str.as_str()));
}
});
@@ -70,13 +70,12 @@ impl NamedStruct {
.ns_prefix
.iter()
.map(|(ns, prefix)| {
let sep = if !prefix.value().is_empty() {
b":".to_vec()
let attr_name = if prefix.value().is_empty() {
"xmlns".to_owned()
} else {
b"".to_vec()
format!("xmlns:{}", prefix.value())
};
let attr_name = [b"xmlns".as_ref(), &sep, &prefix.value()].concat();
let a = syn::LitByteStr::new(&attr_name, prefix.span());
let a = syn::LitByteStr::new(attr_name.as_bytes(), prefix.span());
quote! {
bytes_start.push_attribute((#a.as_ref(), #ns.as_ref()));
}
@@ -91,8 +90,8 @@ impl NamedStruct {
fn serialize(
&self,
ns: Option<::quick_xml::name::Namespace>,
tag: Option<&[u8]>,
namespaces: &::std::collections::HashMap<::quick_xml::name::Namespace, &[u8]>,
tag: Option<&str>,
namespaces: &::std::collections::HashMap<::quick_xml::name::Namespace, &str>,
writer: &mut ::quick_xml::Writer<&mut Vec<u8>>
) -> ::std::io::Result<()> {
use ::quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
@@ -105,15 +104,15 @@ impl NamedStruct {
.unwrap_or(None)
.map(|prefix| {
if !prefix.is_empty() {
[*prefix, b":"].concat()
format!("{prefix}:")
} else {
Vec::new()
String::new()
}
});
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));
//
let qname = tagname.as_ref().map(|tagname| ::quick_xml::name::QName(tagname.as_bytes()));
if let Some(qname) = &qname {
let mut bytes_start = BytesStart::from(qname.to_owned());
if !has_prefix {

View File

@@ -68,7 +68,7 @@ impl NamedStruct {
.ns_prefix
.iter()
.map(|(ns, prefix)| {
quote! { (#ns, #prefix.as_ref()) }
quote! { (#ns, #prefix) }
})
.collect()
} else {
@@ -77,9 +77,9 @@ impl NamedStruct {
quote! {
impl #impl_generics ::rustical_xml::XmlRootTag for #ident #type_generics #where_clause {
fn root_tag() -> &'static [u8] { #root }
fn root_tag() -> &'static str { #root }
fn root_ns() -> Option<::quick_xml::name::Namespace<'static>> { #ns }
fn root_ns_prefixes() -> ::std::collections::HashMap<::quick_xml::name::Namespace<'static>, &'static [u8]> {
fn root_ns_prefixes() -> ::std::collections::HashMap<::quick_xml::name::Namespace<'static>, &'static str> {
::std::collections::HashMap::from_iter(vec![
#(#prefixes),*
])

View File

@@ -49,7 +49,7 @@ impl<T: XmlRootTag + XmlDeserialize> XmlDocument for T {
let (ns, name) = reader.resolve_element(start.name());
let matches = match (Self::root_ns(), &ns, name) {
// Wrong tag
(_, _, name) if name.as_ref() != Self::root_tag() => false,
(_, _, name) if name.as_ref() != Self::root_tag().as_bytes() => false,
// Wrong namespace
(Some(root_ns), ns, _) if &ResolveResult::Bound(root_ns) != ns => false,
_ => true,
@@ -60,7 +60,7 @@ impl<T: XmlRootTag + XmlDeserialize> XmlDocument for T {
format!("{ns:?}"),
String::from_utf8_lossy(name.as_ref()).to_string(),
format!("{root_ns:?}"),
String::from_utf8_lossy(Self::root_tag()).to_string(),
Self::root_tag().to_owned(),
));
};

View File

@@ -23,9 +23,9 @@ pub use xml_derive::PropName;
pub use xml_derive::XmlRootTag;
pub trait XmlRootTag {
fn root_tag() -> &'static [u8];
fn root_tag() -> &'static str;
fn root_ns() -> Option<Namespace<'static>>;
fn root_ns_prefixes() -> HashMap<Namespace<'static>, &'static [u8]>;
fn root_ns_prefixes() -> HashMap<Namespace<'static>, &'static str>;
}
#[derive(Debug)]

View File

@@ -28,7 +28,7 @@ impl<'a> From<&'a Namespace<'a>> for NamespaceOwned {
}
impl NamespaceOwned {
pub fn as_ref(&self) -> Namespace {
pub fn as_ref(&self) -> Namespace<'_> {
Namespace(&self.0)
}
}

View File

@@ -10,8 +10,8 @@ pub trait XmlSerialize {
fn serialize(
&self,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
tag: Option<&str>,
namespaces: &HashMap<Namespace, &str>,
writer: &mut quick_xml::Writer<&mut Vec<u8>>,
) -> std::io::Result<()>;
@@ -22,8 +22,8 @@ impl<T: XmlSerialize> XmlSerialize for Option<T> {
fn serialize(
&self,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
tag: Option<&str>,
namespaces: &HashMap<Namespace, &str>,
writer: &mut quick_xml::Writer<&mut Vec<u8>>,
) -> std::io::Result<()> {
if let Some(some) = self {
@@ -60,8 +60,8 @@ impl XmlSerialize for () {
fn serialize(
&self,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
tag: Option<&str>,
namespaces: &HashMap<Namespace, &str>,
writer: &mut quick_xml::Writer<&mut Vec<u8>>,
) -> std::io::Result<()> {
let prefix = ns
@@ -69,20 +69,18 @@ impl XmlSerialize for () {
.unwrap_or(None)
.map(|prefix| {
if !prefix.is_empty() {
[*prefix, b":"].concat()
[*prefix, ":"].concat()
} else {
Vec::new()
String::new()
}
});
let has_prefix = prefix.is_some();
let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat());
let qname = tagname.as_ref().map(|tagname| QName(tagname));
let qname = tagname.as_ref().map(|tagname| QName(tagname.as_bytes()));
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()));
}
if !has_prefix && let Some(ns) = &ns {
bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref()));
}
writer.write_event(Event::Empty(bytes_start))?;
}

View File

@@ -107,8 +107,8 @@ impl<T: ValueSerialize> XmlSerialize for T {
fn serialize(
&self,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
tag: Option<&str>,
namespaces: &HashMap<Namespace, &str>,
writer: &mut quick_xml::Writer<&mut Vec<u8>>,
) -> std::io::Result<()> {
let prefix = ns
@@ -116,20 +116,18 @@ impl<T: ValueSerialize> XmlSerialize for T {
.unwrap_or(None)
.map(|prefix| {
if !prefix.is_empty() {
[*prefix, b":"].concat()
[*prefix, ":"].concat()
} else {
Vec::new()
String::new()
}
});
let has_prefix = prefix.is_some();
let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat());
let qname = tagname.as_ref().map(|tagname| QName(tagname));
let qname = tagname.as_ref().map(|tagname| QName(tagname.as_bytes()));
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()));
}
if !has_prefix && let Some(ns) = &ns {
bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref()));
}
writer.write_event(Event::Start(bytes_start))?;
}

View File

@@ -1,9 +1,9 @@
use rustical_xml::{de::XmlDocument, XmlDeserialize, XmlRootTag};
use rustical_xml::{XmlDeserialize, XmlRootTag, de::XmlDocument};
#[test]
fn test_struct_tagged_enum() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"propfind")]
#[xml(root = "propfind")]
struct Propfind {
prop: Prop,
}
@@ -58,7 +58,7 @@ fn test_struct_tagged_enum() {
#[test]
fn test_tagged_enum_complex() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"propfind")]
#[xml(root = "propfind")]
struct Propfind {
prop: PropStruct,
}
@@ -116,7 +116,7 @@ fn test_enum_document() {
#[test]
fn test_untagged_enum() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
prop: PropElement,
}

View File

@@ -6,7 +6,7 @@ use std::collections::HashSet;
#[test]
fn test_struct_text_field() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(ty = "text")]
text: String,
@@ -27,7 +27,7 @@ fn test_struct_text_field() {
#[test]
fn test_struct_document() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
child: Child,
}
@@ -52,9 +52,9 @@ fn test_struct_document() {
#[test]
fn test_struct_rename_field() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(rename = b"ok-wow")]
#[xml(rename = "ok-wow")]
child: Child,
}
@@ -78,7 +78,7 @@ fn test_struct_rename_field() {
#[test]
fn test_struct_optional_field() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
child: Option<Child>,
}
@@ -96,9 +96,9 @@ fn test_struct_optional_field() {
#[test]
fn test_struct_vec() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(rename = b"child", flatten)]
#[xml(rename = "child", flatten)]
children: Vec<Child>,
}
@@ -124,9 +124,9 @@ fn test_struct_vec() {
#[test]
fn test_struct_set() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(rename = b"child", flatten)]
#[xml(rename = "child", flatten)]
children: HashSet<Child>,
}
@@ -154,7 +154,7 @@ fn test_struct_ns() {
const NS_HELLO: Namespace = Namespace(b"hello");
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(ns = "NS_HELLO")]
child: (),
@@ -169,7 +169,7 @@ fn test_struct_attr() {
const NS_HELLO: Namespace = Namespace(b"hello");
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(ns = "NS_HELLO")]
child: (),
@@ -196,7 +196,7 @@ fn test_struct_attr() {
#[test]
fn test_struct_generics() {
#[derive(XmlDeserialize, XmlRootTag)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document<T: XmlDeserialize> {
#[allow(dead_code)]
child: T,
@@ -217,7 +217,7 @@ fn test_struct_generics() {
#[test]
fn test_struct_unparsed() {
#[derive(XmlDeserialize, XmlRootTag)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[allow(dead_code)]
child: Unparsed,
@@ -238,7 +238,7 @@ fn test_struct_unparsed() {
#[test]
fn test_xml_values() {
#[derive(XmlDeserialize, XmlRootTag, PartialEq, Debug)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
href: String,
}
@@ -262,7 +262,7 @@ fn test_xml_values() {
#[test]
fn test_xml_cdata() {
#[derive(XmlDeserialize, XmlRootTag, PartialEq, Debug)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(ty = "text")]
hello: String,
@@ -293,7 +293,7 @@ fn test_xml_cdata() {
#[test]
fn test_struct_xml_decl() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
child: Child,
}
@@ -323,7 +323,7 @@ fn test_struct_xml_decl() {
#[test]
fn test_struct_tuple() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
child: Child,
}
@@ -348,7 +348,7 @@ fn test_struct_tuple() {
#[test]
fn test_struct_untagged_ns() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(ty = "untagged")]
child: Child,

View File

@@ -29,7 +29,7 @@ enum CalendarProp {
#[allow(dead_code)]
Getcontenttype(&'static str),
#[xml(ns = "NS_DAV", rename = b"principal-URL")]
#[xml(ns = "NS_DAV", rename = "principal-URL")]
#[allow(dead_code)]
PrincipalUrl,
#[allow(dead_code)]

View File

@@ -36,7 +36,7 @@ fn test_propertyupdate() {
}
#[derive(XmlDeserialize, XmlRootTag)]
#[xml(root = b"propertyupdate")]
#[xml(root = "propertyupdate")]
struct PropertyupdateElement<T: XmlDeserialize> {
#[xml(ty = "untagged", flatten)]
#[allow(dead_code)]

View File

@@ -3,7 +3,7 @@ use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot};
#[test]
fn test_struct_value_tagged() {
#[derive(Debug, XmlRootTag, XmlSerialize, PartialEq)]
#[xml(root = b"propfind")]
#[xml(root = "propfind")]
struct Document {
prop: Prop,
}

View File

@@ -7,7 +7,7 @@ use xml_derive::XmlDeserialize;
#[test]
fn test_struct_document() {
#[derive(Debug, XmlRootTag, XmlSerialize, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
child: Child,
}
@@ -30,7 +30,7 @@ fn test_struct_document() {
#[test]
fn test_struct_untagged_attr() {
#[derive(Debug, XmlRootTag, XmlSerialize, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(ty = "attr")]
name: String,
@@ -57,7 +57,7 @@ fn test_struct_untagged_attr() {
#[test]
fn test_struct_value_tagged() {
#[derive(Debug, XmlRootTag, XmlSerialize, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
href: String,
num: usize,
@@ -82,7 +82,7 @@ fn test_struct_value_tagged() {
#[test]
fn test_struct_value_untagged() {
#[derive(Debug, XmlRootTag, XmlSerialize, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(ty = "untagged")]
href: String,
@@ -103,7 +103,7 @@ fn test_struct_value_untagged() {
#[test]
fn test_struct_vec() {
#[derive(Debug, XmlRootTag, XmlSerialize, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(flatten)]
href: Vec<String>,
@@ -127,7 +127,7 @@ fn test_struct_vec() {
#[test]
fn test_struct_serialize_with() {
#[derive(Debug, XmlRootTag, XmlSerialize, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(serialize_with = "serialize_href")]
href: String,
@@ -136,8 +136,8 @@ fn test_struct_serialize_with() {
fn serialize_href(
val: &str,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
tag: Option<&str>,
namespaces: &HashMap<Namespace, &str>,
writer: &mut Writer<&mut Vec<u8>>,
) -> std::io::Result<()> {
val.to_uppercase().serialize(ns, tag, namespaces, writer)
@@ -160,7 +160,7 @@ fn test_struct_serialize_with() {
#[test]
fn test_struct_tag_list() {
#[derive(Debug, XmlRootTag, XmlSerialize, XmlDeserialize, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(ty = "untagged", flatten)]
tags: Vec<Tag>,
@@ -194,9 +194,9 @@ fn test_struct_ns() {
const NS: Namespace = quick_xml::name::Namespace(b"NS:TEST:");
#[derive(Debug, XmlRootTag, XmlSerialize)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
#[xml(ns = "NS", rename = b"okay")]
#[xml(ns = "NS", rename = "okay")]
child: String,
}
@@ -210,7 +210,7 @@ fn test_struct_ns() {
#[test]
fn test_struct_tuple() {
#[derive(Debug, XmlRootTag, XmlSerialize, PartialEq)]
#[xml(root = b"document")]
#[xml(root = "document")]
struct Document {
child: Child,
}
@@ -230,8 +230,8 @@ fn test_tuple_struct() {
const NS: Namespace = quick_xml::name::Namespace(b"NS:TEST:");
#[derive(Debug, XmlRootTag, XmlSerialize)]
#[xml(root = b"document")]
struct Document(#[xml(ns = "NS", rename = b"okay")] String);
#[xml(root = "document")]
struct Document(#[xml(ns = "NS", rename = "okay")] String);
Document("hello!".to_string())
.serialize_to_string()

View File

@@ -38,6 +38,7 @@ pub fn make_app<AS: AddressbookStore, CS: CalendarStore, S: SubscriptionStore>(
oidc_config: Option<OidcConfig>,
nextcloud_login_config: NextcloudLoginConfig,
dav_push_enabled: bool,
session_cookie_samesite_strict: bool,
) -> Router<()> {
let combined_cal_store = Arc::new(CombinedCalendarStore::new(
cal_store.clone(),
@@ -128,7 +129,11 @@ pub fn make_app<AS: AddressbookStore, CS: CalendarStore, S: SubscriptionStore>(
SessionManagerLayer::new(session_store)
.with_name("rustical_session")
.with_secure(true)
.with_same_site(SameSite::Strict)
.with_same_site(if session_cookie_samesite_strict {
SameSite::Strict
} else {
SameSite::Lax
})
.with_expiry(Expiry::OnInactivity(
tower_sessions::cookie::time::Duration::hours(2),
)),

View File

@@ -7,6 +7,7 @@ use serde::{Deserialize, Serialize};
pub struct HttpConfig {
pub host: String,
pub port: u16,
pub session_cookie_samesite_strict: bool,
}
impl Default for HttpConfig {
@@ -14,6 +15,7 @@ impl Default for HttpConfig {
Self {
host: "0.0.0.0".to_owned(),
port: 4000,
session_cookie_samesite_strict: false,
}
}
}

View File

@@ -116,6 +116,7 @@ async fn main() -> Result<()> {
config.oidc.clone(),
config.nextcloud_login.clone(),
config.dav_push.enabled,
config.http.session_cookie_samesite_strict,
);
let app = ServiceExt::<Request>::into_make_service(
NormalizePathLayer::trim_trailing_slash().layer(app),