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