mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 21:42:34 +00:00
Add namespaces to propnames
This commit is contained in:
@@ -16,16 +16,17 @@ use rustical_dav::resource::{Resource, ResourceService};
|
|||||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
|
use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
|
||||||
|
use rustical_xml::EnumVariants;
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
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};
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(CalendarPropName),
|
name(CalendarPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
pub enum CalendarProp {
|
pub enum CalendarProp {
|
||||||
@@ -89,6 +90,12 @@ impl From<CalendarResource> for Calendar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CalendarResource {
|
||||||
|
fn get_synctoken(&self) -> String {
|
||||||
|
self.cal.format_synctoken()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Resource for CalendarResource {
|
impl Resource for CalendarResource {
|
||||||
type PropName = CalendarPropName;
|
type PropName = CalendarPropName;
|
||||||
type Prop = CalendarProp;
|
type Prop = CalendarProp;
|
||||||
@@ -98,13 +105,16 @@ impl Resource for CalendarResource {
|
|||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
if self.cal.subscription_url.is_none() {
|
if self.cal.subscription_url.is_none() {
|
||||||
Resourcetype(&[
|
Resourcetype(&[
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_DAV, "collection"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_CALDAV, "calendar"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_CALDAV), "calendar"),
|
||||||
])
|
])
|
||||||
} else {
|
} else {
|
||||||
Resourcetype(&[
|
Resourcetype(&[
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_DAV, "collection"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_CALENDARSERVER, "subscribed"),
|
ResourcetypeInner(
|
||||||
|
Some(rustical_dav::namespace::NS_CALENDARSERVER),
|
||||||
|
"subscribed",
|
||||||
|
),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,8 +159,8 @@ impl Resource for CalendarResource {
|
|||||||
CalendarPropName::SupportedReportSet => {
|
CalendarPropName::SupportedReportSet => {
|
||||||
CalendarProp::SupportedReportSet(SupportedReportSet::default())
|
CalendarProp::SupportedReportSet(SupportedReportSet::default())
|
||||||
}
|
}
|
||||||
CalendarPropName::SyncToken => CalendarProp::SyncToken(self.cal.format_synctoken()),
|
CalendarPropName::SyncToken => CalendarProp::SyncToken(self.get_synctoken()),
|
||||||
CalendarPropName::Getctag => CalendarProp::Getctag(self.cal.format_synctoken()),
|
CalendarPropName::Getctag => CalendarProp::Getctag(self.get_synctoken()),
|
||||||
CalendarPropName::Source => {
|
CalendarPropName::Source => {
|
||||||
CalendarProp::Source(self.cal.subscription_url.to_owned().map(HrefElement::from))
|
CalendarProp::Source(self.cal.subscription_url.to_owned().map(HrefElement::from))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ use rustical_dav::{
|
|||||||
xml::Resourcetype,
|
xml::Resourcetype,
|
||||||
};
|
};
|
||||||
use rustical_store::{auth::User, CalendarObject, CalendarStore};
|
use rustical_store::{auth::User, CalendarObject, CalendarStore};
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
|
||||||
|
|
||||||
pub struct CalendarObjectResourceService<C: CalendarStore> {
|
pub struct CalendarObjectResourceService<C: CalendarStore> {
|
||||||
cal_store: Arc<C>,
|
cal_store: Arc<C>,
|
||||||
@@ -24,10 +24,10 @@ impl<C: CalendarStore> CalendarObjectResourceService<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(CalendarObjectPropName),
|
name(CalendarObjectPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
pub enum CalendarObjectProp {
|
pub enum CalendarObjectProp {
|
||||||
|
|||||||
@@ -8,25 +8,25 @@ use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
|
|||||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CalendarSetResource {
|
pub struct CalendarSetResource {
|
||||||
pub(crate) principal: String,
|
pub(crate) principal: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(PrincipalPropName),
|
name(PrincipalPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
pub enum PrincipalProp {
|
pub enum PrincipalProp {
|
||||||
// WebDAV Access Control (RFC 3744)
|
// WebDAV Access Control (RFC 3744)
|
||||||
#[strum_discriminants(strum(serialize = "principal-URL"))]
|
#[strum_discriminants(strum(serialize = "principal-URL"))]
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"principal-URL")]
|
||||||
PrincipalUrl(HrefElement),
|
PrincipalUrl(HrefElement),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ impl Resource for CalendarSetResource {
|
|||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[ResourcetypeInner(
|
Resourcetype(&[ResourcetypeInner(
|
||||||
rustical_dav::namespace::NS_DAV,
|
Some(rustical_dav::namespace::NS_DAV),
|
||||||
"collection",
|
"collection",
|
||||||
)])
|
)])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use rustical_dav::privileges::UserPrivilegeSet;
|
|||||||
use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
|
use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
|
||||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
|
||||||
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PrincipalResource {
|
pub struct PrincipalResource {
|
||||||
@@ -18,10 +18,10 @@ pub struct PrincipalResource {
|
|||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
||||||
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
|
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(PrincipalPropName),
|
name(PrincipalPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
pub enum PrincipalProp {
|
pub enum PrincipalProp {
|
||||||
@@ -36,7 +36,7 @@ pub enum PrincipalProp {
|
|||||||
|
|
||||||
// WebDAV Access Control (RFC 3744)
|
// WebDAV Access Control (RFC 3744)
|
||||||
#[strum_discriminants(strum(serialize = "principal-URL"))]
|
#[strum_discriminants(strum(serialize = "principal-URL"))]
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"principal-URL")]
|
||||||
PrincipalUrl(HrefElement),
|
PrincipalUrl(HrefElement),
|
||||||
|
|
||||||
// CalDAV (RFC 4791)
|
// CalDAV (RFC 4791)
|
||||||
@@ -64,8 +64,8 @@ impl Resource for PrincipalResource {
|
|||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[
|
Resourcetype(&[
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_DAV, "collection"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_DAV, "principal"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "principal"),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ use rustical_dav::{
|
|||||||
xml::Resourcetype,
|
xml::Resourcetype,
|
||||||
};
|
};
|
||||||
use rustical_store::{auth::User, AddressObject, AddressbookStore};
|
use rustical_store::{auth::User, AddressObject, AddressbookStore};
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
|
||||||
|
|
||||||
use super::methods::{get_object, put_object};
|
use super::methods::{get_object, put_object};
|
||||||
|
|
||||||
@@ -20,10 +20,10 @@ pub struct AddressObjectResourceService<AS: AddressbookStore> {
|
|||||||
addr_store: Arc<AS>,
|
addr_store: Arc<AS>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(AddressObjectPropName),
|
name(AddressObjectPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
pub enum AddressObjectProp {
|
pub enum AddressObjectProp {
|
||||||
|
|||||||
@@ -16,14 +16,13 @@ use rustical_dav::resource::{Resource, ResourceService};
|
|||||||
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore};
|
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore};
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
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};
|
||||||
|
|
||||||
pub struct AddressbookResourceService<AS: AddressbookStore, S: SubscriptionStore>
|
pub struct AddressbookResourceService<AS: AddressbookStore, S: SubscriptionStore> {
|
||||||
{
|
|
||||||
addr_store: Arc<AS>,
|
addr_store: Arc<AS>,
|
||||||
__phantom_sub: PhantomData<S>,
|
__phantom_sub: PhantomData<S>,
|
||||||
}
|
}
|
||||||
@@ -37,10 +36,10 @@ impl<A: AddressbookStore, S: SubscriptionStore> AddressbookResourceService<A, S>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(AddressbookPropName),
|
name(AddressbookPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
pub enum AddressbookProp {
|
pub enum AddressbookProp {
|
||||||
@@ -87,8 +86,8 @@ impl Resource for AddressbookResource {
|
|||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[
|
Resourcetype(&[
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_DAV, "collection"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_CARDDAV, "addressbook"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_CARDDAV), "addressbook"),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
|
|||||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::AddressbookStore;
|
use rustical_store::AddressbookStore;
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::{EnumDiscriminants, EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
|
||||||
|
|
||||||
pub struct PrincipalResourceService<A: AddressbookStore> {
|
pub struct PrincipalResourceService<A: AddressbookStore> {
|
||||||
addr_store: Arc<A>,
|
addr_store: Arc<A>,
|
||||||
@@ -26,10 +26,10 @@ pub struct PrincipalResource {
|
|||||||
principal: String,
|
principal: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
|
||||||
#[strum_discriminants(
|
#[strum_discriminants(
|
||||||
name(PrincipalPropName),
|
name(PrincipalPropName),
|
||||||
derive(EnumString, VariantNames, IntoStaticStr),
|
derive(EnumString, IntoStaticStr),
|
||||||
strum(serialize_all = "kebab-case")
|
strum(serialize_all = "kebab-case")
|
||||||
)]
|
)]
|
||||||
pub enum PrincipalProp {
|
pub enum PrincipalProp {
|
||||||
@@ -69,8 +69,8 @@ impl Resource for PrincipalResource {
|
|||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[
|
Resourcetype(&[
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_DAV, "collection"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_DAV, "principal"),
|
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "principal"),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use crate::Error;
|
|||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use actix_web::{web::Path, HttpRequest};
|
use actix_web::{web::Path, HttpRequest};
|
||||||
|
use itertools::Itertools;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_xml::Unparsed;
|
use rustical_xml::Unparsed;
|
||||||
use rustical_xml::XmlDeserialize;
|
use rustical_xml::XmlDeserialize;
|
||||||
@@ -98,20 +99,27 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
|
|||||||
let propname: <R::Resource as Resource>::PropName = prop.clone().into();
|
let propname: <R::Resource as Resource>::PropName = prop.clone().into();
|
||||||
let propname: &str = propname.into();
|
let propname: &str = propname.into();
|
||||||
match resource.set_prop(prop) {
|
match resource.set_prop(prop) {
|
||||||
Ok(()) => props_ok.push(propname.to_owned()),
|
Ok(()) => props_ok.push((None, propname.to_owned())),
|
||||||
Err(Error::PropReadOnly) => props_conflict.push(propname.to_owned()),
|
Err(Error::PropReadOnly) => {
|
||||||
|
props_conflict.push((None, propname.to_owned()))
|
||||||
|
}
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
SetPropertyPropWrapper::Invalid(invalid) => {
|
SetPropertyPropWrapper::Invalid(invalid) => {
|
||||||
let propname = invalid.tag_name();
|
let propname = invalid.tag_name();
|
||||||
if <R::Resource as Resource>::list_props().contains(&propname.as_str()) {
|
if <R::Resource as Resource>::list_props()
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_ns, tag)| tag)
|
||||||
|
.collect_vec()
|
||||||
|
.contains(&propname.as_str())
|
||||||
|
{
|
||||||
// This happens in following cases:
|
// This happens in following cases:
|
||||||
// - read-only properties with #[serde(skip_deserializing)]
|
// - read-only properties with #[serde(skip_deserializing)]
|
||||||
// - internal properties
|
// - internal properties
|
||||||
props_conflict.push(propname)
|
props_conflict.push((None, propname))
|
||||||
} else {
|
} else {
|
||||||
props_not_found.push(propname);
|
props_not_found.push((None, propname));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,12 +128,12 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
|
|||||||
let propname = remove_el.prop.0 .0;
|
let propname = remove_el.prop.0 .0;
|
||||||
match <<R::Resource as Resource>::PropName as FromStr>::from_str(&propname) {
|
match <<R::Resource as Resource>::PropName as FromStr>::from_str(&propname) {
|
||||||
Ok(prop) => match resource.remove_prop(&prop) {
|
Ok(prop) => match resource.remove_prop(&prop) {
|
||||||
Ok(()) => props_ok.push(propname),
|
Ok(()) => props_ok.push((None, propname)),
|
||||||
Err(Error::PropReadOnly) => props_conflict.push(propname),
|
Err(Error::PropReadOnly) => props_conflict.push((None, propname)),
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
},
|
},
|
||||||
// I guess removing a nonexisting property should be successful :)
|
// I guess removing a nonexisting property should be successful :)
|
||||||
Err(_) => props_ok.push(propname),
|
Err(_) => props_ok.push((None, propname)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ use crate::Error;
|
|||||||
use actix_web::dev::ResourceMap;
|
use actix_web::dev::ResourceMap;
|
||||||
use actix_web::{http::StatusCode, ResponseError};
|
use actix_web::{http::StatusCode, ResponseError};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use quick_xml::name::Namespace;
|
||||||
pub use resource_service::ResourceService;
|
pub use resource_service::ResourceService;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use strum::{EnumString, VariantNames};
|
use strum::EnumString;
|
||||||
|
|
||||||
mod methods;
|
mod methods;
|
||||||
mod resource_service;
|
mod resource_service;
|
||||||
@@ -20,10 +21,10 @@ pub use resource_service::*;
|
|||||||
pub trait ResourceProp: XmlSerialize + XmlDeserialize {}
|
pub trait ResourceProp: XmlSerialize + XmlDeserialize {}
|
||||||
impl<T: XmlSerialize + XmlDeserialize> ResourceProp for T {}
|
impl<T: XmlSerialize + XmlDeserialize> ResourceProp for T {}
|
||||||
|
|
||||||
pub trait ResourcePropName: FromStr + VariantNames {}
|
pub trait ResourcePropName: FromStr {}
|
||||||
impl<T: FromStr + VariantNames> ResourcePropName for T {}
|
impl<T: FromStr> ResourcePropName for T {}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumVariants)]
|
||||||
pub enum CommonPropertiesProp {
|
pub enum CommonPropertiesProp {
|
||||||
// WebDAV (RFC 2518)
|
// WebDAV (RFC 2518)
|
||||||
#[xml(skip_deserializing)]
|
#[xml(skip_deserializing)]
|
||||||
@@ -49,7 +50,7 @@ pub enum EitherProp<Left: ResourceProp, Right: ResourceProp> {
|
|||||||
Right(Right),
|
Right(Right),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(EnumString, VariantNames, Clone)]
|
#[derive(EnumString, Clone)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
pub enum CommonPropertiesPropName {
|
pub enum CommonPropertiesPropName {
|
||||||
Resourcetype,
|
Resourcetype,
|
||||||
@@ -60,14 +61,18 @@ pub enum CommonPropertiesPropName {
|
|||||||
|
|
||||||
pub trait Resource: Clone + 'static {
|
pub trait Resource: Clone + 'static {
|
||||||
type PropName: ResourcePropName + From<Self::Prop> + Into<&'static str>;
|
type PropName: ResourcePropName + From<Self::Prop> + Into<&'static str>;
|
||||||
type Prop: ResourceProp + PartialEq + Clone;
|
type Prop: ResourceProp + PartialEq + Clone + EnumVariants;
|
||||||
type Error: ResponseError + From<crate::Error>;
|
type Error: ResponseError + From<crate::Error>;
|
||||||
type PrincipalResource: Resource + NamedRoute;
|
type PrincipalResource: Resource + NamedRoute;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype;
|
fn get_resourcetype(&self) -> Resourcetype;
|
||||||
|
|
||||||
fn list_props() -> Vec<&'static str> {
|
fn list_props() -> Vec<(Option<Namespace<'static>>, &'static str)> {
|
||||||
[Self::PropName::VARIANTS, CommonPropertiesPropName::VARIANTS].concat()
|
[
|
||||||
|
Self::Prop::variant_names(),
|
||||||
|
CommonPropertiesProp::variant_names(),
|
||||||
|
]
|
||||||
|
.concat()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_internal_prop(
|
fn get_internal_prop(
|
||||||
@@ -137,9 +142,10 @@ pub trait Resource: Clone + 'static {
|
|||||||
Error::BadRequest("propname MUST be the only queried prop".to_owned()).into(),
|
Error::BadRequest("propname MUST be the only queried prop".to_owned()).into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let props = Self::list_props()
|
let props = Self::list_props()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(str::to_string)
|
.map(|(ns, tag)| (ns.to_owned(), tag.to_string()))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
return Ok(ResponseElement {
|
return Ok(ResponseElement {
|
||||||
@@ -159,7 +165,10 @@ pub trait Resource: Clone + 'static {
|
|||||||
Error::BadRequest("allprop MUST be the only queried prop".to_owned()).into(),
|
Error::BadRequest("allprop MUST be the only queried prop".to_owned()).into(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
props = Self::list_props();
|
props = Self::list_props()
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_ns, tag)| tag)
|
||||||
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut valid_props = vec![];
|
let mut valid_props = vec![];
|
||||||
@@ -195,7 +204,11 @@ pub trait Resource: Clone + 'static {
|
|||||||
if !invalid_props.is_empty() {
|
if !invalid_props.is_empty() {
|
||||||
propstats.push(PropstatWrapper::TagList(PropstatElement {
|
propstats.push(PropstatWrapper::TagList(PropstatElement {
|
||||||
status: StatusCode::NOT_FOUND,
|
status: StatusCode::NOT_FOUND,
|
||||||
prop: invalid_props.into(),
|
prop: invalid_props
|
||||||
|
.into_iter()
|
||||||
|
.map(|tag| (None, tag))
|
||||||
|
.collect_vec()
|
||||||
|
.into(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
Ok(ResponseElement {
|
Ok(ResponseElement {
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ use crate::xml::{Resourcetype, ResourcetypeInner};
|
|||||||
use actix_web::dev::ResourceMap;
|
use actix_web::dev::ResourceMap;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use strum::{EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumString, IntoStaticStr};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RootResource<PR: Resource>(PhantomData<PR>);
|
pub struct RootResource<PR: Resource>(PhantomData<PR>);
|
||||||
@@ -18,11 +18,11 @@ impl<PR: Resource> Default for RootResource<PR> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(EnumString, VariantNames, Clone, IntoStaticStr)]
|
#[derive(EnumString, Clone, IntoStaticStr)]
|
||||||
#[strum(serialize_all = "kebab-case")]
|
#[strum(serialize_all = "kebab-case")]
|
||||||
pub enum RootResourcePropName {}
|
pub enum RootResourcePropName {}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, Serialize, Clone, PartialEq)]
|
#[derive(XmlDeserialize, XmlSerialize, Serialize, Clone, PartialEq, EnumVariants)]
|
||||||
pub enum RootResourceProp {}
|
pub enum RootResourceProp {}
|
||||||
|
|
||||||
impl From<RootResourceProp> for RootResourcePropName {
|
impl From<RootResourceProp> for RootResourcePropName {
|
||||||
@@ -38,7 +38,10 @@ impl<PR: Resource + NamedRoute> Resource for RootResource<PR> {
|
|||||||
type PrincipalResource = PR;
|
type PrincipalResource = PR;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
Resourcetype(&[ResourcetypeInner(crate::namespace::NS_DAV, "collection")])
|
Resourcetype(&[ResourcetypeInner(
|
||||||
|
Some(crate::namespace::NS_DAV),
|
||||||
|
"collection",
|
||||||
|
)])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prop(
|
fn get_prop(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ pub struct Resourcetype(#[xml(flatten, ty = "untagged")] pub &'static [Resourcet
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, XmlSerialize)]
|
#[derive(Debug, Clone, PartialEq, XmlSerialize)]
|
||||||
pub struct ResourcetypeInner(
|
pub struct ResourcetypeInner(
|
||||||
#[xml(ty = "namespace")] pub quick_xml::name::Namespace<'static>,
|
#[xml(ty = "namespace")] pub Option<quick_xml::name::Namespace<'static>>,
|
||||||
#[xml(ty = "tag_name")] pub &'static str,
|
#[xml(ty = "tag_name")] pub &'static str,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -27,8 +27,8 @@ mod tests {
|
|||||||
let mut writer = quick_xml::Writer::new(&mut buf);
|
let mut writer = quick_xml::Writer::new(&mut buf);
|
||||||
Document {
|
Document {
|
||||||
resourcetype: Resourcetype(&[
|
resourcetype: Resourcetype(&[
|
||||||
ResourcetypeInner(crate::namespace::NS_DAV, "displayname"),
|
ResourcetypeInner(Some(crate::namespace::NS_DAV), "displayname"),
|
||||||
ResourcetypeInner(crate::namespace::NS_CALENDARSERVER, "calendar-color"),
|
ResourcetypeInner(Some(crate::namespace::NS_CALENDARSERVER), "calendar-color"),
|
||||||
]),
|
]),
|
||||||
}
|
}
|
||||||
.serialize_root(&mut writer)
|
.serialize_root(&mut writer)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use rustical_xml::XmlSerialize;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, From)]
|
#[derive(Clone, Debug, PartialEq, From)]
|
||||||
pub struct TagList(Vec<String>);
|
pub struct TagList(Vec<(Option<Namespace<'static>>, String)>);
|
||||||
|
|
||||||
impl XmlSerialize for TagList {
|
impl XmlSerialize for TagList {
|
||||||
fn serialize<W: std::io::Write>(
|
fn serialize<W: std::io::Write>(
|
||||||
@@ -18,9 +18,18 @@ impl XmlSerialize for TagList {
|
|||||||
struct Inner(#[xml(ty = "untagged", flatten)] Vec<Tag>);
|
struct Inner(#[xml(ty = "untagged", flatten)] Vec<Tag>);
|
||||||
|
|
||||||
#[derive(Debug, XmlSerialize, PartialEq)]
|
#[derive(Debug, XmlSerialize, PartialEq)]
|
||||||
struct Tag(#[xml(ty = "tag_name")] String);
|
struct Tag(
|
||||||
Inner(self.0.iter().map(|t| Tag(t.to_owned())).collect())
|
#[xml(ty = "namespace")] Option<Namespace<'static>>,
|
||||||
.serialize(ns, tag, namespaces, writer)
|
#[xml(ty = "tag_name")] String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Inner(
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.map(|(ns, tag)| Tag(ns.to_owned(), tag.to_owned()))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
.serialize(ns, tag, namespaces, writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(refining_impl_trait)]
|
#[allow(refining_impl_trait)]
|
||||||
|
|||||||
Reference in New Issue
Block a user