mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 14:02:29 +00:00
xml: Strict namespace, some tests and restructuring
This commit is contained in:
@@ -21,6 +21,7 @@ pub(crate) struct CalendarMultigetRequest {
|
||||
#[xml(ty = "untagged")]
|
||||
pub(crate) prop: PropfindType,
|
||||
#[xml(flatten)]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
pub(crate) href: Vec<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -19,15 +19,19 @@ use crate::{
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct TimeRangeElement {
|
||||
#[xml(ty = "attr")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) start: Option<UtcDateTime>,
|
||||
#[xml(ty = "attr")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) end: Option<UtcDateTime>,
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
struct ParamFilterElement {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
is_not_defined: Option<()>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
text_match: Option<TextMatchElement>,
|
||||
|
||||
#[xml(ty = "attr")]
|
||||
@@ -38,18 +42,24 @@ struct ParamFilterElement {
|
||||
#[allow(dead_code)]
|
||||
struct TextMatchElement {
|
||||
#[xml(ty = "attr")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
collation: String,
|
||||
#[xml(ty = "attr")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
negate_collation: String,
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct PropFilterElement {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
is_not_defined: Option<()>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
time_range: Option<TimeRangeElement>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
text_match: Option<TextMatchElement>,
|
||||
#[xml(flatten)]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
param_filter: Vec<ParamFilterElement>,
|
||||
}
|
||||
|
||||
@@ -57,14 +67,16 @@ pub(crate) struct PropFilterElement {
|
||||
#[allow(dead_code)]
|
||||
// https://datatracker.ietf.org/doc/html/rfc4791#section-9.7.1
|
||||
pub(crate) struct CompFilterElement {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) is_not_defined: Option<()>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) time_range: Option<TimeRangeElement>,
|
||||
#[xml(flatten)]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", flatten)]
|
||||
pub(crate) prop_filter: Vec<PropFilterElement>,
|
||||
#[xml(flatten)]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", flatten)]
|
||||
pub(crate) comp_filter: Vec<CompFilterElement>,
|
||||
|
||||
#[xml(ty = "attr")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", ty = "attr")]
|
||||
pub(crate) name: String,
|
||||
}
|
||||
|
||||
@@ -155,6 +167,7 @@ impl CompFilterElement {
|
||||
#[allow(dead_code)]
|
||||
// https://datatracker.ietf.org/doc/html/rfc4791#section-9.7
|
||||
pub(crate) struct FilterElement {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) comp_filter: CompFilterElement,
|
||||
}
|
||||
|
||||
@@ -170,8 +183,11 @@ impl FilterElement {
|
||||
pub struct CalendarQueryRequest {
|
||||
#[xml(ty = "untagged")]
|
||||
pub prop: PropfindType,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) filter: Option<FilterElement>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) timezone: Option<String>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) timezone_id: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@ use actix_web::{
|
||||
};
|
||||
use calendar_multiget::{handle_calendar_multiget, CalendarMultigetRequest};
|
||||
use calendar_query::{handle_calendar_query, CalendarQueryRequest};
|
||||
use rustical_dav::xml::sync_collection::SyncCollectionRequest;
|
||||
use rustical_store::{auth::User, CalendarStore};
|
||||
use rustical_xml::{XmlDeserialize, XmlDocument};
|
||||
use sync_collection::{handle_sync_collection, SyncCollectionRequest};
|
||||
use sync_collection::handle_sync_collection;
|
||||
use tracing::instrument;
|
||||
|
||||
mod calendar_multiget;
|
||||
@@ -16,8 +17,11 @@ mod sync_collection;
|
||||
|
||||
#[derive(XmlDeserialize, XmlDocument, Clone, Debug, PartialEq)]
|
||||
pub(crate) enum ReportRequest {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
CalendarMultiget(CalendarMultigetRequest),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
CalendarQuery(CalendarQueryRequest),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
SyncCollection(SyncCollectionRequest),
|
||||
}
|
||||
|
||||
@@ -133,4 +137,31 @@ mod tests {
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xml_calendar_multiget() {
|
||||
let report_request = ReportRequest::parse_str(r#"
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<calendar-multiget xmlns="urn:ietf:params:xml:ns:caldav" xmlns:D="DAV:">
|
||||
<D:prop>
|
||||
<D:getetag/>
|
||||
<D:displayname/>
|
||||
</D:prop>
|
||||
<D:href>/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b</D:href>
|
||||
</calendar-multiget>
|
||||
"#).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
report_request,
|
||||
ReportRequest::CalendarMultiget(CalendarMultigetRequest {
|
||||
prop: rustical_dav::xml::PropfindType::Prop(PropElement(vec![
|
||||
Propname("getetag".to_owned()),
|
||||
Propname("displayname".to_owned())
|
||||
])),
|
||||
href: vec![
|
||||
"/caldav/user/user/6f787542-5256-401a-8db97003260da/ae7a998fdfd1d84a20391168962c62b".to_owned()
|
||||
]
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +1,22 @@
|
||||
use actix_web::{http::StatusCode, HttpRequest};
|
||||
use rustical_dav::{
|
||||
resource::{CommonPropertiesProp, EitherProp, Resource},
|
||||
xml::{multistatus::ResponseElement, MultistatusElement},
|
||||
xml::{PropElement, PropfindType},
|
||||
xml::{
|
||||
multistatus::ResponseElement, sync_collection::SyncCollectionRequest, MultistatusElement,
|
||||
PropElement, PropfindType,
|
||||
},
|
||||
};
|
||||
use rustical_store::{
|
||||
auth::User,
|
||||
synctoken::{format_synctoken, parse_synctoken},
|
||||
CalendarStore,
|
||||
};
|
||||
use rustical_xml::{ValueDeserialize, ValueSerialize, XmlDeserialize};
|
||||
|
||||
use crate::{
|
||||
calendar_object::resource::{CalendarObjectProp, CalendarObjectResource},
|
||||
Error,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) enum SyncLevel {
|
||||
One,
|
||||
Infinity,
|
||||
}
|
||||
|
||||
impl ValueDeserialize for SyncLevel {
|
||||
fn deserialize(val: &str) -> Result<Self, rustical_xml::XmlError> {
|
||||
Ok(match val {
|
||||
"1" => Self::One,
|
||||
"Infinity" => Self::Infinity,
|
||||
_ => {
|
||||
return Err(rustical_xml::XmlError::Other(
|
||||
"Invalid sync-level".to_owned(),
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueSerialize for SyncLevel {
|
||||
fn serialize(&self) -> String {
|
||||
match self {
|
||||
SyncLevel::One => "1",
|
||||
SyncLevel::Infinity => "Infinity",
|
||||
}
|
||||
.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
// <!ELEMENT sync-collection (sync-token, sync-level, limit?, prop)>
|
||||
// <!-- DAV:limit defined in RFC 5323, Section 5.17 -->
|
||||
// <!-- DAV:prop defined in RFC 4918, Section 14.18 -->
|
||||
pub(crate) struct SyncCollectionRequest {
|
||||
pub(crate) sync_token: String,
|
||||
pub(crate) sync_level: SyncLevel,
|
||||
pub(crate) timezone: Option<String>,
|
||||
#[xml(ty = "untagged")]
|
||||
pub prop: PropfindType,
|
||||
pub(crate) limit: Option<u64>,
|
||||
}
|
||||
|
||||
pub async fn handle_sync_collection<C: CalendarStore>(
|
||||
sync_collection: SyncCollectionRequest,
|
||||
req: HttpRequest,
|
||||
|
||||
Reference in New Issue
Block a user