xml: namespace serialization

This commit is contained in:
Lennart
2024-12-31 18:20:02 +01:00
parent 61e2dab37f
commit 098e374e4d
20 changed files with 215 additions and 133 deletions

View File

@@ -1,47 +1,9 @@
use quick_xml::events::attributes::Attribute;
use quick_xml::name::Namespace;
// An enum keeping track of the XML namespaces used for WebDAV and its extensions
//
// Can also generate appropriate attributes for quick_xml
pub enum Namespace {
Dav,
DavPush,
CalDAV,
CardDAV,
ICal,
CServer,
Nextcloud,
}
impl Namespace {
pub fn as_str(&self) -> &'static str {
match self {
Self::Dav => "DAV:",
Self::DavPush => "DAV:Push",
Self::CalDAV => "urn:ietf:params:xml:ns:caldav",
Self::CardDAV => "urn:ietf:params:xml:ns:carddav",
Self::ICal => "http://apple.com/ns/ical/",
Self::CServer => "http://calendarserver.org/ns/",
Self::Nextcloud => "http://nextcloud.com/ns",
}
}
// Returns an opinionated namespace attribute name
pub fn xml_attr(&self) -> &'static str {
match self {
Self::Dav => "xmlns",
Self::DavPush => "xmlns:P",
Self::CalDAV => "xmlns:C",
Self::CardDAV => "xmlns:CARD",
Self::ICal => "xmlns:IC",
Self::CServer => "xmlns:CS",
Self::Nextcloud => "xmlns:NEXTC",
}
}
}
impl From<Namespace> for Attribute<'static> {
fn from(value: Namespace) -> Self {
(value.xml_attr(), value.as_str()).into()
}
}
pub const NS_DAV: Namespace = Namespace(b"DAV:");
pub const NS_DAVPUSH: Namespace = Namespace(b"DAV:Push");
pub const NS_CALDAV: Namespace = Namespace(b"urn:ietf:params:xml:ns:caldav");
pub const NS_CARDDAV: Namespace = Namespace(b"urn:ietf:params:xml:ns:carddav");
pub const NS_ICAL: Namespace = Namespace(b"http://apple.com/ns/ical/");
pub const NS_CALENDARSERVER: Namespace = Namespace(b"http://calendarserver.org/ns/");
pub const NS_NEXTCLOUD: Namespace = Namespace(b"http://nextcloud.com/ns");

View File

