dav: Fix proppatch supporting multiple properties in <set> and <remove> elements

This commit is contained in:
Lennart
2025-07-18 20:59:37 +02:00
parent 34839aa2ed
commit 16f9ce6f38

View File

@@ -26,21 +26,21 @@ enum SetPropertyPropWrapper<T: XmlDeserialize> {
// We are <prop>
#[derive(XmlDeserialize, Clone, Debug)]
struct SetPropertyPropWrapperWrapper<T: XmlDeserialize>(
#[xml(ty = "untagged")] SetPropertyPropWrapper<T>,
#[xml(ty = "untagged", flatten)] Vec<SetPropertyPropWrapper<T>>,
);
// We are <set>
#[derive(XmlDeserialize, Clone, Debug)]
struct SetPropertyElement<T: XmlDeserialize> {
#[xml(ns = "crate::namespace::NS_DAV")]
prop: T,
prop: SetPropertyPropWrapperWrapper<T>,
}
#[derive(XmlDeserialize, Clone, Debug)]
struct TagName(#[xml(ty = "tag_name")] String);
#[derive(XmlDeserialize, Clone, Debug)]
struct PropertyElement(#[xml(ty = "untagged")] TagName);
struct PropertyElement(#[xml(ty = "untagged", flatten)] Vec<TagName>);
#[derive(XmlDeserialize, Clone, Debug)]
struct RemovePropertyElement {
@@ -81,9 +81,8 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
let href = path.to_owned();
// Extract operations
let PropertyupdateElement::<SetPropertyPropWrapperWrapper<<R::Resource as Resource>::Prop>>(
operations,
) = XmlDocument::parse_str(body).map_err(Error::XmlError)?;
let PropertyupdateElement::<<R::Resource as Resource>::Prop>(operations) =
XmlDocument::parse_str(body).map_err(Error::XmlError)?;
let mut resource = resource_service
.get_resource(path_components, false)
@@ -100,17 +99,17 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
for operation in operations.into_iter() {
match operation {
Operation::Set(SetPropertyElement {
prop: SetPropertyPropWrapperWrapper(property),
prop: SetPropertyPropWrapperWrapper(properties),
}) => {
for property in properties {
match property {
SetPropertyPropWrapper::Valid(prop) => {
let propname: <<R::Resource as Resource>::Prop as PropName>::Names =
prop.clone().into();
let (ns, propname): (Option<Namespace>, &str) = propname.into();
match resource.set_prop(prop) {
Ok(()) => {
props_ok.push((ns.map(NamespaceOwned::from), propname.to_owned()))
}
Ok(()) => props_ok
.push((ns.map(NamespaceOwned::from), propname.to_owned())),
Err(Error::PropReadOnly) => props_conflict
.push((ns.map(NamespaceOwned::from), propname.to_owned())),
Err(err) => return Err(err.into()),
@@ -139,9 +138,12 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
}
}
}
}
Operation::Remove(remove_el) => {
let propname = remove_el.prop.0.0;
match <<R::Resource as Resource>::Prop as PropName>::Names::from_str(&propname) {
for tagname in remove_el.prop.0 {
let propname = tagname.0;
match <<R::Resource as Resource>::Prop as PropName>::Names::from_str(&propname)
{
Ok(prop) => match resource.remove_prop(&prop) {
Ok(()) => props_ok.push((None, propname)),
Err(Error::PropReadOnly) => props_conflict.push({
@@ -156,6 +158,7 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
}
}
}
}
if props_not_found.is_empty() && props_conflict.is_empty() {
// Only save if no errors occured