better support for time-range
This commit is contained in:
parent
52f870633c
commit
6b9720844a
8 changed files with 187 additions and 112 deletions
|
@ -917,15 +917,11 @@ impl QRead<TimeRange> for TimeRange {
|
|||
xml.open(CAL_URN, "time-range").await?;
|
||||
|
||||
let start = match xml.prev_attr("start") {
|
||||
Some(r) => {
|
||||
Some(NaiveDateTime::parse_from_str(r.as_str(), UTC_DATETIME_FMT)?.and_utc())
|
||||
}
|
||||
Some(r) => Some(NaiveDateTime::parse_from_str(r.as_str(), UTC_DATETIME_FMT)?.and_utc()),
|
||||
_ => None,
|
||||
};
|
||||
let end = match xml.prev_attr("end") {
|
||||
Some(r) => {
|
||||
Some(NaiveDateTime::parse_from_str(r.as_str(), UTC_DATETIME_FMT)?.and_utc())
|
||||
}
|
||||
Some(r) => Some(NaiveDateTime::parse_from_str(r.as_str(), UTC_DATETIME_FMT)?.and_utc()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
|
|
@ -739,19 +739,15 @@ impl QWrite for TimeRange {
|
|||
"start",
|
||||
format!("{}", start.format(UTC_DATETIME_FMT)).as_str(),
|
||||
)),
|
||||
Self::OnlyEnd(end) => empty.push_attribute((
|
||||
"end",
|
||||
format!("{}", end.format(UTC_DATETIME_FMT)).as_str(),
|
||||
)),
|
||||
Self::OnlyEnd(end) => {
|
||||
empty.push_attribute(("end", format!("{}", end.format(UTC_DATETIME_FMT)).as_str()))
|
||||
}
|
||||
Self::FullRange(start, end) => {
|
||||
empty.push_attribute((
|
||||
"start",
|
||||
format!("{}", start.format(UTC_DATETIME_FMT)).as_str(),
|
||||
));
|
||||
empty.push_attribute((
|
||||
"end",
|
||||
format!("{}", end.format(UTC_DATETIME_FMT)).as_str(),
|
||||
));
|
||||
empty.push_attribute(("end", format!("{}", end.format(UTC_DATETIME_FMT)).as_str()));
|
||||
}
|
||||
}
|
||||
xml.q.write_event_async(Event::Empty(empty)).await
|
||||
|
|
|
@ -3,6 +3,5 @@
|
|||
/// However, for many reason, it's not satisfying:
|
||||
/// the goal will be to rewrite it in the end so it better
|
||||
/// integrates into Aerogramme
|
||||
|
||||
pub mod parser;
|
||||
pub mod query;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use chrono::TimeDelta;
|
||||
|
||||
use nom::IResult;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::{tag, tag_no_case};
|
||||
use nom::combinator::{value, opt, map, map_opt};
|
||||
use nom::sequence::{pair, tuple};
|
||||
use nom::character::complete as nomchar;
|
||||
use nom::combinator::{map, map_opt, opt, value};
|
||||
use nom::sequence::{pair, tuple};
|
||||
use nom::IResult;
|
||||
|
||||
use aero_dav::caltypes as cal;
|
||||
|
||||
|
@ -19,10 +19,13 @@ pub fn date_time(dt: &str) -> Option<chrono::DateTime<chrono::Utc>> {
|
|||
let tmpl = match dt.chars().last() {
|
||||
Some('Z') => cal::UTC_DATETIME_FMT,
|
||||
Some(_) => {
|
||||
tracing::warn!(raw_time=dt, "floating datetime is not properly supported yet");
|
||||
tracing::warn!(
|
||||
raw_time = dt,
|
||||
"floating datetime is not properly supported yet"
|
||||
);
|
||||
cal::FLOATING_DATETIME_FMT
|
||||
},
|
||||
None => return None
|
||||
}
|
||||
None => return None,
|
||||
};
|
||||
|
||||
chrono::NaiveDateTime::parse_from_str(dt, tmpl)
|
||||
|
@ -43,46 +46,58 @@ pub fn date_time(dt: &str) -> Option<chrono::DateTime<chrono::Utc>> {
|
|||
/// dur-day = 1*DIGIT "D"
|
||||
/// ```
|
||||
pub fn dur_value(text: &str) -> IResult<&str, TimeDelta> {
|
||||
map_opt(tuple((
|
||||
dur_sign,
|
||||
tag_no_case("P"),
|
||||
alt((
|
||||
dur_date,
|
||||
dur_time,
|
||||
dur_week,
|
||||
))
|
||||
)), |(sign, _, delta)| {
|
||||
delta.checked_mul(sign)
|
||||
})(text)
|
||||
map_opt(
|
||||
tuple((
|
||||
dur_sign,
|
||||
tag_no_case("P"),
|
||||
alt((dur_date, dur_time, dur_week)),
|
||||
)),
|
||||
|(sign, _, delta)| delta.checked_mul(sign),
|
||||
)(text)
|
||||
}
|
||||
|
||||
fn dur_sign(text: &str) -> IResult<&str, i32> {
|
||||
map(opt(alt((value(1, tag("+")), value(-1, tag("-"))))), |x| x.unwrap_or(1))(text)
|
||||
map(opt(alt((value(1, tag("+")), value(-1, tag("-"))))), |x| {
|
||||
x.unwrap_or(1)
|
||||
})(text)
|
||||
}
|
||||
fn dur_date(text: &str) -> IResult<&str, TimeDelta> {
|
||||
map(pair(dur_day, opt(dur_time)), |(day, time)| day + time.unwrap_or(TimeDelta::zero()))(text)
|
||||
map(pair(dur_day, opt(dur_time)), |(day, time)| {
|
||||
day + time.unwrap_or(TimeDelta::zero())
|
||||
})(text)
|
||||
}
|
||||
fn dur_time(text: &str) -> IResult<&str, TimeDelta> {
|
||||
map(pair(tag_no_case("T"), alt((dur_hour, dur_minute, dur_second))), |(_, x)| x)(text)
|
||||
map(
|
||||
pair(tag_no_case("T"), alt((dur_hour, dur_minute, dur_second))),
|
||||
|(_, x)| x,
|
||||
)(text)
|
||||
}
|
||||
fn dur_week(text: &str) -> IResult<&str, TimeDelta> {
|
||||
map_opt(pair(nomchar::i64, tag_no_case("W")), |(i, _)| TimeDelta::try_weeks(i))(text)
|
||||
map_opt(pair(nomchar::i64, tag_no_case("W")), |(i, _)| {
|
||||
TimeDelta::try_weeks(i)
|
||||
})(text)
|
||||
}
|
||||
fn dur_day(text: &str) -> IResult<&str, TimeDelta> {
|
||||
map_opt(pair(nomchar::i64, tag_no_case("D")), |(i, _)| TimeDelta::try_days(i))(text)
|
||||
map_opt(pair(nomchar::i64, tag_no_case("D")), |(i, _)| {
|
||||
TimeDelta::try_days(i)
|
||||
})(text)
|
||||
}
|
||||
fn dur_hour(text: &str) -> IResult<&str, TimeDelta> {
|
||||
map_opt(tuple((nomchar::i64, tag_no_case("H"), opt(dur_minute))), |(i, _, mm)| {
|
||||
TimeDelta::try_hours(i).map(|hours| hours + mm.unwrap_or(TimeDelta::zero()))
|
||||
})(text)
|
||||
map_opt(
|
||||
tuple((nomchar::i64, tag_no_case("H"), opt(dur_minute))),
|
||||
|(i, _, mm)| TimeDelta::try_hours(i).map(|hours| hours + mm.unwrap_or(TimeDelta::zero())),
|
||||
)(text)
|
||||
}
|
||||
fn dur_minute(text: &str) -> IResult<&str, TimeDelta> {
|
||||
map_opt(tuple((nomchar::i64, tag_no_case("M"), opt(dur_second))), |(i, _, ms)| {
|
||||
TimeDelta::try_minutes(i).map(|min| min + ms.unwrap_or(TimeDelta::zero()))
|
||||
})(text)
|
||||
map_opt(
|
||||
tuple((nomchar::i64, tag_no_case("M"), opt(dur_second))),
|
||||
|(i, _, ms)| TimeDelta::try_minutes(i).map(|min| min + ms.unwrap_or(TimeDelta::zero())),
|
||||
)(text)
|
||||
}
|
||||
fn dur_second(text: &str) -> IResult<&str, TimeDelta> {
|
||||
map_opt(pair(nomchar::i64, tag_no_case("S")), |(i, _)| TimeDelta::try_seconds(i))(text)
|
||||
map_opt(pair(nomchar::i64, tag_no_case("S")), |(i, _)| {
|
||||
TimeDelta::try_seconds(i)
|
||||
})(text)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -95,8 +110,11 @@ mod tests {
|
|||
let to_parse = "P15DT5H0M20S";
|
||||
let (_, time_delta) = dur_value(to_parse).unwrap();
|
||||
assert_eq!(
|
||||
time_delta,
|
||||
TimeDelta::try_days(15).unwrap() + TimeDelta::try_hours(5).unwrap() + TimeDelta::try_seconds(20).unwrap());
|
||||
time_delta,
|
||||
TimeDelta::try_days(15).unwrap()
|
||||
+ TimeDelta::try_hours(5).unwrap()
|
||||
+ TimeDelta::try_seconds(20).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -104,35 +122,25 @@ mod tests {
|
|||
// A duration of 7 weeks would be:
|
||||
let to_parse = "P7W";
|
||||
let (_, time_delta) = dur_value(to_parse).unwrap();
|
||||
assert_eq!(
|
||||
time_delta,
|
||||
TimeDelta::try_weeks(7).unwrap()
|
||||
);
|
||||
assert_eq!(time_delta, TimeDelta::try_weeks(7).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rfc4791_example1() {
|
||||
fn rfc4791_example1() {
|
||||
// 10 minutes before
|
||||
let to_parse = "-PT10M";
|
||||
|
||||
let (_, time_delta) = dur_value(to_parse).unwrap();
|
||||
assert_eq!(
|
||||
time_delta,
|
||||
TimeDelta::try_minutes(-10).unwrap()
|
||||
);
|
||||
assert_eq!(time_delta, TimeDelta::try_minutes(-10).unwrap());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn ical_org_example1() {
|
||||
// The following example is for a "VALARM" calendar component that specifies an email alarm
|
||||
// The following example is for a "VALARM" calendar component that specifies an email alarm
|
||||
// that will trigger 2 days before the scheduled due DATE-TIME of a to-do with which it is associated.
|
||||
let to_parse = "-P2D";
|
||||
|
||||
let (_, time_delta) = dur_value(to_parse).unwrap();
|
||||
assert_eq!(
|
||||
time_delta,
|
||||
TimeDelta::try_days(-2).unwrap()
|
||||
);
|
||||
assert_eq!(time_delta, TimeDelta::try_days(-2).unwrap());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::parser;
|
||||
use aero_dav::caltypes as cal;
|
||||
use crate::parser as parser;
|
||||
|
||||
pub fn is_component_match(
|
||||
parent: &icalendar::parser::Component,
|
||||
|
@ -7,6 +7,7 @@ pub fn is_component_match(
|
|||
filter: &cal::CompFilter,
|
||||
) -> bool {
|
||||
// Find the component among the list
|
||||
//@FIXME do not handle correctly multiple entities (eg. 3 VEVENT)
|
||||
let maybe_comp = components
|
||||
.iter()
|
||||
.find(|candidate| candidate.name.as_str() == filter.name.as_str());
|
||||
|
@ -21,7 +22,12 @@ pub fn is_component_match(
|
|||
(Some(component), Some(cal::CompFilterRules::Matches(matcher))) => {
|
||||
// check time range
|
||||
if let Some(time_range) = &matcher.time_range {
|
||||
if !is_in_time_range(&filter.name, parent, component.properties.as_ref(), time_range) {
|
||||
if !is_in_time_range(
|
||||
&filter.name,
|
||||
parent,
|
||||
component.properties.as_ref(),
|
||||
time_range,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +83,7 @@ fn is_properties_match(props: &[icalendar::parser::Property], filters: &[cal::Pr
|
|||
// check value
|
||||
match &pattern.time_or_text {
|
||||
Some(cal::TimeOrText::Time(time_range)) => {
|
||||
let maybe_parsed_date = parser::date_time(prop.val.as_str());
|
||||
let maybe_parsed_date = parser::date_time(prop.val.as_str());
|
||||
|
||||
let parsed_date = match maybe_parsed_date {
|
||||
None => return false,
|
||||
|
@ -146,8 +152,8 @@ fn is_properties_match(props: &[icalendar::parser::Property], filters: &[cal::Pr
|
|||
}
|
||||
|
||||
fn resolve_trigger(
|
||||
parent: &icalendar::parser::Component,
|
||||
properties: &[icalendar::parser::Property]
|
||||
parent: &icalendar::parser::Component,
|
||||
properties: &[icalendar::parser::Property],
|
||||
) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||
// A. Do we have a TRIGGER property? If not, returns early
|
||||
let maybe_trigger_prop = properties
|
||||
|
@ -160,34 +166,56 @@ fn resolve_trigger(
|
|||
};
|
||||
|
||||
// B.1 Is it an absolute datetime? If so, returns early
|
||||
let maybe_absolute = trigger_prop.params.iter()
|
||||
let maybe_absolute = trigger_prop
|
||||
.params
|
||||
.iter()
|
||||
.find(|param| param.key.as_str() == "VALUE")
|
||||
.map(|param| param.val.as_ref()).flatten()
|
||||
.map(|param| param.val.as_ref())
|
||||
.flatten()
|
||||
.map(|v| v.as_str() == "DATE-TIME");
|
||||
|
||||
if maybe_absolute.is_some() {
|
||||
return prop_date(properties, "TRIGGER");
|
||||
let final_date = prop_date(properties, "TRIGGER");
|
||||
tracing::trace!(trigger=?final_date, "resolved absolute trigger");
|
||||
return final_date;
|
||||
}
|
||||
|
||||
// B.2 Otherwise it's a timedelta relative to a parent field.
|
||||
// C.1 Parse the timedelta value, returns early if invalid
|
||||
let (_, time_delta) = parser::dur_value(trigger_prop.val.as_str()).ok()?;
|
||||
|
||||
// C.2 Get the parent reference absolute datetime, returns early if invalid
|
||||
let maybe_related_field = trigger_prop
|
||||
let maybe_bound = trigger_prop
|
||||
.params
|
||||
.iter()
|
||||
.find(|param| param.key.as_str() == "RELATED")
|
||||
.map(|param| param.val.as_ref())
|
||||
.flatten();
|
||||
let related_field = maybe_related_field.map(|v| v.as_str()).unwrap_or("DTSTART");
|
||||
|
||||
// If the trigger is set relative to START, then the "DTSTART" property MUST be present in the associated
|
||||
// "VEVENT" or "VTODO" calendar component.
|
||||
//
|
||||
// If an alarm is specified for an event with the trigger set relative to the END,
|
||||
// then the "DTEND" property or the "DTSTART" and "DURATION " properties MUST be present
|
||||
// in the associated "VEVENT" calendar component.
|
||||
//
|
||||
// If the alarm is specified for a to-do with a trigger set relative to the END,
|
||||
// then either the "DUE" property or the "DTSTART" and "DURATION " properties
|
||||
// MUST be present in the associated "VTODO" calendar component.
|
||||
let related_field = match maybe_bound.as_ref().map(|v| v.as_str()) {
|
||||
Some("START") => "DTSTART",
|
||||
Some("END") => "DTEND", //@FIXME must add support for DUE, DTSTART, and DURATION
|
||||
_ => "DTSTART", // by default use DTSTART
|
||||
};
|
||||
let parent_date = match prop_date(parent.properties.as_ref(), related_field) {
|
||||
Some(v) => v,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// C.3 Compute the final date from the base date + timedelta
|
||||
|
||||
todo!()
|
||||
let final_date = parent_date + time_delta;
|
||||
tracing::trace!(trigger=?final_date, "resolved relative trigger");
|
||||
Some(final_date)
|
||||
}
|
||||
|
||||
fn is_in_time_range(
|
||||
|
@ -209,10 +237,12 @@ fn is_in_time_range(
|
|||
cal::Component::VEvent => {
|
||||
let dtstart = match prop_date(properties, "DTSTART") {
|
||||
Some(v) => v,
|
||||
_ => return false,
|
||||
_ => return false,
|
||||
};
|
||||
let maybe_dtend = prop_date(properties, "DTEND");
|
||||
let maybe_duration = prop_parse::<i64>(properties, "DURATION").map(|d| chrono::TimeDelta::new(std::cmp::max(d, 0), 0)).flatten();
|
||||
let maybe_duration = prop_parse::<i64>(properties, "DURATION")
|
||||
.map(|d| chrono::TimeDelta::new(std::cmp::max(d, 0), 0))
|
||||
.flatten();
|
||||
|
||||
//@FIXME missing "date" management (only support "datetime")
|
||||
match (&maybe_dtend, &maybe_duration) {
|
||||
|
@ -223,23 +253,35 @@ fn is_in_time_range(
|
|||
// | N | N | N | Y | (start <= DTSTART AND end > DTSTART) |
|
||||
_ => start <= &dtstart && end > &dtstart,
|
||||
}
|
||||
},
|
||||
}
|
||||
cal::Component::VTodo => {
|
||||
let maybe_dtstart = prop_date(properties, "DTSTART");
|
||||
let maybe_due = prop_date(properties, "DUE");
|
||||
let maybe_completed = prop_date(properties, "COMPLETED");
|
||||
let maybe_created = prop_date(properties, "CREATED");
|
||||
let maybe_duration = prop_parse::<i64>(properties, "DURATION").map(|d| chrono::TimeDelta::new(d, 0)).flatten();
|
||||
let maybe_duration = prop_parse::<i64>(properties, "DURATION")
|
||||
.map(|d| chrono::TimeDelta::new(d, 0))
|
||||
.flatten();
|
||||
|
||||
match (maybe_dtstart, maybe_duration, maybe_due, maybe_completed, maybe_created) {
|
||||
match (
|
||||
maybe_dtstart,
|
||||
maybe_duration,
|
||||
maybe_due,
|
||||
maybe_completed,
|
||||
maybe_created,
|
||||
) {
|
||||
// | Y | Y | N | * | * | (start <= DTSTART+DURATION) AND |
|
||||
// | | | | | | ((end > DTSTART) OR |
|
||||
// | | | | | | (end >= DTSTART+DURATION)) |
|
||||
(Some(dtstart), Some(duration), None, _, _) => *start <= dtstart + duration && (*end > dtstart || *end >= dtstart + duration),
|
||||
(Some(dtstart), Some(duration), None, _, _) => {
|
||||
*start <= dtstart + duration && (*end > dtstart || *end >= dtstart + duration)
|
||||
}
|
||||
// | Y | N | Y | * | * | ((start < DUE) OR (start <= DTSTART)) |
|
||||
// | | | | | | AND |
|
||||
// | | | | | | ((end > DTSTART) OR (end >= DUE)) |
|
||||
(Some(dtstart), None, Some(due), _, _) => (*start < due || *start <= dtstart) && (*end > dtstart || *end >= due),
|
||||
(Some(dtstart), None, Some(due), _, _) => {
|
||||
(*start < due || *start <= dtstart) && (*end > dtstart || *end >= due)
|
||||
}
|
||||
// | Y | N | N | * | * | (start <= DTSTART) AND (end > DTSTART) |
|
||||
(Some(dtstart), None, None, _, _) => *start <= dtstart && *end > dtstart,
|
||||
// | N | N | Y | * | * | (start < DUE) AND (end >= DUE) |
|
||||
|
@ -247,15 +289,20 @@ fn is_in_time_range(
|
|||
// | N | N | N | Y | Y | ((start <= CREATED) OR (start <= COMPLETED))|
|
||||
// | | | | | | AND |
|
||||
// | | | | | | ((end >= CREATED) OR (end >= COMPLETED))|
|
||||
(None, None, None, Some(completed), Some(created)) => (*start <= created || *start <= completed) && (*end >= created || *end >= completed),
|
||||
(None, None, None, Some(completed), Some(created)) => {
|
||||
(*start <= created || *start <= completed)
|
||||
&& (*end >= created || *end >= completed)
|
||||
}
|
||||
// | N | N | N | Y | N | (start <= COMPLETED) AND (end >= COMPLETED) |
|
||||
(None, None, None, Some(completed), None) => *start <= completed && *end >= completed,
|
||||
(None, None, None, Some(completed), None) => {
|
||||
*start <= completed && *end >= completed
|
||||
}
|
||||
// | N | N | N | N | Y | (end > CREATED) |
|
||||
(None, None, None, None, Some(created)) => *end > created,
|
||||
// | N | N | N | N | N | TRUE |
|
||||
_ => true,
|
||||
}
|
||||
},
|
||||
}
|
||||
cal::Component::VJournal => {
|
||||
let maybe_dtstart = prop_date(properties, "DTSTART");
|
||||
match maybe_dtstart {
|
||||
|
@ -264,17 +311,20 @@ fn is_in_time_range(
|
|||
// | N | * | FALSE |
|
||||
None => false,
|
||||
}
|
||||
},
|
||||
}
|
||||
cal::Component::VFreeBusy => {
|
||||
//@FIXME freebusy is not supported yet
|
||||
false
|
||||
},
|
||||
}
|
||||
cal::Component::VAlarm => {
|
||||
//@FIXME does not support REPEAT
|
||||
let maybe_trigger = resolve_trigger(parent, properties);
|
||||
// (start <= trigger-time) AND (end > trigger-time)
|
||||
false
|
||||
},
|
||||
match maybe_trigger {
|
||||
// (start <= trigger-time) AND (end > trigger-time)
|
||||
Some(trigger_time) => *start <= trigger_time && *end > trigger_time,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -374,7 +374,11 @@ fn apply_filter<'a>(
|
|||
tracing::debug!(filter=?root_filter, "calendar-query filter");
|
||||
|
||||
// Adjust return value according to filter
|
||||
match is_component_match(&fake_vcal_component, &[fake_vcal_component.clone()], root_filter) {
|
||||
match is_component_match(
|
||||
&fake_vcal_component,
|
||||
&[fake_vcal_component.clone()],
|
||||
root_filter,
|
||||
) {
|
||||
true => Some(Ok(single_node)),
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
@ -590,6 +590,13 @@ fn rfc4791_webdav_caldav() {
|
|||
.send()?;
|
||||
let _obj5_etag = resp.headers().get("etag").expect("etag must be set");
|
||||
assert_eq!(resp.status(), 201);
|
||||
let resp = http
|
||||
.put("http://localhost:8087/alice/calendar/Personal/rfc6.ics")
|
||||
.header("If-None-Match", "*")
|
||||
.body(ICAL_RFC6)
|
||||
.send()?;
|
||||
let _obj6_etag = resp.headers().get("etag").expect("etag must be set");
|
||||
assert_eq!(resp.status(), 201);
|
||||
|
||||
// A generic function to check a <calendar-data/> query result
|
||||
let check_cal =
|
||||
|
@ -611,25 +618,17 @@ fn rfc4791_webdav_caldav() {
|
|||
.iter()
|
||||
.find(|p| p.status.0.as_u16() == 200)
|
||||
.expect("some propstats must be 200");
|
||||
let etag = obj_success
|
||||
.prop
|
||||
.0
|
||||
.iter()
|
||||
.find_map(|p| match p {
|
||||
dav::AnyProperty::Value(dav::Property::GetEtag(x)) => Some(x.as_str()),
|
||||
_ => None,
|
||||
});
|
||||
let etag = obj_success.prop.0.iter().find_map(|p| match p {
|
||||
dav::AnyProperty::Value(dav::Property::GetEtag(x)) => Some(x.as_str()),
|
||||
_ => None,
|
||||
});
|
||||
assert_eq!(etag, ref_etag);
|
||||
let calendar_data = obj_success
|
||||
.prop
|
||||
.0
|
||||
.iter()
|
||||
.find_map(|p| match p {
|
||||
dav::AnyProperty::Value(dav::Property::Extension(
|
||||
realization::Property::Cal(cal::Property::CalendarData(x)),
|
||||
)) => Some(x.payload.as_bytes()),
|
||||
_ => None,
|
||||
});
|
||||
let calendar_data = obj_success.prop.0.iter().find_map(|p| match p {
|
||||
dav::AnyProperty::Value(dav::Property::Extension(
|
||||
realization::Property::Cal(cal::Property::CalendarData(x)),
|
||||
)) => Some(x.payload.as_bytes()),
|
||||
_ => None,
|
||||
});
|
||||
assert_eq!(calendar_data, ref_ical);
|
||||
};
|
||||
|
||||
|
@ -753,7 +752,14 @@ fn rfc4791_webdav_caldav() {
|
|||
assert_eq!(resp.status(), 207);
|
||||
let multistatus = dav_deserialize::<dav::Multistatus<All>>(&resp.text()?);
|
||||
assert_eq!(multistatus.responses.len(), 1);
|
||||
check_cal(&multistatus, ("/alice/calendar/Personal/rfc2.ics", Some(obj2_etag.to_str().expect("etag header convertible to str")), None));
|
||||
check_cal(
|
||||
&multistatus,
|
||||
(
|
||||
"/alice/calendar/Personal/rfc2.ics",
|
||||
Some(obj2_etag.to_str().expect("etag header convertible to str")),
|
||||
None,
|
||||
),
|
||||
);
|
||||
|
||||
// 7.8.5. Example: Retrieval of To-Dos by Alarm Time Range
|
||||
let cal_query = r#"<?xml version="1.0" encoding="utf-8" ?>
|
||||
|
@ -766,7 +772,7 @@ fn rfc4791_webdav_caldav() {
|
|||
<C:comp-filter name="VCALENDAR">
|
||||
<C:comp-filter name="VTODO">
|
||||
<C:comp-filter name="VALARM">
|
||||
<C:time-range start="20060106T100000Z" end="20060107T100000Z"/>
|
||||
<C:time-range start="20060201T000000Z" end="20060301T000000Z"/>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>
|
||||
</C:comp-filter>
|
||||
|
@ -781,18 +787,17 @@ fn rfc4791_webdav_caldav() {
|
|||
.send()?;
|
||||
assert_eq!(resp.status(), 207);
|
||||
let multistatus = dav_deserialize::<dav::Multistatus<All>>(&resp.text()?);
|
||||
//assert_eq!(multistatus.responses.len(), 1);
|
||||
assert_eq!(multistatus.responses.len(), 1);
|
||||
|
||||
// 7.8.6. Example: Retrieval of Event by UID
|
||||
// @TODO
|
||||
|
||||
// 7.8.7. Example: Retrieval of Events by PARTSTAT
|
||||
// @TODO
|
||||
|
||||
|
||||
// 7.8.9. Example: Retrieval of All Pending To-Dos
|
||||
// @TODO
|
||||
|
||||
|
||||
// --- REPORT calendar-multiget ---
|
||||
let cal_query = r#"<?xml version="1.0" encoding="utf-8" ?>
|
||||
<C:calendar-multiget xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
|
||||
|
|
|
@ -158,3 +158,20 @@ UID:E10BA47467C5C69BB74E8725@example.com
|
|||
END:VTODO
|
||||
END:VCALENDAR
|
||||
"#;
|
||||
|
||||
pub static ICAL_RFC6: &[u8] = br#"BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Example Corp.//CalDAV Client//EN
|
||||
BEGIN:VTODO
|
||||
DTSTART:20060205T235335Z
|
||||
DUE;VALUE=DATE:20060104
|
||||
STATUS:NEEDS-ACTION
|
||||
SUMMARY:Task #1
|
||||
UID:DDDEEB7915FA61233B861457@example.com
|
||||
BEGIN:VALARM
|
||||
ACTION:AUDIO
|
||||
TRIGGER;RELATED=START:-PT10M
|
||||
END:VALARM
|
||||
END:VTODO
|
||||
END:VCALENDAR
|
||||
"#;
|
||||
|
|
Loading…
Reference in a new issue