diff --git a/src/event.rs b/src/event.rs index 44496cc..262292a 100644 --- a/src/event.rs +++ b/src/event.rs @@ -23,6 +23,10 @@ impl Event { &self.id } + pub fn uid(&self) -> &str { + unimplemented!() + } + pub fn name(&self) -> &str { &self.name } diff --git a/src/ical/parser.rs b/src/ical/parser.rs index 5afd315..0bb907e 100644 --- a/src/ical/parser.rs +++ b/src/ical/parser.rs @@ -29,6 +29,7 @@ pub fn parse(content: &str, item_id: ItemId, sync_status: SyncStatus) -> Result< CurrentType::Todo(todo) => { let mut name = None; + let mut uid = None; let mut completed = false; for prop in &todo.properties { if prop.name == "SUMMARY" { @@ -44,13 +45,20 @@ pub fn parse(content: &str, item_id: ItemId, sync_status: SyncStatus) -> Result< completed = true; } } + if prop.name == "UID" { + uid = prop.value.clone(); + } } let name = match name { Some(name) => name, None => return Err(format!("Missing name for item {}", item_id).into()), }; + let uid = match uid { + Some(uid) => uid, + None => return Err(format!("Missing UID for item {}", item_id).into()), + }; - Item::Task(Task::new_with_parameters(name, completed, item_id, sync_status)) + Item::Task(Task::new_with_parameters(name, completed, uid, item_id, sync_status)) }, }; @@ -99,7 +107,7 @@ mod test { VERSION:2.0 PRODID:-//Nextcloud Tasks v0.13.6 BEGIN:VTODO -UID:0633de27-8c32-42be-bcb8-63bc879c6185 +UID:0633de27-8c32-42be-bcb8-63bc879c6185@some-domain.com CREATED:20210321T001600 LAST-MODIFIED:20210321T001600 DTSTAMP:20210321T001600 @@ -112,7 +120,7 @@ END:VCALENDAR VERSION:2.0 PRODID:-//Nextcloud Tasks v0.13.6 BEGIN:VTODO -UID:0633de27-8c32-42be-bcb8-63bc879c6185 +UID:19960401T080045Z-4000F192713-0052@example.com CREATED:20210321T001600 LAST-MODIFIED:20210402T081557 DTSTAMP:20210402T081557 @@ -160,6 +168,7 @@ END:VCALENDAR assert_eq!(task.name(), "Do not forget to do this"); assert_eq!(task.id(), &item_id); + assert_eq!(task.uid(), "0633de27-8c32-42be-bcb8-63bc879c6185@some-domain.com"); assert_eq!(task.completed(), false); assert_eq!(task.sync_status(), &sync_status); } diff --git a/src/item.rs b/src/item.rs index 27772ba..1f0c9f2 100644 --- a/src/item.rs +++ b/src/item.rs @@ -25,6 +25,13 @@ impl Item { } } + pub fn uid(&self) -> &str { + match self { + Item::Event(e) => e.uid(), + Item::Task(t) => t.uid(), + } + } + pub fn name(&self) -> &str { match self { Item::Event(e) => e.name(), diff --git a/src/task.rs b/src/task.rs index 9e0dfd3..1588aa5 100644 --- a/src/task.rs +++ b/src/task.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use uuid::Uuid; use crate::item::ItemId; use crate::item::SyncStatus; @@ -7,9 +8,13 @@ use crate::calendar::CalendarId; /// A to-do task #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Task { - /// The task unique ID, that will never change + /// The task URL id: ItemId, + /// Persistent, globally unique identifier for the calendar component + /// The [RFC](https://tools.ietf.org/html/rfc5545#page-117) recommends concatenating a timestamp with the server's domain name, but UUID are even better + uid: String, + /// The sync status of this item sync_status: SyncStatus, @@ -25,13 +30,15 @@ impl Task { pub fn new(name: String, completed: bool, parent_calendar_id: &CalendarId) -> Self { let new_item_id = ItemId::random(parent_calendar_id); let new_sync_status = SyncStatus::NotSynced; - Self::new_with_parameters(name, completed, new_item_id, new_sync_status) + let new_uid = Uuid::new_v4().to_hyphenated().to_string(); + Self::new_with_parameters(name, completed, new_uid, new_item_id, new_sync_status) } /// Create a new Task instance, that may be synced already - pub fn new_with_parameters(name: String, completed: bool, id: ItemId, sync_status: SyncStatus) -> Self { + pub fn new_with_parameters(name: String, completed: bool, uid: String, id: ItemId, sync_status: SyncStatus) -> Self { Self { id, + uid, name, sync_status, completed, @@ -39,6 +46,7 @@ impl Task { } pub fn id(&self) -> &ItemId { &self.id } + pub fn uid(&self) -> &str { &self.uid } pub fn name(&self) -> &str { &self.name } pub fn completed(&self) -> bool { self.completed } pub fn sync_status(&self) -> &SyncStatus { &self.sync_status }