mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 16:32:29 +00:00
migrating propstat responses to serde
This commit is contained in:
@@ -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) => {
|
prop_responses.push(response);
|
||||||
writer
|
}
|
||||||
.write_serializable(valid_prop.tagname(), &response)
|
Err(_) => invalid_props.push(prop),
|
||||||
.map_err(|_e| quick_xml::Error::TextNotFound)?;
|
|
||||||
}
|
|
||||||
Err(_) => invalid_props.push(prop),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
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())
|
Ok(std::str::from_utf8(&output_buffer)?.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()?;
|
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>(())
|
el.end()
|
||||||
})?;
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_propstat_element<F, W: Write>(
|
|
||||||
writer: &mut Writer<W>,
|
|
||||||
status: StatusCode,
|
|
||||||
prop_closure: F,
|
|
||||||
) -> Result<(), quick_xml::Error>
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Writer<W>) -> 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 <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>
|
||||||
|
|||||||
Reference in New Issue
Block a user