Items include a last modified (DTSTAMP) date
This commit is contained in:
parent
e24fab2ccb
commit
8e35f4c579
7 changed files with 119 additions and 12 deletions
|
@ -2,7 +2,8 @@
|
|||
|
||||
use std::error::Error;
|
||||
|
||||
use ics::properties::{Comment, Status, Summary};
|
||||
use chrono::{DateTime, Utc};
|
||||
use ics::properties::{LastModified, Status, Summary};
|
||||
use ics::{ICalendar, ToDo};
|
||||
|
||||
use crate::item::Item;
|
||||
|
@ -14,9 +15,14 @@ fn ical_product_id() -> String {
|
|||
|
||||
/// Create an iCal item from a `crate::item::Item`
|
||||
pub fn build_from(item: &Item) -> Result<String, Box<dyn Error>> {
|
||||
let mut todo = ToDo::new(item.uid(), "20181021T190000");
|
||||
todo.push(Summary::new("Take pictures of squirrels (with ÜTF-8 chars)"));
|
||||
todo.push(Comment::new("That's really something I'd like to do one day"));
|
||||
let s_last_modified = format_date_time(item.last_modified());
|
||||
|
||||
let mut todo = ToDo::new(
|
||||
item.uid(),
|
||||
s_last_modified.clone(),
|
||||
);
|
||||
todo.push(LastModified::new(s_last_modified));
|
||||
todo.push(Summary::new(item.name()));
|
||||
|
||||
match item {
|
||||
Item::Task(t) => {
|
||||
|
@ -34,6 +40,9 @@ pub fn build_from(item: &Item) -> Result<String, Box<dyn Error>> {
|
|||
Ok(calendar.to_string())
|
||||
}
|
||||
|
||||
fn format_date_time(dt: &DateTime<Utc>) -> String {
|
||||
dt.format("%Y%m%dT%H%M%S").to_string()
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -44,20 +53,21 @@ mod tests {
|
|||
#[test]
|
||||
fn test_ical_from_task() {
|
||||
let cal_id = "http://my.calend.ar/id".parse().unwrap();
|
||||
let now = format_date_time(&Utc::now());
|
||||
|
||||
let task = Item::Task(Task::new(
|
||||
String::from("This is a task"), true, &cal_id
|
||||
String::from("This is a task with ÜTF-8 characters"), true, &cal_id
|
||||
));
|
||||
let expected_ical = format!("BEGIN:VCALENDAR\r\n\
|
||||
VERSION:2.0\r\n\
|
||||
PRODID:-//{}//{}//EN\r\n\
|
||||
BEGIN:VTODO\r\n\
|
||||
UID:{}\r\n\
|
||||
DTSTAMP:20181021T190000\r\n\
|
||||
SUMMARY:Take pictures of squirrels (with ÜTF-8 chars)\r\n\
|
||||
COMMENT:That's really something I'd like to do one day\r\n\
|
||||
DTSTAMP:{}\r\n\
|
||||
SUMMARY:This is a task with ÜTF-8 characters\r\n\
|
||||
STATUS:COMPLETED\r\n\
|
||||
END:VTODO\r\n\
|
||||
END:VCALENDAR\r\n", ORG_NAME, PRODUCT_NAME, task.uid());
|
||||
END:VCALENDAR\r\n", ORG_NAME, PRODUCT_NAME, task.uid(), now);
|
||||
|
||||
let ical = build_from(&task);
|
||||
assert_eq!(ical.unwrap(), expected_ical);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use std::error::Error;
|
||||
|
||||
use ical::parser::ical::component::{IcalCalendar, IcalEvent, IcalTodo};
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
|
||||
use crate::Item;
|
||||
use crate::item::SyncStatus;
|
||||
|
@ -31,6 +32,7 @@ pub fn parse(content: &str, item_id: ItemId, sync_status: SyncStatus) -> Result<
|
|||
let mut name = None;
|
||||
let mut uid = None;
|
||||
let mut completed = false;
|
||||
let mut last_modified = None;
|
||||
for prop in &todo.properties {
|
||||
if prop.name == "SUMMARY" {
|
||||
name = prop.value.clone();
|
||||
|
@ -48,6 +50,19 @@ pub fn parse(content: &str, item_id: ItemId, sync_status: SyncStatus) -> Result<
|
|||
if prop.name == "UID" {
|
||||
uid = prop.value.clone();
|
||||
}
|
||||
if prop.name == "DTSTAMP" {
|
||||
// this property specifies the date and time that the information associated with
|
||||
// the calendar component was last revised in the calendar store.
|
||||
last_modified = prop.value.as_ref()
|
||||
.and_then(|s| {
|
||||
parse_date_time(s)
|
||||
.map_err(|err| {
|
||||
log::warn!("Invalid DTSTAMP: {}", s);
|
||||
err
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
}
|
||||
}
|
||||
let name = match name {
|
||||
Some(name) => name,
|
||||
|
@ -57,8 +72,12 @@ pub fn parse(content: &str, item_id: ItemId, sync_status: SyncStatus) -> Result<
|
|||
Some(uid) => uid,
|
||||
None => return Err(format!("Missing UID for item {}", item_id).into()),
|
||||
};
|
||||
let last_modified = match last_modified {
|
||||
Some(dt) => dt,
|
||||
None => return Err(format!("Missing DTSTAMP for item {}, but this is required by RFC5545", item_id).into()),
|
||||
};
|
||||
|
||||
Item::Task(Task::new_with_parameters(name, completed, uid, item_id, sync_status))
|
||||
Item::Task(Task::new_with_parameters(name, completed, uid, item_id, sync_status, last_modified))
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -71,6 +90,12 @@ pub fn parse(content: &str, item_id: ItemId, sync_status: SyncStatus) -> Result<
|
|||
Ok(item)
|
||||
}
|
||||
|
||||
fn parse_date_time(dt: &str) -> Result<DateTime<Utc>, chrono::format::ParseError> {
|
||||
Utc.datetime_from_str(dt, "%Y%m%dT%H%M%S")
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum CurrentType<'a> {
|
||||
Event(&'a IcalEvent),
|
||||
Todo(&'a IcalTodo),
|
||||
|
@ -171,6 +196,7 @@ END:VCALENDAR
|
|||
assert_eq!(task.uid(), "0633de27-8c32-42be-bcb8-63bc879c6185@some-domain.com");
|
||||
assert_eq!(task.completed(), false);
|
||||
assert_eq!(task.sync_status(), &sync_status);
|
||||
assert_eq!(task.last_modified(), &Utc.ymd(2021, 03, 21).and_hms(0, 16, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue