migrating propstat responses to serde

This commit is contained in:
Lennart
2024-03-14 21:40:38 +01:00
parent b21586d077
commit a7f4c2ad65
2 changed files with 71 additions and 91 deletions

View File

@@ -7,10 +7,7 @@ use serde::Serialize;
use std::str::FromStr; use std::str::FromStr;
use strum::{EnumProperty, VariantNames}; use strum::{EnumProperty, VariantNames};
use crate::{ use crate::xml_snippets::TagList;
tagname::TagName,
xml_snippets::{write_invalid_props_response, write_propstat_response},
};
// A resource is identified by a URI and has properties // A resource is identified by a URI and has properties
// A resource can also be a collection // A resource can also be a collection
@@ -39,6 +36,26 @@ pub trait Resource: Sized {
fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse>; fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse>;
} }
#[derive(Serialize)]
struct PropWrapper<T: Serialize> {
#[serde(rename = "$value")]
prop: T,
}
#[derive(Serialize)]
#[serde(rename_all = "kebab-case")]
struct PropstatElement<T: Serialize> {
prop: T,
status: String,
}
#[derive(Serialize)]
#[serde(rename_all = "kebab-case")]
struct PropstatResponseElement<T: Serialize> {
href: String,
propstat: PropstatElement<T>,
}
pub trait HandlePropfind { pub trait HandlePropfind {
fn propfind(&self, props: Vec<&str>) -> Result<String>; fn propfind(&self, props: Vec<&str>) -> Result<String>;
} }
@@ -59,25 +76,45 @@ impl<R: Resource> HandlePropfind for R {
let mut output_buffer = Vec::new(); let mut output_buffer = Vec::new();
let mut writer = Writer::new_with_indent(&mut output_buffer, b' ', 2); let mut writer = Writer::new_with_indent(&mut output_buffer, b' ', 2);
write_propstat_response(&mut writer, self.get_path(), StatusCode::OK, |writer| { let mut prop_responses = Vec::new();
for prop in props { for prop in props {
if let Ok(valid_prop) = R::PropType::from_str(prop) { if let Ok(valid_prop) = R::PropType::from_str(prop) {
// TODO: Fix error types
match self.get_prop(valid_prop.clone()) { match self.get_prop(valid_prop.clone()) {
Ok(response) => { Ok(response) => {
writer prop_responses.push(response);
.write_serializable(valid_prop.tagname(), &response)
.map_err(|_e| quick_xml::Error::TextNotFound)?;
} }
Err(_) => invalid_props.push(prop), Err(_) => invalid_props.push(prop),
}; }
} else { } else {
invalid_props.push(prop); invalid_props.push(prop);
} }
} }
Ok(())
})?; writer.write_serializable(
write_invalid_props_response(&mut writer, self.get_path(), invalid_props)?; "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()) Ok(std::str::from_utf8(&output_buffer)?.to_string())
} }
} }

View File

@@ -1,11 +1,6 @@
use std::io::Write;
use actix_web::http::StatusCode;
use anyhow::Result; use anyhow::Result;
use quick_xml::{ use quick_xml::{events::attributes::Attribute, Writer};
events::{attributes::Attribute, BytesText}, use serde::ser::SerializeMap;
Writer,
};
use serde::Serialize; use serde::Serialize;
#[derive(Serialize)] #[derive(Serialize)]
@@ -21,71 +16,19 @@ impl HrefElement {
#[derive(Serialize)] #[derive(Serialize)]
pub struct TextNode(pub Option<String>); pub struct TextNode(pub Option<String>);
pub fn write_invalid_props_response<W: Write>( pub struct TagList(pub Vec<String>);
writer: &mut Writer<W>,
href: &str,
invalid_props: Vec<&str>,
) -> Result<(), quick_xml::Error> {
if invalid_props.is_empty() {
return Ok(());
};
write_propstat_response(writer, href, StatusCode::NOT_FOUND, |writer| { impl Serialize for TagList {
for prop in invalid_props { fn serialize<S>(&self, serializer: S) -> std::prelude::v1::Result<S::Ok, S::Error>
writer.create_element(prop).write_empty()?;
}
Ok::<(), quick_xml::Error>(())
})?;
Ok(())
}
pub fn write_propstat_element<F, W: Write>(
writer: &mut Writer<W>,
status: StatusCode,
prop_closure: F,
) -> Result<(), quick_xml::Error>
where where
F: FnOnce(&mut Writer<W>) -> Result<(), quick_xml::Error>, S: serde::Serializer,
{ {
writer let mut el = serializer.serialize_map(Some(self.0.len()))?;
.create_element("propstat") for tag in &self.0 {
.write_inner_content(|writer| { el.serialize_entry(&tag, &())?;
writer }
.create_element("prop") el.end()
.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 <prop> element
pub fn write_propstat_response<F, W: Write>(
writer: &mut Writer<W>,
href: &str,
status: StatusCode,
prop_closure: F,
) -> Result<(), quick_xml::Error>
where
F: FnOnce(&mut Writer<W>) -> 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(())
} }
pub fn generate_multistatus<'a, F, A>(namespaces: A, closure: F) -> Result<String> pub fn generate_multistatus<'a, F, A>(namespaces: A, closure: F) -> Result<String>