mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 21:42:34 +00:00
caldav: calendar-query fix xml
This commit is contained in:
22
Cargo.lock
generated
22
Cargo.lock
generated
@@ -2999,7 +2999,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical"
|
name = "rustical"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argon2",
|
"argon2",
|
||||||
@@ -3042,7 +3042,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_caldav"
|
name = "rustical_caldav"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3080,7 +3080,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_carddav"
|
name = "rustical_carddav"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3112,7 +3112,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_dav"
|
name = "rustical_dav"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3137,7 +3137,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_dav_push"
|
name = "rustical_dav_push"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3163,7 +3163,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_frontend"
|
name = "rustical_frontend"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
"askama_web",
|
"askama_web",
|
||||||
@@ -3196,7 +3196,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_ical"
|
name = "rustical_ical"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -3214,7 +3214,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_oidc"
|
name = "rustical_oidc"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3229,7 +3229,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_store"
|
name = "rustical_store"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -3263,7 +3263,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_store_sqlite"
|
name = "rustical_store_sqlite"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -3284,7 +3284,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustical_xml"
|
name = "rustical_xml"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ pub(crate) struct TimeRangeElement {
|
|||||||
|
|
||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc4791#section-9.7.3
|
||||||
struct ParamFilterElement {
|
struct ParamFilterElement {
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
is_not_defined: Option<()>,
|
is_not_defined: Option<()>,
|
||||||
@@ -32,11 +33,13 @@ struct TextMatchElement {
|
|||||||
#[xml(ty = "attr")]
|
#[xml(ty = "attr")]
|
||||||
collation: String,
|
collation: String,
|
||||||
#[xml(ty = "attr")]
|
#[xml(ty = "attr")]
|
||||||
negate_collation: String,
|
// "yes" or "no", default: "no"
|
||||||
|
negate_condition: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc4791#section-9.7.2
|
||||||
pub(crate) struct PropFilterElement {
|
pub(crate) struct PropFilterElement {
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
is_not_defined: Option<()>,
|
is_not_defined: Option<()>,
|
||||||
@@ -46,6 +49,9 @@ pub(crate) struct PropFilterElement {
|
|||||||
text_match: Option<TextMatchElement>,
|
text_match: Option<TextMatchElement>,
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", flatten)]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", flatten)]
|
||||||
param_filter: Vec<ParamFilterElement>,
|
param_filter: Vec<ParamFilterElement>,
|
||||||
|
|
||||||
|
#[xml(ty = "attr")]
|
||||||
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||||
@@ -61,7 +67,7 @@ pub(crate) struct CompFilterElement {
|
|||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", flatten)]
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", flatten)]
|
||||||
pub(crate) comp_filter: Vec<CompFilterElement>,
|
pub(crate) comp_filter: Vec<CompFilterElement>,
|
||||||
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", ty = "attr")]
|
#[xml(ty = "attr")]
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,3 +209,102 @@ pub async fn get_objects_calendar_query<C: CalendarStore>(
|
|||||||
}
|
}
|
||||||
Ok(objects)
|
Ok(objects)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use rustical_dav::xml::PropElement;
|
||||||
|
use rustical_xml::XmlDocument;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
calendar::methods::report::{
|
||||||
|
ReportRequest,
|
||||||
|
calendar_query::{
|
||||||
|
CalendarQueryRequest, CompFilterElement, FilterElement, ParamFilterElement,
|
||||||
|
PropFilterElement, TextMatchElement,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
calendar_object::{CalendarObjectPropName, CalendarObjectPropWrapperName},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn calendar_query_7_8_7() {
|
||||||
|
const INPUT: &str = r#"
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||||
|
<D:prop xmlns:D="DAV:">
|
||||||
|
<D:getetag/>
|
||||||
|
<C:calendar-data/>
|
||||||
|
</D:prop>
|
||||||
|
<C:filter>
|
||||||
|
<C:comp-filter name="VCALENDAR">
|
||||||
|
<C:comp-filter name="VEVENT">
|
||||||
|
<C:prop-filter name="ATTENDEE">
|
||||||
|
<C:text-match collation="i;ascii-casemap">mailto:lisa@example.com</C:text-match>
|
||||||
|
<C:param-filter name="PARTSTAT">
|
||||||
|
<C:text-match collation="i;ascii-casemap">NEEDS-ACTION</C:text-match>
|
||||||
|
</C:param-filter>
|
||||||
|
</C:prop-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:comp-filter>
|
||||||
|
</C:filter>
|
||||||
|
</C:calendar-query>
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let report = ReportRequest::parse_str(INPUT).unwrap();
|
||||||
|
let calendar_query: CalendarQueryRequest =
|
||||||
|
if let ReportRequest::CalendarQuery(query) = report {
|
||||||
|
query
|
||||||
|
} else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
calendar_query,
|
||||||
|
CalendarQueryRequest {
|
||||||
|
prop: rustical_dav::xml::PropfindType::Prop(PropElement(
|
||||||
|
vec![
|
||||||
|
CalendarObjectPropWrapperName::CalendarObject(
|
||||||
|
CalendarObjectPropName::Getetag,
|
||||||
|
),
|
||||||
|
CalendarObjectPropWrapperName::CalendarObject(
|
||||||
|
CalendarObjectPropName::CalendarData(Default::default())
|
||||||
|
),
|
||||||
|
],
|
||||||
|
vec![]
|
||||||
|
)),
|
||||||
|
filter: Some(FilterElement {
|
||||||
|
comp_filter: CompFilterElement {
|
||||||
|
is_not_defined: None,
|
||||||
|
time_range: None,
|
||||||
|
prop_filter: vec![],
|
||||||
|
comp_filter: vec![CompFilterElement {
|
||||||
|
prop_filter: vec![PropFilterElement {
|
||||||
|
name: "ATTENDEE".to_owned(),
|
||||||
|
text_match: Some(TextMatchElement {
|
||||||
|
collation: "i;ascii-casemap".to_owned(),
|
||||||
|
negate_condition: None
|
||||||
|
}),
|
||||||
|
is_not_defined: None,
|
||||||
|
param_filter: vec![ParamFilterElement {
|
||||||
|
is_not_defined: None,
|
||||||
|
name: "PARTSTAT".to_owned(),
|
||||||
|
text_match: Some(TextMatchElement {
|
||||||
|
collation: "i;ascii-casemap".to_owned(),
|
||||||
|
negate_condition: None
|
||||||
|
}),
|
||||||
|
}],
|
||||||
|
time_range: None
|
||||||
|
}],
|
||||||
|
comp_filter: vec![],
|
||||||
|
is_not_defined: None,
|
||||||
|
name: "VEVENT".to_owned(),
|
||||||
|
time_range: None
|
||||||
|
}],
|
||||||
|
name: "VCALENDAR".to_owned()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
timezone: None,
|
||||||
|
timezone_id: None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user