diff --git a/crates/dav/src/resource.rs b/crates/dav/src/resource.rs index 22d2685..c9989d2 100644 --- a/crates/dav/src/resource.rs +++ b/crates/dav/src/resource.rs @@ -7,10 +7,7 @@ use serde::Serialize; use std::str::FromStr; use strum::{EnumProperty, VariantNames}; -use crate::{ - tagname::TagName, - xml_snippets::{write_invalid_props_response, write_propstat_response}, -}; +use crate::xml_snippets::TagList; // A resource is identified by a URI and has properties // A resource can also be a collection @@ -39,6 +36,26 @@ pub trait Resource: Sized { fn get_prop(&self, prop: Self::PropType) -> Result; } +#[derive(Serialize)] +struct PropWrapper { + #[serde(rename = "$value")] + prop: T, +} + +#[derive(Serialize)] +#[serde(rename_all = "kebab-case")] +struct PropstatElement { + prop: T, + status: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "kebab-case")] +struct PropstatResponseElement { + href: String, + propstat: PropstatElement, +} + pub trait HandlePropfind { fn propfind(&self, props: Vec<&str>) -> Result; } @@ -59,25 +76,45 @@ impl HandlePropfind for R { let mut output_buffer = Vec::new(); let mut writer = Writer::new_with_indent(&mut output_buffer, b' ', 2); - write_propstat_response(&mut writer, self.get_path(), StatusCode::OK, |writer| { - for prop in props { - if let Ok(valid_prop) = R::PropType::from_str(prop) { - // TODO: Fix error types - match self.get_prop(valid_prop.clone()) { - Ok(response) => { - writer - .write_serializable(valid_prop.tagname(), &response) - .map_err(|_e| quick_xml::Error::TextNotFound)?; - } - Err(_) => invalid_props.push(prop), - }; - } else { - invalid_props.push(prop); + let mut prop_responses = Vec::new(); + for prop in props { + if let Ok(valid_prop) = R::PropType::from_str(prop) { + match self.get_prop(valid_prop.clone()) { + Ok(response) => { + prop_responses.push(response); + } + Err(_) => invalid_props.push(prop), } + } else { + invalid_props.push(prop); } - Ok(()) - })?; - write_invalid_props_response(&mut writer, self.get_path(), invalid_props)?; + } + + writer.write_serializable( + "response", + &PropstatResponseElement { + href: self.get_path().to_owned(), + propstat: PropstatElement { + status: format!("HTTP/1.1 {}", StatusCode::OK), + prop: PropWrapper { + prop: prop_responses, + }, + }, + }, + )?; + if !invalid_props.is_empty() { + // TODO: proper error reporting + writer.write_serializable( + "response", + &PropstatResponseElement { + href: self.get_path().to_owned(), + propstat: PropstatElement { + status: format!("HTTP/1.1 {}", StatusCode::NOT_FOUND), + prop: TagList(invalid_props.iter().map(|&s| s.to_owned()).collect()), + }, + }, + )?; + } Ok(std::str::from_utf8(&output_buffer)?.to_string()) } } diff --git a/crates/dav/src/xml_snippets.rs b/crates/dav/src/xml_snippets.rs index f6485f8..4f3612d 100644 --- a/crates/dav/src/xml_snippets.rs +++ b/crates/dav/src/xml_snippets.rs @@ -1,11 +1,6 @@ -use std::io::Write; - -use actix_web::http::StatusCode; use anyhow::Result; -use quick_xml::{ - events::{attributes::Attribute, BytesText}, - Writer, -}; +use quick_xml::{events::attributes::Attribute, Writer}; +use serde::ser::SerializeMap; use serde::Serialize; #[derive(Serialize)] @@ -21,71 +16,19 @@ impl HrefElement { #[derive(Serialize)] pub struct TextNode(pub Option); -pub fn write_invalid_props_response( - writer: &mut Writer, - href: &str, - invalid_props: Vec<&str>, -) -> Result<(), quick_xml::Error> { - if invalid_props.is_empty() { - return Ok(()); - }; +pub struct TagList(pub Vec); - write_propstat_response(writer, href, StatusCode::NOT_FOUND, |writer| { - for prop in invalid_props { - writer.create_element(prop).write_empty()?; +impl Serialize for TagList { + fn serialize(&self, serializer: S) -> std::prelude::v1::Result + where + S: serde::Serializer, + { + let mut el = serializer.serialize_map(Some(self.0.len()))?; + for tag in &self.0 { + el.serialize_entry(&tag, &())?; } - Ok::<(), quick_xml::Error>(()) - })?; - - Ok(()) -} - -pub fn write_propstat_element( - writer: &mut Writer, - status: StatusCode, - prop_closure: F, -) -> Result<(), quick_xml::Error> -where - F: FnOnce(&mut Writer) -> Result<(), quick_xml::Error>, -{ - writer - .create_element("propstat") - .write_inner_content(|writer| { - writer - .create_element("prop") - .write_inner_content(prop_closure)?; - - writer - .create_element("status") - .write_text_content(BytesText::new(&format!("HTTP/1.1 {}", status)))?; - Ok::<(), quick_xml::Error>(()) - })?; - Ok(()) -} - -// Writes a propstat response into a multistatus -// closure hooks into the element -pub fn write_propstat_response( - writer: &mut Writer, - href: &str, - status: StatusCode, - prop_closure: F, -) -> Result<(), quick_xml::Error> -where - F: FnOnce(&mut Writer) -> Result<(), quick_xml::Error>, -{ - writer - .create_element("response") - .write_inner_content(|writer| { - writer - .create_element("href") - .write_text_content(BytesText::new(href))?; - - write_propstat_element(writer, status, prop_closure)?; - - Ok::<(), quick_xml::Error>(()) - })?; - Ok(()) + el.end() + } } pub fn generate_multistatus<'a, F, A>(namespaces: A, closure: F) -> Result