@@ -1,5 +1,6 @@
use quick_xml::name::Namespace;
use rustical_xml::{XmlDeserialize, XmlSerialize};
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, XmlSerialize, XmlDeserialize, Eq, Hash, PartialEq)]
pub enum UserPrivilege {
@@ -16,8 +17,9 @@ pub enum UserPrivilege {
impl XmlSerialize for UserPrivilegeSet {
fn serialize<W: std::io::Write>(
&self,
ns: Option<&[u8]>,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
writer: &mut quick_xml::Writer<W>,
) -> std::io::Result<()> {
#[derive(XmlSerialize)]
@@ -29,7 +31,7 @@ impl XmlSerialize for UserPrivilegeSet {
FakeUserPrivilegeSet {
privileges: self.privileges.iter().cloned().collect(),
}
.serialize(ns, tag, writer)
.serialize(ns, tag, namespaces, writer)
}
#[allow(refining_impl_trait)]

View File

@@ -1,9 +1,12 @@
use crate::{namespace::Namespace, xml::TagList};
use std::collections::HashMap;
use crate::xml::TagList;
use actix_web::{
body::BoxBody,
http::{header::ContentType, StatusCode},
HttpRequest, HttpResponse, Responder, ResponseError,
};
use quick_xml::name::Namespace;
use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot};
// Intermediate struct because of a serde limitation, see following article:
@@ -25,11 +28,12 @@ pub struct PropstatElement<PropType: XmlSerialize> {
fn xml_serialize_status<W: ::std::io::Write>(
status: &StatusCode,
ns: Option<&[u8]>,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
writer: &mut quick_xml::Writer<W>,
) -> std::io::Result<()> {
XmlSerialize::serialize(&format!("HTTP/1.1 {}", status), ns, tag, writer)
XmlSerialize::serialize(&format!("HTTP/1.1 {}", status), ns, tag, namespaces, writer)
}
#[derive(XmlSerialize)]
@@ -53,14 +57,16 @@ pub struct ResponseElement<PropstatType: XmlSerialize> {
fn xml_serialize_optional_status<W: ::std::io::Write>(
val: &Option<StatusCode>,
ns: Option<&[u8]>,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
writer: &mut quick_xml::Writer<W>,
) -> std::io::Result<()> {
XmlSerialize::serialize(
&val.map(|status| format!("HTTP/1.1 {}", status)),
ns,
tag,
namespaces,
writer,
)
}
@@ -86,12 +92,12 @@ pub struct MultistatusElement<PropType: XmlSerialize, MemberPropType: XmlSeriali
#[xml(rename = b"response", flatten)]
pub member_responses: Vec<ResponseElement<MemberPropType>>,
// TODO: napespaces
pub ns_dav: &'static str,
pub ns_davpush: &'static str,
pub ns_caldav: &'static str,
pub ns_ical: &'static str,
pub ns_calendarserver: &'static str,
pub ns_carddav: &'static str,
// pub ns_dav: &'static str,
// pub ns_davpush: &'static str,
// pub ns_caldav: &'static str,
// pub ns_ical: &'static str,
// pub ns_calendarserver: &'static str,
// pub ns_carddav: &'static str,
pub sync_token: Option<String>,
}
@@ -100,12 +106,12 @@ impl<T1: XmlSerialize, T2: XmlSerialize> Default for MultistatusElement<T1, T2>
Self {
responses: vec![],
member_responses: vec![],
ns_dav: Namespace::Dav.as_str(),
ns_davpush: Namespace::DavPush.as_str(),
ns_caldav: Namespace::CalDAV.as_str(),
ns_ical: Namespace::ICal.as_str(),
ns_calendarserver: Namespace::CServer.as_str(),
ns_carddav: Namespace::CardDAV.as_str(),
// ns_dav: Namespace::Dav.as_str(),
// ns_davpush: Namespace::DavPush.as_str(),
// ns_caldav: Namespace::CalDAV.as_str(),
// ns_ical: Namespace::ICal.as_str(),
// ns_calendarserver: Namespace::CServer.as_str(),
// ns_carddav: Namespace::CardDAV.as_str(),
sync_token: None,
}
}

View File

@@ -2,7 +2,7 @@ use rustical_xml::XmlDeserialize;
use rustical_xml::XmlRootTag;
#[derive(Debug, Clone, XmlDeserialize, XmlRootTag, PartialEq)]
#[xml(root = b"propfind", ns = b"DAV:")]
#[xml(root = b"propfind", ns = "crate::namespace::NS_DAV")]
pub struct PropfindElement {
#[xml(ty = "untagged")]
pub prop: PropfindType,

View File

@@ -1,5 +1,8 @@
use std::collections::HashMap;
use quick_xml::events::attributes::Attribute;
use quick_xml::events::{BytesEnd, BytesStart, Event};
use quick_xml::name::Namespace;
use rustical_xml::XmlSerialize;
#[derive(Debug, Clone, PartialEq)]
@@ -8,8 +11,9 @@ pub struct Resourcetype(pub &'static [&'static str]);
impl XmlSerialize for Resourcetype {
fn serialize<W: std::io::Write>(
&self,
ns: Option<&[u8]>,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
writer: &mut quick_xml::Writer<W>,
) -> std::io::Result<()> {
let tag_str = tag.map(String::from_utf8_lossy);

View File

@@ -1,5 +1,7 @@
use derive_more::derive::From;
use quick_xml::name::Namespace;
use rustical_xml::XmlSerialize;
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq, From)]
pub struct TagList(Vec<String>);
@@ -7,8 +9,9 @@ pub struct TagList(Vec<String>);
impl XmlSerialize for TagList {
fn serialize<W: std::io::Write>(
&self,
ns: Option<&[u8]>,
ns: Option<Namespace>,
tag: Option<&[u8]>,
namespaces: &HashMap<Namespace, &[u8]>,
writer: &mut quick_xml::Writer<W>,
) -> std::io::Result<()> {
#[derive(Debug, XmlSerialize, PartialEq)]
@@ -25,7 +28,7 @@ impl XmlSerialize for TagList {
Inner {
tags: self.0.iter().map(|t| Tag { name: t.to_owned() }).collect(),
}
.serialize(ns, tag, writer)
.serialize(ns, tag, namespaces, writer)
}
#[allow(refining_impl_trait)]