mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 21:42:34 +00:00
save progress: Move from serde::Serialize to XmlSerialize
This commit is contained in:
@@ -1,25 +1,30 @@
|
|||||||
|
use rustical_xml::XmlSerialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, Serialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct SupportedCalendarComponent {
|
pub struct SupportedCalendarComponent {
|
||||||
#[serde(rename = "@name")]
|
#[serde(rename = "@name")]
|
||||||
|
#[xml(ty = "attr")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, Serialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct SupportedCalendarComponentSet {
|
pub struct SupportedCalendarComponentSet {
|
||||||
#[serde(rename = "C:comp")]
|
#[serde(rename = "C:comp")]
|
||||||
|
#[xml(flatten)]
|
||||||
pub comp: Vec<SupportedCalendarComponent>,
|
pub comp: Vec<SupportedCalendarComponent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, Serialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct CalendarData {
|
pub struct CalendarData {
|
||||||
#[serde(rename = "@content-type")]
|
#[serde(rename = "@content-type")]
|
||||||
|
#[xml(ty = "attr")]
|
||||||
content_type: String,
|
content_type: String,
|
||||||
#[serde(rename = "@version")]
|
#[serde(rename = "@version")]
|
||||||
|
#[xml(ty = "attr")]
|
||||||
version: String,
|
version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,14 +37,14 @@ impl Default for CalendarData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Default, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, Serialize, Default, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct SupportedCalendarData {
|
pub struct SupportedCalendarData {
|
||||||
#[serde(rename = "C:calendar-data", alias = "calendar-data")]
|
#[serde(rename = "C:calendar-data", alias = "calendar-data")]
|
||||||
calendar_data: CalendarData,
|
calendar_data: CalendarData,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, Serialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum ReportMethod {
|
pub enum ReportMethod {
|
||||||
CalendarQuery,
|
CalendarQuery,
|
||||||
@@ -47,14 +52,15 @@ pub enum ReportMethod {
|
|||||||
SyncCollection,
|
SyncCollection,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, Serialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct ReportWrapper {
|
pub struct ReportWrapper {
|
||||||
#[serde(rename = "$value")]
|
#[serde(rename = "$value")]
|
||||||
|
#[xml(ty = "untagged")]
|
||||||
report: ReportMethod,
|
report: ReportMethod,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, Serialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct SupportedReportWrapper {
|
pub struct SupportedReportWrapper {
|
||||||
report: ReportWrapper,
|
report: ReportWrapper,
|
||||||
@@ -69,9 +75,10 @@ impl From<ReportMethod> for SupportedReportWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RFC 3253 section-3.1.5
|
// RFC 3253 section-3.1.5
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, Serialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct SupportedReportSet {
|
pub struct SupportedReportSet {
|
||||||
|
#[xml(flatten)]
|
||||||
supported_report: Vec<SupportedReportWrapper>,
|
supported_report: Vec<SupportedReportWrapper>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,27 +94,29 @@ impl Default for SupportedReportSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, Serialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum Transport {
|
pub enum Transport {
|
||||||
#[serde(rename = "P:web-push")]
|
#[serde(rename = "P:web-push")]
|
||||||
WebPush,
|
WebPush,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, Serialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct TransportWrapper {
|
pub struct TransportWrapper {
|
||||||
#[serde(rename = "$value")]
|
#[serde(rename = "$value")]
|
||||||
|
#[xml(ty = "untagged")]
|
||||||
transport: Transport,
|
transport: Transport,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, Serialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Transports {
|
pub struct Transports {
|
||||||
// NOTE: Here we implement an older version of the spec since the new property name is not reflected
|
// NOTE: Here we implement an older version of the spec since the new property name is not reflected
|
||||||
// in DAVx5 yet
|
// in DAVx5 yet
|
||||||
// https://github.com/bitfireAT/webdav-push/commit/461259a2f2174454b2b00033419b11fac52b79e3
|
// https://github.com/bitfireAT/webdav-push/commit/461259a2f2174454b2b00033419b11fac52b79e3
|
||||||
#[serde(rename = "P:transport")]
|
#[serde(rename = "P:transport")]
|
||||||
|
#[xml(flatten)]
|
||||||
transports: Vec<TransportWrapper>,
|
transports: Vec<TransportWrapper>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ use rustical_dav::resource::{Resource, ResourceService};
|
|||||||
use rustical_dav::xml::HrefElement;
|
use rustical_dav::xml::HrefElement;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::{Calendar, CalendarStore};
|
use rustical_store::{Calendar, CalendarStore};
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use serde::Serialize;
|
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -32,13 +31,12 @@ pub struct CalendarResourceService<C: CalendarStore + ?Sized> {
|
|||||||
calendar_id: String,
|
calendar_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Serialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(CalendarPropName),
|
name(CalendarPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, VariantNames, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub enum CalendarProp {
|
pub enum CalendarProp {
|
||||||
// WebDAV (RFC 2518)
|
// WebDAV (RFC 2518)
|
||||||
Displayname(Option<String>),
|
Displayname(Option<String>),
|
||||||
@@ -48,37 +46,28 @@ pub enum CalendarProp {
|
|||||||
// NOTE: Here we implement an older version of the spec since the new property name is not reflected
|
// NOTE: Here we implement an older version of the spec since the new property name is not reflected
|
||||||
// in DAVx5 yet
|
// in DAVx5 yet
|
||||||
// https://github.com/bitfireAT/webdav-push/commit/461259a2f2174454b2b00033419b11fac52b79e3
|
// https://github.com/bitfireAT/webdav-push/commit/461259a2f2174454b2b00033419b11fac52b79e3
|
||||||
#[serde(skip_deserializing)]
|
|
||||||
#[xml(skip_deserializing)]
|
#[xml(skip_deserializing)]
|
||||||
#[serde(rename = "P:push-transports", alias = "push-transports")]
|
// #[serde(rename = "P:push-transports", alias = "push-transports")]
|
||||||
Transports(Transports),
|
Transports(Transports),
|
||||||
Topic(String),
|
Topic(String),
|
||||||
|
|
||||||
// CalDAV (RFC 4791)
|
// CalDAV (RFC 4791)
|
||||||
#[serde(rename = "IC:calendar-color", alias = "calendar-color")]
|
// #[serde(rename = "IC:calendar-color", alias = "calendar-color")]
|
||||||
CalendarColor(Option<String>),
|
CalendarColor(Option<String>),
|
||||||
#[serde(rename = "C:calendar-description", alias = "calendar-description")]
|
// #[serde(rename = "C:calendar-description", alias = "calendar-description")]
|
||||||
CalendarDescription(Option<String>),
|
CalendarDescription(Option<String>),
|
||||||
#[serde(rename = "C:calendar-timezone", alias = "calendar-timezone")]
|
// #[serde(rename = "C:calendar-timezone", alias = "calendar-timezone")]
|
||||||
CalendarTimezone(Option<String>),
|
CalendarTimezone(Option<String>),
|
||||||
#[serde(rename = "IC:calendar-order", alias = "calendar-order")]
|
// #[serde(rename = "IC:calendar-order", alias = "calendar-order")]
|
||||||
CalendarOrder(Option<i64>),
|
CalendarOrder(Option<i64>),
|
||||||
#[serde(
|
// #[serde(rename = "C:supported-calendar-component-set")]
|
||||||
rename = "C:supported-calendar-component-set",
|
|
||||||
alias = "supported-calendar-component-set"
|
|
||||||
)]
|
|
||||||
// TODO: Re-add
|
// TODO: Re-add
|
||||||
#[xml(skip_deserializing)]
|
#[xml(skip_deserializing)]
|
||||||
SupportedCalendarComponentSet(SupportedCalendarComponentSet),
|
SupportedCalendarComponentSet(SupportedCalendarComponentSet),
|
||||||
#[serde(
|
// #[serde(rename = "C:supported-calendar-data")]
|
||||||
rename = "C:supported-calendar-data",
|
|
||||||
alias = "supported-calendar-data"
|
|
||||||
)]
|
|
||||||
#[serde(skip_deserializing)]
|
|
||||||
#[xml(skip_deserializing)]
|
#[xml(skip_deserializing)]
|
||||||
SupportedCalendarData(SupportedCalendarData),
|
SupportedCalendarData(SupportedCalendarData),
|
||||||
MaxResourceSize(i64),
|
MaxResourceSize(i64),
|
||||||
#[serde(skip_deserializing)]
|
|
||||||
#[xml(skip_deserializing)]
|
#[xml(skip_deserializing)]
|
||||||
SupportedReportSet(SupportedReportSet),
|
SupportedReportSet(SupportedReportSet),
|
||||||
|
|
||||||
@@ -86,9 +75,9 @@ pub enum CalendarProp {
|
|||||||
SyncToken(String),
|
SyncToken(String),
|
||||||
|
|
||||||
// CalendarServer
|
// CalendarServer
|
||||||
#[serde(rename = "CS:getctag", alias = "getctag")]
|
// #[serde(rename = "CS:getctag", alias = "getctag")]
|
||||||
Getctag(String),
|
Getctag(String),
|
||||||
#[serde(rename = "CS:source", alias = "source")]
|
// #[serde(rename = "CS:source", alias = "source")]
|
||||||
Source(Option<HrefElement>),
|
Source(Option<HrefElement>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ use rustical_dav::{
|
|||||||
resource::{Resource, ResourceService},
|
resource::{Resource, ResourceService},
|
||||||
};
|
};
|
||||||
use rustical_store::{auth::User, CalendarObject, CalendarStore};
|
use rustical_store::{auth::User, CalendarObject, CalendarStore};
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
||||||
|
|
||||||
@@ -20,20 +20,19 @@ pub struct CalendarObjectResourceService<C: CalendarStore + ?Sized> {
|
|||||||
object_id: String,
|
object_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Serialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(CalendarObjectPropName),
|
name(CalendarObjectPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, VariantNames, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub enum CalendarObjectProp {
|
pub enum CalendarObjectProp {
|
||||||
// WebDAV (RFC 2518)
|
// WebDAV (RFC 2518)
|
||||||
Getetag(String),
|
Getetag(String),
|
||||||
Getcontenttype(String),
|
Getcontenttype(String),
|
||||||
|
|
||||||
// CalDAV (RFC 4791)
|
// CalDAV (RFC 4791)
|
||||||
#[serde(rename = "C:calendar-data")]
|
// #[serde(rename = "C:calendar-data")]
|
||||||
CalendarData(String),
|
CalendarData(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ use rustical_dav::resource::{Resource, ResourceService};
|
|||||||
use rustical_dav::xml::HrefElement;
|
use rustical_dav::xml::HrefElement;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use serde::Serialize;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
||||||
|
|
||||||
@@ -24,23 +23,21 @@ pub struct PrincipalResource {
|
|||||||
principal: String,
|
principal: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Serialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(PrincipalPropName),
|
name(PrincipalPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, VariantNames, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub enum PrincipalProp {
|
pub enum PrincipalProp {
|
||||||
// WebDAV Access Control (RFC 3744)
|
// WebDAV Access Control (RFC 3744)
|
||||||
#[serde(rename = "principal-URL")]
|
|
||||||
#[strum_discriminants(strum(serialize = "principal-URL"))]
|
#[strum_discriminants(strum(serialize = "principal-URL"))]
|
||||||
PrincipalUrl(HrefElement),
|
PrincipalUrl(HrefElement),
|
||||||
|
|
||||||
// CalDAV (RFC 4791)
|
// CalDAV (RFC 4791)
|
||||||
#[serde(rename = "C:calendar-home-set")]
|
// #[serde(rename = "C:calendar-home-set")]
|
||||||
CalendarHomeSet(HrefElement),
|
CalendarHomeSet(HrefElement),
|
||||||
#[serde(rename = "C:calendar-user-address-set")]
|
// #[serde(rename = "C:calendar-user-address-set")]
|
||||||
CalendarUserAddressSet(HrefElement),
|
CalendarUserAddressSet(HrefElement),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ use rustical_dav::{
|
|||||||
resource::{Resource, ResourceService},
|
resource::{Resource, ResourceService},
|
||||||
};
|
};
|
||||||
use rustical_store::{auth::User, AddressObject, AddressbookStore};
|
use rustical_store::{auth::User, AddressObject, AddressbookStore};
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
||||||
|
|
||||||
@@ -21,20 +21,19 @@ pub struct AddressObjectResourceService<AS: AddressbookStore + ?Sized> {
|
|||||||
object_id: String,
|
object_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Serialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(AddressObjectPropName),
|
name(AddressObjectPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, VariantNames, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub enum AddressObjectProp {
|
pub enum AddressObjectProp {
|
||||||
// WebDAV (RFC 2518)
|
// WebDAV (RFC 2518)
|
||||||
Getetag(String),
|
Getetag(String),
|
||||||
Getcontenttype(String),
|
Getcontenttype(String),
|
||||||
|
|
||||||
// CalDAV (RFC 4791)
|
// CalDAV (RFC 4791)
|
||||||
#[serde(rename = "CARD:address-data")]
|
#[xml(ns = b"urn:ietf:params:xml:ns:carddav")]
|
||||||
AddressData(String),
|
AddressData(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
use serde::Serialize;
|
use rustical_xml::XmlSerialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub struct AddressDataType {
|
pub struct AddressDataType {
|
||||||
#[serde(rename = "@content-type")]
|
#[xml(ty = "attr")]
|
||||||
pub content_type: String,
|
pub content_type: String,
|
||||||
#[serde(rename = "@version")]
|
#[xml(ty = "attr")]
|
||||||
pub version: String,
|
pub version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub struct SupportedAddressData {
|
pub struct SupportedAddressData {
|
||||||
#[serde(rename = "CARD:address-data-type", alias = "address-data-type")]
|
// #[serde(rename = "CARD:address-data-type", alias = "address-data-type")]
|
||||||
|
#[xml(flatten)]
|
||||||
address_data_type: Vec<AddressDataType>,
|
address_data_type: Vec<AddressDataType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,22 +32,19 @@ impl Default for SupportedAddressData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub enum ReportMethod {
|
pub enum ReportMethod {
|
||||||
AddressbookMultiget,
|
AddressbookMultiget,
|
||||||
SyncCollection,
|
SyncCollection,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub struct ReportWrapper {
|
pub struct ReportWrapper {
|
||||||
#[serde(rename = "$value")]
|
#[xml(ty = "untagged")]
|
||||||
report: ReportMethod,
|
report: ReportMethod,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub struct SupportedReportWrapper {
|
pub struct SupportedReportWrapper {
|
||||||
report: ReportWrapper,
|
report: ReportWrapper,
|
||||||
}
|
}
|
||||||
@@ -62,9 +58,9 @@ impl From<ReportMethod> for SupportedReportWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RFC 3253 section-3.1.5
|
// RFC 3253 section-3.1.5
|
||||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub struct SupportedReportSet {
|
pub struct SupportedReportSet {
|
||||||
|
#[xml(flatten)]
|
||||||
supported_report: Vec<SupportedReportWrapper>,
|
supported_report: Vec<SupportedReportWrapper>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ use rustical_dav::privileges::UserPrivilegeSet;
|
|||||||
use rustical_dav::resource::{Resource, ResourceService};
|
use rustical_dav::resource::{Resource, ResourceService};
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::{Addressbook, AddressbookStore};
|
use rustical_store::{Addressbook, AddressbookStore};
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use serde::Serialize;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
||||||
@@ -26,8 +25,7 @@ pub struct AddressbookResourceService<AS: AddressbookStore + ?Sized> {
|
|||||||
addressbook_id: String,
|
addressbook_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Serialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(AddressbookPropName),
|
name(AddressbookPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, VariantNames, IntoStaticStr),
|
||||||
@@ -39,19 +37,11 @@ pub enum AddressbookProp {
|
|||||||
Getcontenttype(String),
|
Getcontenttype(String),
|
||||||
|
|
||||||
// CardDAV (RFC 6352)
|
// CardDAV (RFC 6352)
|
||||||
#[serde(
|
#[xml(ns = b"urn:ietf:params:xml:ns:carddav")]
|
||||||
rename = "CARD:addressbook-description",
|
|
||||||
alias = "addressbook-description"
|
|
||||||
)]
|
|
||||||
AddressbookDescription(Option<String>),
|
AddressbookDescription(Option<String>),
|
||||||
#[serde(
|
|
||||||
rename = "CARD:supported-address-data",
|
|
||||||
alias = "supported-address-data"
|
|
||||||
)]
|
|
||||||
#[serde(skip_deserializing)]
|
|
||||||
#[xml(skip_deserializing)]
|
#[xml(skip_deserializing)]
|
||||||
|
#[xml(ns = b"urn:ietf:params:xml:ns:carddav")]
|
||||||
SupportedAddressData(SupportedAddressData),
|
SupportedAddressData(SupportedAddressData),
|
||||||
#[serde(skip_deserializing)]
|
|
||||||
#[xml(skip_deserializing)]
|
#[xml(skip_deserializing)]
|
||||||
SupportedReportSet(SupportedReportSet),
|
SupportedReportSet(SupportedReportSet),
|
||||||
MaxResourceSize(i64),
|
MaxResourceSize(i64),
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ use rustical_dav::resource::{Resource, ResourceService};
|
|||||||
use rustical_dav::xml::HrefElement;
|
use rustical_dav::xml::HrefElement;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::AddressbookStore;
|
use rustical_store::AddressbookStore;
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use serde::Serialize;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
||||||
|
|
||||||
@@ -24,23 +23,22 @@ pub struct PrincipalResource {
|
|||||||
principal: String,
|
principal: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Serialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(PrincipalPropName),
|
name(PrincipalPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, VariantNames, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub enum PrincipalProp {
|
pub enum PrincipalProp {
|
||||||
// WebDAV Access Control (RFC 3744)
|
// WebDAV Access Control (RFC 3744)
|
||||||
#[serde(rename = "principal-URL")]
|
#[xml(rename = b"principal-URL")]
|
||||||
#[strum_discriminants(strum(serialize = "principal-URL"))]
|
#[strum_discriminants(strum(serialize = "principal-URL"))]
|
||||||
PrincipalUrl(HrefElement),
|
PrincipalUrl(HrefElement),
|
||||||
|
|
||||||
// CardDAV (RFC 6352)
|
// CardDAV (RFC 6352)
|
||||||
#[serde(rename = "CARD:addressbook-home-set")]
|
#[xml(ns = b"urn:ietf:params:xml:ns:carddav")]
|
||||||
AddressbookHomeSet(HrefElement),
|
AddressbookHomeSet(HrefElement),
|
||||||
#[serde(rename = "CARD:principal-address")]
|
#[xml(ns = b"urn:ietf:params:xml:ns:carddav")]
|
||||||
PrincipalAddress(Option<HrefElement>),
|
PrincipalAddress(Option<HrefElement>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ pub enum Error {
|
|||||||
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
XmlSerializationError(#[from] quick_xml::SeError),
|
XmlSerializationError(#[from] quick_xml::SeError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
IOError(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl actix_web::error::ResponseError for Error {
|
impl actix_web::error::ResponseError for Error {
|
||||||
@@ -36,6 +39,7 @@ impl actix_web::error::ResponseError for Error {
|
|||||||
Self::XmlDeserializationError(_) => StatusCode::BAD_REQUEST,
|
Self::XmlDeserializationError(_) => StatusCode::BAD_REQUEST,
|
||||||
Self::XmlSerializationError(_) => StatusCode::BAD_REQUEST,
|
Self::XmlSerializationError(_) => StatusCode::BAD_REQUEST,
|
||||||
Error::PropReadOnly => StatusCode::CONFLICT,
|
Error::PropReadOnly => StatusCode::CONFLICT,
|
||||||
|
Self::IOError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use serde::Serialize;
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, XmlDeserialize, Eq, Hash, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, XmlDeserialize, Eq, Hash, PartialEq)]
|
||||||
pub enum UserPrivilege {
|
pub enum UserPrivilege {
|
||||||
Read,
|
Read,
|
||||||
Write,
|
Write,
|
||||||
@@ -14,37 +13,33 @@ pub enum UserPrivilege {
|
|||||||
All,
|
All,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for UserPrivilegeSet {
|
impl XmlSerialize for UserPrivilegeSet {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<W: std::io::Write>(
|
||||||
where
|
&self,
|
||||||
S: serde::Serializer,
|
ns: Option<&[u8]>,
|
||||||
{
|
tag: Option<&[u8]>,
|
||||||
#[derive(Serialize)]
|
writer: &mut quick_xml::Writer<W>,
|
||||||
#[serde(rename_all = "kebab-case")]
|
) -> std::io::Result<()> {
|
||||||
pub struct UserPrivilegeWrapper<'a> {
|
#[derive(XmlSerialize)]
|
||||||
#[serde(rename = "$value")]
|
pub struct FakeUserPrivilegeSet {
|
||||||
privilege: &'a UserPrivilege,
|
#[xml(rename = b"privilege", flatten)]
|
||||||
}
|
privileges: Vec<UserPrivilege>,
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub struct FakeUserPrivilegeSet<'a> {
|
|
||||||
#[serde(rename = "privilege")]
|
|
||||||
privileges: Vec<UserPrivilegeWrapper<'a>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FakeUserPrivilegeSet {
|
FakeUserPrivilegeSet {
|
||||||
privileges: self
|
privileges: self.privileges.iter().cloned().collect(),
|
||||||
.privileges
|
|
||||||
.iter()
|
|
||||||
.map(|privilege| UserPrivilegeWrapper { privilege })
|
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
.serialize(serializer)
|
.serialize(ns, tag, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(refining_impl_trait)]
|
||||||
|
fn attributes<'a>(&self) -> Option<Vec<quick_xml::events::attributes::Attribute<'a>>> {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, XmlDeserialize, Default, PartialEq)]
|
#[derive(Debug, Clone, Default, PartialEq)]
|
||||||
pub struct UserPrivilegeSet {
|
pub struct UserPrivilegeSet {
|
||||||
#[xml(flatten)]
|
|
||||||
privileges: HashSet<UserPrivilege>,
|
privileges: HashSet<UserPrivilege>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,28 +10,22 @@ use actix_web::{http::StatusCode, ResponseError};
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
pub use resource_service::ResourceService;
|
pub use resource_service::ResourceService;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use serde::Serialize;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use strum::{EnumString, VariantNames};
|
use strum::{EnumString, VariantNames};
|
||||||
|
|
||||||
mod methods;
|
mod methods;
|
||||||
mod resource_service;
|
mod resource_service;
|
||||||
|
|
||||||
pub trait ResourceProp: Serialize + XmlDeserialize {}
|
pub trait ResourceProp: XmlSerialize + XmlDeserialize {}
|
||||||
impl<T: Serialize + XmlDeserialize> ResourceProp for T {}
|
impl<T: XmlSerialize + XmlDeserialize> ResourceProp for T {}
|
||||||
|
|
||||||
pub trait ResourcePropName: FromStr + VariantNames {}
|
pub trait ResourcePropName: FromStr + VariantNames {}
|
||||||
impl<T: FromStr + VariantNames> ResourcePropName for T {}
|
impl<T: FromStr + VariantNames> ResourcePropName for T {}
|
||||||
|
|
||||||
pub trait ResourceType: Serialize + XmlDeserialize {}
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq)]
|
||||||
impl<T: Serialize + XmlDeserialize> ResourceType for T {}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Serialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub enum CommonPropertiesProp {
|
pub enum CommonPropertiesProp {
|
||||||
// WebDAV (RFC 2518)
|
// WebDAV (RFC 2518)
|
||||||
#[serde(skip_deserializing)]
|
|
||||||
#[xml(skip_deserializing)]
|
#[xml(skip_deserializing)]
|
||||||
Resourcetype(Resourcetype),
|
Resourcetype(Resourcetype),
|
||||||
|
|
||||||
@@ -39,15 +33,15 @@ pub enum CommonPropertiesProp {
|
|||||||
CurrentUserPrincipal(HrefElement),
|
CurrentUserPrincipal(HrefElement),
|
||||||
|
|
||||||
// WebDAV Access Control Protocol (RFC 3477)
|
// WebDAV Access Control Protocol (RFC 3477)
|
||||||
|
#[xml(skip_deserializing)]
|
||||||
CurrentUserPrivilegeSet(UserPrivilegeSet),
|
CurrentUserPrivilegeSet(UserPrivilegeSet),
|
||||||
Owner(Option<HrefElement>),
|
Owner(Option<HrefElement>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(XmlSerialize)]
|
||||||
|
#[xml(untagged)]
|
||||||
pub enum EitherProp<Left: ResourceProp, Right: ResourceProp> {
|
pub enum EitherProp<Left: ResourceProp, Right: ResourceProp> {
|
||||||
#[serde(untagged)]
|
|
||||||
Left(Left),
|
Left(Left),
|
||||||
#[serde(untagged)]
|
|
||||||
Right(Right),
|
Right(Right),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use actix_web::dev::ResourceMap;
|
|||||||
use actix_web::HttpRequest;
|
use actix_web::HttpRequest;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::any::type_name;
|
use std::any::type_name;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@@ -23,7 +23,7 @@ impl<PR: Resource> Default for RootResource<PR> {
|
|||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
pub enum RootResourcePropName {}
|
pub enum RootResourcePropName {}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Serialize, Clone, PartialEq)]
|
#[derive(XmlDeserialize, XmlSerialize, Serialize, Clone, PartialEq)]
|
||||||
pub enum RootResourceProp {}
|
pub enum RootResourceProp {}
|
||||||
|
|
||||||
impl From<RootResourceProp> for RootResourcePropName {
|
impl From<RootResourceProp> for RootResourcePropName {
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ use derive_more::derive::From;
|
|||||||
pub use multistatus::MultistatusElement;
|
pub use multistatus::MultistatusElement;
|
||||||
pub use propfind::{PropElement, PropfindElement, PropfindType, Propname};
|
pub use propfind::{PropElement, PropfindElement, PropfindType, Propname};
|
||||||
pub use resourcetype::Resourcetype;
|
pub use resourcetype::Resourcetype;
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use serde::Serialize;
|
|
||||||
pub use tag_list::TagList;
|
pub use tag_list::TagList;
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Debug, Clone, Serialize, From, PartialEq)]
|
#[derive(XmlDeserialize, XmlSerialize, Debug, Clone, From, PartialEq)]
|
||||||
pub struct HrefElement {
|
pub struct HrefElement {
|
||||||
pub href: String,
|
pub href: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,33 +4,37 @@ use actix_web::{
|
|||||||
http::{header::ContentType, StatusCode},
|
http::{header::ContentType, StatusCode},
|
||||||
HttpRequest, HttpResponse, Responder, ResponseError,
|
HttpRequest, HttpResponse, Responder, ResponseError,
|
||||||
};
|
};
|
||||||
use serde::{Serialize, Serializer};
|
use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot};
|
||||||
|
|
||||||
// Intermediate struct because of a serde limitation, see following article:
|
// Intermediate struct because of a serde limitation, see following article:
|
||||||
// https://stackoverflow.com/questions/78444158/unsupportedcannot-serialize-enum-newtype-variant-exampledata
|
// https://stackoverflow.com/questions/78444158/unsupportedcannot-serialize-enum-newtype-variant-exampledata
|
||||||
#[derive(Serialize)]
|
#[derive(XmlSerialize)]
|
||||||
pub struct PropTagWrapper<T: Serialize> {
|
pub struct PropTagWrapper<T: XmlSerialize> {
|
||||||
#[serde(rename = "$value")]
|
#[xml(flatten, ty = "untagged")]
|
||||||
pub prop: Vec<T>,
|
pub prop: Vec<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// RFC 2518
|
// RFC 2518
|
||||||
// <!ELEMENT propstat (prop, status, responsedescription?) >
|
// <!ELEMENT propstat (prop, status, responsedescription?) >
|
||||||
#[derive(Serialize)]
|
#[derive(XmlSerialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
pub struct PropstatElement<PropType: XmlSerialize> {
|
||||||
pub struct PropstatElement<PropType: Serialize> {
|
|
||||||
pub prop: PropType,
|
pub prop: PropType,
|
||||||
#[serde(serialize_with = "serialize_status")]
|
#[xml(serialize_with = "xml_serialize_status")]
|
||||||
pub status: StatusCode,
|
pub status: StatusCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_status<S: Serializer>(status: &StatusCode, serializer: S) -> Result<S::Ok, S::Error> {
|
fn xml_serialize_status<W: ::std::io::Write>(
|
||||||
format!("HTTP/1.1 {}", status).serialize(serializer)
|
status: &StatusCode,
|
||||||
|
ns: Option<&[u8]>,
|
||||||
|
tag: Option<&[u8]>,
|
||||||
|
writer: &mut quick_xml::Writer<W>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
XmlSerialize::serialize(&format!("HTTP/1.1 {}", status), ns, tag, writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(XmlSerialize)]
|
||||||
#[serde(untagged)]
|
#[xml(untagged)]
|
||||||
pub enum PropstatWrapper<T: Serialize> {
|
pub enum PropstatWrapper<T: XmlSerialize> {
|
||||||
Normal(PropstatElement<PropTagWrapper<T>>),
|
Normal(PropstatElement<PropTagWrapper<T>>),
|
||||||
TagList(PropstatElement<TagList>),
|
TagList(PropstatElement<TagList>),
|
||||||
}
|
}
|
||||||
@@ -38,26 +42,30 @@ pub enum PropstatWrapper<T: Serialize> {
|
|||||||
// RFC 2518
|
// RFC 2518
|
||||||
// <!ELEMENT response (href, ((href*, status)|(propstat+)),
|
// <!ELEMENT response (href, ((href*, status)|(propstat+)),
|
||||||
// responsedescription?) >
|
// responsedescription?) >
|
||||||
#[derive(Serialize)]
|
#[derive(XmlSerialize)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
pub struct ResponseElement<PropstatType: XmlSerialize> {
|
||||||
pub struct ResponseElement<PropstatType: Serialize> {
|
|
||||||
pub href: String,
|
pub href: String,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[xml(serialize_with = "xml_serialize_optional_status")]
|
||||||
#[serde(serialize_with = "serialize_optional_status")]
|
|
||||||
pub status: Option<StatusCode>,
|
pub status: Option<StatusCode>,
|
||||||
|
#[xml(flatten)]
|
||||||
pub propstat: Vec<PropstatWrapper<PropstatType>>,
|
pub propstat: Vec<PropstatWrapper<PropstatType>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_optional_status<S: Serializer>(
|
fn xml_serialize_optional_status<W: ::std::io::Write>(
|
||||||
status_option: &Option<StatusCode>,
|
val: &Option<StatusCode>,
|
||||||
serializer: S,
|
ns: Option<&[u8]>,
|
||||||
) -> Result<S::Ok, S::Error> {
|
tag: Option<&[u8]>,
|
||||||
status_option
|
writer: &mut quick_xml::Writer<W>,
|
||||||
.map(|status| format!("HTTP/1.1 {}", status))
|
) -> std::io::Result<()> {
|
||||||
.serialize(serializer)
|
XmlSerialize::serialize(
|
||||||
|
&val.map(|status| format!("HTTP/1.1 {}", status)),
|
||||||
|
ns,
|
||||||
|
tag,
|
||||||
|
writer,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<PT: Serialize> Default for ResponseElement<PT> {
|
impl<PT: XmlSerialize> Default for ResponseElement<PT> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
href: String::new(),
|
href: String::new(),
|
||||||
@@ -70,30 +78,24 @@ impl<PT: Serialize> Default for ResponseElement<PT> {
|
|||||||
// RFC 2518
|
// RFC 2518
|
||||||
// <!ELEMENT multistatus (response+, responsedescription?) >
|
// <!ELEMENT multistatus (response+, responsedescription?) >
|
||||||
// Extended by sync-token as specified in RFC 6578
|
// Extended by sync-token as specified in RFC 6578
|
||||||
#[derive(Serialize)]
|
#[derive(XmlSerialize, XmlRootTag)]
|
||||||
#[serde(rename = "multistatus", rename_all = "kebab-case")]
|
#[xml(root = b"multistatus")]
|
||||||
pub struct MultistatusElement<PropType: Serialize, MemberPropType: Serialize> {
|
pub struct MultistatusElement<PropType: XmlSerialize, MemberPropType: XmlSerialize> {
|
||||||
#[serde(rename = "response")]
|
#[xml(rename = b"response", flatten)]
|
||||||
pub responses: Vec<ResponseElement<PropType>>,
|
pub responses: Vec<ResponseElement<PropType>>,
|
||||||
#[serde(rename = "response")]
|
#[xml(rename = b"response", flatten)]
|
||||||
pub member_responses: Vec<ResponseElement<MemberPropType>>,
|
pub member_responses: Vec<ResponseElement<MemberPropType>>,
|
||||||
#[serde(rename = "@xmlns")]
|
// TODO: napespaces
|
||||||
pub ns_dav: &'static str,
|
pub ns_dav: &'static str,
|
||||||
#[serde(rename = "@xmlns:P")]
|
|
||||||
pub ns_davpush: &'static str,
|
pub ns_davpush: &'static str,
|
||||||
#[serde(rename = "@xmlns:C")]
|
|
||||||
pub ns_caldav: &'static str,
|
pub ns_caldav: &'static str,
|
||||||
#[serde(rename = "@xmlns:IC")]
|
|
||||||
pub ns_ical: &'static str,
|
pub ns_ical: &'static str,
|
||||||
#[serde(rename = "@xmlns:CS")]
|
|
||||||
pub ns_calendarserver: &'static str,
|
pub ns_calendarserver: &'static str,
|
||||||
#[serde(rename = "@xmlns:CARD")]
|
|
||||||
pub ns_carddav: &'static str,
|
pub ns_carddav: &'static str,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub sync_token: Option<String>,
|
pub sync_token: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T1: Serialize, T2: Serialize> Default for MultistatusElement<T1, T2> {
|
impl<T1: XmlSerialize, T2: XmlSerialize> Default for MultistatusElement<T1, T2> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
responses: vec![],
|
responses: vec![],
|
||||||
@@ -109,19 +111,18 @@ impl<T1: Serialize, T2: Serialize> Default for MultistatusElement<T1, T2> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T1: Serialize, T2: Serialize> Responder for MultistatusElement<T1, T2> {
|
impl<T1: XmlSerialize, T2: XmlSerialize> Responder for MultistatusElement<T1, T2> {
|
||||||
type Body = BoxBody;
|
type Body = BoxBody;
|
||||||
|
|
||||||
fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
|
fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
|
||||||
let mut output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".to_owned();
|
let mut output: Vec<_> = b"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".into();
|
||||||
let mut ser = quick_xml::se::Serializer::new(&mut output);
|
let mut writer = quick_xml::Writer::new_with_indent(&mut output, b' ', 4);
|
||||||
ser.indent(' ', 4);
|
if let Err(err) = self.serialize_root(&mut writer) {
|
||||||
if let Err(err) = self.serialize(ser) {
|
|
||||||
return crate::Error::from(err).error_response();
|
return crate::Error::from(err).error_response();
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse::MultiStatus()
|
HttpResponse::MultiStatus()
|
||||||
.content_type(ContentType::xml())
|
.content_type(ContentType::xml())
|
||||||
.body(output)
|
.body(String::from_utf8(output).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,62 @@
|
|||||||
use serde::ser::SerializeMap;
|
use quick_xml::events::attributes::Attribute;
|
||||||
use serde::Serialize;
|
use quick_xml::events::{BytesEnd, BytesStart, Event};
|
||||||
|
use rustical_xml::XmlSerialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Resourcetype(pub &'static [&'static str]);
|
pub struct Resourcetype(pub &'static [&'static str]);
|
||||||
|
|
||||||
impl Serialize for Resourcetype {
|
impl XmlSerialize for Resourcetype {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<W: std::io::Write>(
|
||||||
where
|
&self,
|
||||||
S: serde::Serializer,
|
ns: Option<&[u8]>,
|
||||||
{
|
tag: Option<&[u8]>,
|
||||||
let mut map = serializer.serialize_map(Some(self.0.len()))?;
|
writer: &mut quick_xml::Writer<W>,
|
||||||
for entry in self.0 {
|
) -> std::io::Result<()> {
|
||||||
map.serialize_entry(entry, &())?;
|
let tag_str = tag.map(String::from_utf8_lossy);
|
||||||
|
if let Some(tag) = &tag_str {
|
||||||
|
writer.write_event(Event::Start(BytesStart::new(tag.to_owned())))?;
|
||||||
}
|
}
|
||||||
map.end()
|
|
||||||
|
for &ty in self.0 {
|
||||||
|
writer.write_event(Event::Empty(BytesStart::new(ty)))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(tag) = &tag_str {
|
||||||
|
writer.write_event(Event::End(BytesEnd::new(tag.to_owned())))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(refining_impl_trait)]
|
||||||
|
fn attributes<'a>(&self) -> Option<Vec<Attribute<'a>>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Resourcetype;
|
||||||
|
use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot};
|
||||||
|
|
||||||
|
#[derive(XmlSerialize, XmlRootTag)]
|
||||||
|
#[xml(root = b"document")]
|
||||||
|
struct Document {
|
||||||
|
resourcetype: Resourcetype,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serialize_resourcetype() {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let mut writer = quick_xml::Writer::new(&mut buf);
|
||||||
|
Document {
|
||||||
|
resourcetype: Resourcetype(&["collection", "hello"]),
|
||||||
|
}
|
||||||
|
.serialize_root(&mut writer)
|
||||||
|
.unwrap();
|
||||||
|
let out = String::from_utf8(buf).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
out,
|
||||||
|
"<document><resourcetype><collection/><hello/></resourcetype></document>"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,36 @@
|
|||||||
use derive_more::derive::From;
|
use derive_more::derive::From;
|
||||||
use serde::ser::SerializeMap;
|
use rustical_xml::XmlSerialize;
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, From)]
|
#[derive(Clone, Debug, PartialEq, From)]
|
||||||
pub struct TagList(Vec<String>);
|
pub struct TagList(Vec<String>);
|
||||||
|
|
||||||
impl Serialize for TagList {
|
impl XmlSerialize for TagList {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<W: std::io::Write>(
|
||||||
where
|
&self,
|
||||||
S: serde::Serializer,
|
ns: Option<&[u8]>,
|
||||||
{
|
tag: Option<&[u8]>,
|
||||||
let mut map = serializer.serialize_map(Some(self.0.len()))?;
|
writer: &mut quick_xml::Writer<W>,
|
||||||
for name in &self.0 {
|
) -> std::io::Result<()> {
|
||||||
map.serialize_entry(&name, &())?;
|
#[derive(Debug, XmlSerialize, PartialEq)]
|
||||||
|
struct Inner {
|
||||||
|
#[xml(ty = "untagged", flatten)]
|
||||||
|
tags: Vec<Tag>,
|
||||||
}
|
}
|
||||||
map.end()
|
|
||||||
|
#[derive(Debug, XmlSerialize, PartialEq)]
|
||||||
|
struct Tag {
|
||||||
|
#[xml(ty = "tag_name")]
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
Inner {
|
||||||
|
tags: self.0.iter().map(|t| Tag { name: t.to_owned() }).collect(),
|
||||||
|
}
|
||||||
|
.serialize(ns, tag, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(refining_impl_trait)]
|
||||||
|
fn attributes<'a>(&self) -> Option<Vec<quick_xml::events::attributes::Attribute<'a>>> {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user