From 571420126f0e6b2ed797470167b6eddfb35686c7 Mon Sep 17 00:00:00 2001 From: daladim Date: Sun, 28 Feb 2021 11:54:31 +0100 Subject: [PATCH] Created Item (enum of either a task or en event) --- src/calendar.rs | 106 +++++++++++++++++++++++++++++++++--------------- src/event.rs | 29 +++++++++++++ src/item.rs | 86 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 ++ src/provider.rs | 24 +++++------ src/task.rs | 27 ++---------- tests/sync.rs | 93 ++++++++++++++++++++++-------------------- 7 files changed, 257 insertions(+), 112 deletions(-) create mode 100644 src/event.rs create mode 100644 src/item.rs diff --git a/src/calendar.rs b/src/calendar.rs index c5645c9..de74c9b 100644 --- a/src/calendar.rs +++ b/src/calendar.rs @@ -7,8 +7,8 @@ use url::Url; use serde::{Deserialize, Serialize}; use chrono::{DateTime, Utc}; -use crate::task::Task; -use crate::task::TaskId; +use crate::Item; +use crate::item::ItemId; use bitflags::bitflags; @@ -49,6 +49,24 @@ impl TryFrom for SupportedComponents { } +/// Flags to tell which events should be retrieved +pub enum SearchFilter { + /// Return all items + All, + /// Return only tasks + Tasks, + // /// Return only completed tasks + // CompletedTasks, + // /// Return only calendar events + // Events, +} + +impl Default for SearchFilter { + fn default() -> Self { + SearchFilter::All + } +} + /// A Caldav Calendar #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Calendar { @@ -56,8 +74,8 @@ pub struct Calendar { url: Url, supported_components: SupportedComponents, - tasks: Vec, - deleted_tasks: BTreeMap, TaskId>, + items: Vec, + deleted_items: BTreeMap, ItemId>, } impl Calendar { @@ -65,8 +83,8 @@ impl Calendar { pub fn new(name: String, url: Url, supported_components: SupportedComponents) -> Self { Self { name, url, supported_components, - tasks: Vec::new(), - deleted_tasks: BTreeMap::new(), + items: Vec::new(), + deleted_items: BTreeMap::new(), } } @@ -85,55 +103,79 @@ impl Calendar { self.supported_components.contains(SupportedComponents::TODO) } - /// Add a task into this calendar - pub fn add_task(&mut self, task: Task) { - self.tasks.push(task); + /// Returns whether this calDAV calendar supports calendar items + pub fn supports_events(&self) -> bool { + self.supported_components.contains(SupportedComponents::EVENT) } - pub fn delete_task(&mut self, task_id: &TaskId) { - self.tasks.retain(|t| t.id() != task_id); - self.deleted_tasks.insert(Utc::now(), task_id.clone()); + /// Add an item into this calendar + pub fn add_item(&mut self, item: Item) { + self.items.push(item); } - /// Returns the list of tasks that this calendar contains - /// Pass a `completed` flag to filter only the completed (or non-completed) tasks - pub fn get_tasks(&self, completed: Option) -> HashMap { - self.get_tasks_modified_since(None, completed) + /// Remove an item from this calendar + pub fn delete_item(&mut self, item_id: &ItemId) { + self.items.retain(|i| i.id() != item_id); + self.deleted_items.insert(Utc::now(), item_id.clone()); } - /// Returns the tasks that have been last-modified after `since` - /// Pass a `completed` flag to filter only the completed (or non-completed) tasks - pub fn get_tasks_modified_since(&self, since: Option>, _completed: Option) -> HashMap { + /// Returns the list of items that this calendar contains + pub fn get_items(&self) -> HashMap { + self.get_items_modified_since(None, None) + } + /// Returns the items that have been last-modified after `since` + pub fn get_items_modified_since(&self, since: Option>, filter: Option) -> HashMap { + let filter = filter.unwrap_or_default(); + let mut map = HashMap::new(); - for task in &self.tasks { + for item in &self.items { match since { None => (), - Some(since) => if task.last_modified() < since { + Some(since) => if item.last_modified() < since { continue; }, } - map.insert(task.id().clone(), task); + match filter { + SearchFilter::Tasks => { + if item.is_task() == false { + continue; + } + }, + _ => (), + } + + map.insert(item.id().clone(), item); } map } - /// Returns the tasks that have been deleted after `since` - pub fn get_tasks_deleted_since(&self, since: DateTime) -> Vec { - self.deleted_tasks.range(since..) - .map(|(_key, value)| value.clone()) - .collect() + /// Returns the items that have been deleted after `since` + pub fn get_items_deleted_since(&self, since: DateTime) -> Vec { + self.deleted_items.range(since..) + .map(|(_key, value)| value.clone()) + .collect() } - /// Returns a particular task - pub fn get_task_by_id_mut(&mut self, id: &TaskId) -> Option<&mut Task> { - for task in &mut self.tasks { - if task.id() == id { - return Some(task); + /// Returns a particular item + pub fn get_item_by_id_mut(&mut self, id: &ItemId) -> Option<&mut Item> { + for item in &mut self.items { + if item.id() == id { + return Some(item); } } return None; } + + + /// Returns the list of tasks that this calendar contains + pub fn get_tasks(&self) -> HashMap { + self.get_tasks_modified_since(None) + } + /// Returns the tasks that have been last-modified after `since` + pub fn get_tasks_modified_since(&self, since: Option>) -> HashMap { + self.get_items_modified_since(since, Some(SearchFilter::Tasks)) + } } diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..ff37d63 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,29 @@ +//! Calendar events + +use serde::{Deserialize, Serialize}; +use chrono::{Utc, DateTime}; + +use crate::item::ItemId; + +/// TODO: implement Event one day. +/// This crate currently only supports tasks, not calendar events. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Event { + id: ItemId, + name: String, + last_modified: DateTime, +} + +impl Event { + pub fn id(&self) -> &ItemId { + &self.id + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn last_modified(&self) -> DateTime { + self.last_modified + } +} diff --git a/src/item.rs b/src/item.rs new file mode 100644 index 0000000..b99281a --- /dev/null +++ b/src/item.rs @@ -0,0 +1,86 @@ +use std::fmt::{Display, Formatter}; + +use serde::{Deserialize, Serialize}; +use chrono::{Utc, DateTime}; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum Item { + Event(crate::event::Event), + Task(crate::task::Task), +} + +impl Item { + pub fn id(&self) -> &ItemId { + match self { + Item::Event(e) => e.id(), + Item::Task(t) => t.id(), + } + } + + pub fn name(&self) -> &str { + match self { + Item::Event(e) => e.name(), + Item::Task(t) => t.name(), + } + } + + pub fn last_modified(&self) -> DateTime { + match self { + Item::Event(e) => e.last_modified(), + Item::Task(t) => t.last_modified(), + } + } + + pub fn is_event(&self) -> bool { + match &self { + Item::Event(_) => true, + _ => false, + } + } + + pub fn is_task(&self) -> bool { + match &self { + Item::Task(_) => true, + _ => false, + } + } + + /// Returns a mutable reference to the inner Task + /// + /// # Panics + /// Panics if the inner item is not a Task + pub fn unwrap_task_mut(&mut self) -> &mut crate::task::Task { + match self { + Item::Task(t) => t, + _ => panic!("Not a task"), + } + } + + /// Returns a reference to the inner Task + /// + /// # Panics + /// Panics if the inner item is not a Task + pub fn unwrap_task(&self) -> &crate::task::Task { + match self { + Item::Task(t) => t, + _ => panic!("Not a task"), + } + } +} + +#[derive(Clone, Debug, PartialEq, Hash, Serialize, Deserialize)] +pub struct ItemId { + content: String, +} +impl ItemId{ + pub fn new() -> Self { + let u = uuid::Uuid::new_v4().to_hyphenated().to_string(); + Self { content:u } + } +} +impl Eq for ItemId {} +impl Display for ItemId { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "{}", self.content) + } +} diff --git a/src/lib.rs b/src/lib.rs index 27c6cac..8937952 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,12 @@ pub mod traits; pub mod calendar; pub use calendar::Calendar; +mod item; +pub use item::Item; mod task; pub use task::Task; +mod event; +pub use event::Event; pub mod provider; pub use provider::Provider; diff --git a/src/provider.rs b/src/provider.rs index 07884ca..17b80ac 100644 --- a/src/provider.rs +++ b/src/provider.rs @@ -6,8 +6,8 @@ use chrono::{DateTime, Utc}; use crate::traits::CalDavSource; use crate::Calendar; -use crate::Task; -use crate::task::TaskId; +use crate::Item; +use crate::item::ItemId; pub struct Provider @@ -49,10 +49,10 @@ where Some(cal) => cal, }; - let server_mod = cal_server.get_tasks_modified_since(Some(self.last_sync), None); - let server_del = cal_server.get_tasks_deleted_since(self.last_sync); - let local_mod = cal_local.get_tasks_modified_since(Some(self.last_sync), None); - let local_del = cal_local.get_tasks_deleted_since(self.last_sync); + let server_mod = cal_server.get_tasks_modified_since(Some(self.last_sync)); + let server_del = cal_server.get_items_deleted_since(self.last_sync); + let local_mod = cal_local.get_tasks_modified_since(Some(self.last_sync)); + let local_del = cal_local.get_items_deleted_since(self.last_sync); let mut tasks_to_add_to_local = Vec::new(); let mut tasks_id_to_remove_from_local = Vec::new(); @@ -91,16 +91,16 @@ where } -fn move_to_calendar(tasks: &mut Vec, calendar: &mut Calendar) { - while tasks.len() > 0 { - let task = tasks.remove(0); - calendar.add_task(task); +fn move_to_calendar(items: &mut Vec, calendar: &mut Calendar) { + while items.len() > 0 { + let item = items.remove(0); + calendar.add_item(item); } } -fn remove_from_calendar(ids: &Vec, calendar: &mut Calendar) { +fn remove_from_calendar(ids: &Vec, calendar: &mut Calendar) { for id in ids { log::info!(" Removing {:?} from local calendar", id); - calendar.delete_task(id); + calendar.delete_item(id); } } diff --git a/src/task.rs b/src/task.rs index 21ff5ca..3e66517 100644 --- a/src/task.rs +++ b/src/task.rs @@ -1,32 +1,13 @@ -use std::fmt::{Display, Formatter}; - use chrono::{Utc, DateTime}; use serde::{Deserialize, Serialize}; -// TODO: turn into this one day -// pub type TaskId = String; // This is an HTML "etag" -#[derive(Clone, Debug, PartialEq, Hash, Serialize, Deserialize)] -pub struct TaskId { - content: String, -} -impl TaskId{ - pub fn new() -> Self { - let u = uuid::Uuid::new_v4().to_hyphenated().to_string(); - Self { content:u } - } -} -impl Eq for TaskId {} -impl Display for TaskId { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { - write!(f, "{}", self.content) - } -} +use crate::item::ItemId; /// A to-do task #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Task { /// The task unique ID, that will never change - id: TaskId, + id: ItemId, /// The last modification date of this task last_modified: DateTime, @@ -41,14 +22,14 @@ impl Task { /// Create a new Task pub fn new(name: String, last_modified: DateTime) -> Self { Self { - id: TaskId::new(), + id: ItemId::new(), name, last_modified, completed: false, } } - pub fn id(&self) -> &TaskId { &self.id } + pub fn id(&self) -> &ItemId { &self.id } pub fn name(&self) -> &str { &self.name } pub fn completed(&self) -> bool { self.completed } pub fn last_modified(&self) -> DateTime { self.last_modified } diff --git a/tests/sync.rs b/tests/sync.rs index 55b48fb..314a27a 100644 --- a/tests/sync.rs +++ b/tests/sync.rs @@ -5,6 +5,7 @@ use url::Url; use my_tasks::traits::CalDavSource; use my_tasks::cache::Cache; +use my_tasks::Item; use my_tasks::Task; use my_tasks::Calendar; use my_tasks::Provider; @@ -21,6 +22,7 @@ async fn test_sync() { let cals_server = provider.server().get_calendars().await.unwrap(); let cals_local = provider.local().get_calendars().await.unwrap(); print_calendar_list(cals_local); + print_calendar_list(cals_server); panic!(); //assert_eq!(cal_server, cal_local, "{:#?}\n{:#?}", cal_server, cal_local); @@ -32,9 +34,10 @@ async fn test_sync() { fn print_calendar_list(cals: &Vec) { for cal in cals { println!("CAL {}", cal.url()); - for (_, item) in cal.get_tasks(None) { - let completion = if item.completed() {"✓"} else {" "}; - println!(" {} {}", completion, item.name()); + for (_, item) in cal.get_items() { + let task = item.unwrap_task(); + let completion = if task.completed() {"✓"} else {" "}; + println!(" {} {}", completion, task.name()); } } } @@ -56,19 +59,19 @@ async fn populate_test_provider() -> Provider { let mut server = Cache::new(&PathBuf::from(String::from("server.json"))); let mut local = Cache::new(&PathBuf::from(String::from("local.json"))); - let task_a = Task::new("task A".into(), Utc.ymd(2000, 1, 1).and_hms(0, 0, 0)); - let task_b = Task::new("task B".into(), Utc.ymd(2000, 1, 2).and_hms(0, 0, 0)); - let task_c = Task::new("task C".into(), Utc.ymd(2000, 1, 3).and_hms(0, 0, 0)); - let task_d = Task::new("task D".into(), Utc.ymd(2000, 1, 4).and_hms(0, 0, 0)); - let task_e = Task::new("task E".into(), Utc.ymd(2000, 1, 5).and_hms(0, 0, 0)); - let task_f = Task::new("task F".into(), Utc.ymd(2000, 1, 6).and_hms(0, 0, 0)); - let task_g = Task::new("task G".into(), Utc.ymd(2000, 1, 7).and_hms(0, 0, 0)); - let task_h = Task::new("task H".into(), Utc.ymd(2000, 1, 8).and_hms(0, 0, 0)); - let task_i = Task::new("task I".into(), Utc.ymd(2000, 1, 9).and_hms(0, 0, 0)); - let task_j = Task::new("task J".into(), Utc.ymd(2000, 1, 10).and_hms(0, 0, 0)); - let task_k = Task::new("task K".into(), Utc.ymd(2000, 1, 11).and_hms(0, 0, 0)); - let task_l = Task::new("task L".into(), Utc.ymd(2000, 1, 12).and_hms(0, 0, 0)); - let task_m = Task::new("task M".into(), Utc.ymd(2000, 1, 12).and_hms(0, 0, 0)); + let task_a = Item::Task(Task::new("task A".into(), Utc.ymd(2000, 1, 1).and_hms(0, 0, 0))); + let task_b = Item::Task(Task::new("task B".into(), Utc.ymd(2000, 1, 2).and_hms(0, 0, 0))); + let task_c = Item::Task(Task::new("task C".into(), Utc.ymd(2000, 1, 3).and_hms(0, 0, 0))); + let task_d = Item::Task(Task::new("task D".into(), Utc.ymd(2000, 1, 4).and_hms(0, 0, 0))); + let task_e = Item::Task(Task::new("task E".into(), Utc.ymd(2000, 1, 5).and_hms(0, 0, 0))); + let task_f = Item::Task(Task::new("task F".into(), Utc.ymd(2000, 1, 6).and_hms(0, 0, 0))); + let task_g = Item::Task(Task::new("task G".into(), Utc.ymd(2000, 1, 7).and_hms(0, 0, 0))); + let task_h = Item::Task(Task::new("task H".into(), Utc.ymd(2000, 1, 8).and_hms(0, 0, 0))); + let task_i = Item::Task(Task::new("task I".into(), Utc.ymd(2000, 1, 9).and_hms(0, 0, 0))); + let task_j = Item::Task(Task::new("task J".into(), Utc.ymd(2000, 1, 10).and_hms(0, 0, 0))); + let task_k = Item::Task(Task::new("task K".into(), Utc.ymd(2000, 1, 11).and_hms(0, 0, 0))); + let task_l = Item::Task(Task::new("task L".into(), Utc.ymd(2000, 1, 12).and_hms(0, 0, 0))); + let task_m = Item::Task(Task::new("task M".into(), Utc.ymd(2000, 1, 12).and_hms(0, 0, 0))); let last_sync = task_m.last_modified(); assert!(last_sync < Utc::now()); @@ -89,19 +92,19 @@ async fn populate_test_provider() -> Provider { // Step 1 // Build the calendar as it was at the time of the sync let mut calendar = Calendar::new("a list".into(), Url::parse("http://todo.list/cal").unwrap(), my_tasks::calendar::SupportedComponents::TODO); - calendar.add_task(task_a); - calendar.add_task(task_b); - calendar.add_task(task_c); - calendar.add_task(task_d); - calendar.add_task(task_e); - calendar.add_task(task_f); - calendar.add_task(task_g); - calendar.add_task(task_h); - calendar.add_task(task_i); - calendar.add_task(task_j); - calendar.add_task(task_k); - calendar.add_task(task_l); - calendar.add_task(task_m); + calendar.add_item(task_a); + calendar.add_item(task_b); + calendar.add_item(task_c); + calendar.add_item(task_d); + calendar.add_item(task_e); + calendar.add_item(task_f); + calendar.add_item(task_g); + calendar.add_item(task_h); + calendar.add_item(task_i); + calendar.add_item(task_j); + calendar.add_item(task_k); + calendar.add_item(task_l); + calendar.add_item(task_m); server.add_calendar(calendar.clone()); local.add_calendar(calendar.clone()); @@ -110,49 +113,49 @@ async fn populate_test_provider() -> Provider { // Edit the server calendar let cal_server = &mut server.get_calendars_mut().await.unwrap()[0]; - cal_server.delete_task(&task_b_id); + cal_server.delete_item(&task_b_id); - cal_server.get_task_by_id_mut(&task_e_id).unwrap() + cal_server.get_item_by_id_mut(&task_e_id).unwrap().unwrap_task_mut() .set_name("E has been remotely renamed".into()); - cal_server.get_task_by_id_mut(&task_f_id).unwrap() + cal_server.get_item_by_id_mut(&task_f_id).unwrap().unwrap_task_mut() .set_name("F renamed in the server".into()); - cal_server.get_task_by_id_mut(&task_g_id).unwrap() + cal_server.get_item_by_id_mut(&task_g_id).unwrap().unwrap_task_mut() .set_completed(true); - cal_server.get_task_by_id_mut(&task_i_id).unwrap() + cal_server.get_item_by_id_mut(&task_i_id).unwrap().unwrap_task_mut() .set_name("I renamed in the server".into()); - cal_server.delete_task(&task_j_id); + cal_server.delete_item(&task_j_id); - let task_n = Task::new("task N (new from server)".into(), Utc::now()); - cal_server.add_task(task_n); + let task_n = Item::Task(Task::new("task N (new from server)".into(), Utc::now())); + cal_server.add_item(task_n); // Step 3 // Edit the local calendar let cal_local = &mut local.get_calendars_mut().await.unwrap()[0]; - cal_local.delete_task(&task_c_id); + cal_local.delete_item(&task_c_id); - cal_local.get_task_by_id_mut(&task_d_id).unwrap() + cal_local.get_item_by_id_mut(&task_d_id).unwrap().unwrap_task_mut() .set_name("D has been locally renamed".into()); - cal_local.get_task_by_id_mut(&task_f_id).unwrap() + cal_local.get_item_by_id_mut(&task_f_id).unwrap().unwrap_task_mut() .set_name("F renamed locally as well!".into()); - cal_local.get_task_by_id_mut(&task_h_id).unwrap() + cal_local.get_item_by_id_mut(&task_h_id).unwrap().unwrap_task_mut() .set_completed(true); - cal_local.get_task_by_id_mut(&task_i_id).unwrap() + cal_local.get_item_by_id_mut(&task_i_id).unwrap().unwrap_task_mut() .set_completed(true); - cal_local.get_task_by_id_mut(&task_j_id).unwrap() + cal_local.get_item_by_id_mut(&task_j_id).unwrap().unwrap_task_mut() .set_completed(true); - let task_o = Task::new("task O (new from local)".into(), Utc::now()); - cal_local.add_task(task_o); + let task_o = Item::Task(Task::new("task O (new from local)".into(), Utc::now())); + cal_local.add_item(task_o); Provider::new(server, local, last_sync) }