From e39cdc7cd557ec211d88ab5031003adb708b08f4 Mon Sep 17 00:00:00 2001 From: daladim Date: Tue, 13 Apr 2021 23:30:43 +0200 Subject: [PATCH] Provider-sync example --- .gitignore | 1 + examples/provider-sync.rs | 36 ++++- src/utils.rs | 291 ++++++++++++++++++++------------------ 3 files changed, 186 insertions(+), 142 deletions(-) diff --git a/.gitignore b/.gitignore index 983704b..da00c36 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /test_cache +/example_cache diff --git a/examples/provider-sync.rs b/examples/provider-sync.rs index 14d5365..011778f 100644 --- a/examples/provider-sync.rs +++ b/examples/provider-sync.rs @@ -1,11 +1,17 @@ use std::path::Path; use my_tasks::{client::Client, traits::CalDavSource}; +use my_tasks::calendar::{CalendarId, cached_calendar::CachedCalendar, remote_calendar::RemoteCalendar}; +use my_tasks::Item; +use my_tasks::Task; use my_tasks::cache::Cache; use my_tasks::Provider; +use my_tasks::traits::BaseCalendar; use my_tasks::settings::URL; use my_tasks::settings::USERNAME; use my_tasks::settings::PASSWORD; +use my_tasks::settings::EXAMPLE_CALENDAR_URL; +use my_tasks::utils::pause; const CACHE_FOLDER: &str = "example_cache"; @@ -14,7 +20,10 @@ const CACHE_FOLDER: &str = "example_cache"; async fn main() { env_logger::init(); - println!("This examples show how to sync a remote server with a local cache, using a Provider"); + println!("This examples show how to sync a remote server with a local cache, using a Provider."); + println!("Make sure you have edited your settings.rs to include correct URLs and credentials."); + println!("You can also set the RUST_LOG environment variable to display more info about the sync."); + pause(); let cache_path = Path::new(CACHE_FOLDER); @@ -32,11 +41,36 @@ async fn main() { println!("---- before sync -----"); my_tasks::utils::print_calendar_list(&cals).await; + println!("Starting a sync..."); if provider.sync().await == false { log::warn!("Sync did not complete, see the previous log lines for more info. You can safely start a new sync."); } + provider.local().save_to_folder().unwrap(); println!("---- after sync -----"); let cals = provider.local().get_calendars().await.unwrap(); my_tasks::utils::print_calendar_list(&cals).await; + + add_items_and_sync_again(&mut provider).await; +} + +async fn add_items_and_sync_again(provider: &mut Provider) { + println!("Now, we'll add a task and run the sync again."); + pause(); + + let changed_calendar_id: CalendarId = EXAMPLE_CALENDAR_URL.parse().unwrap(); + let changed_calendar = provider.local().get_calendar(&changed_calendar_id).await.unwrap(); + + let new_name = "New example task"; + let new_task = Task::new(String::from(new_name), false, &changed_calendar_id); + changed_calendar.lock().unwrap().add_item(Item::Task(new_task)).await.unwrap(); + + if provider.sync().await == false { + log::warn!("Sync did not complete, see the previous log lines for more info. You can safely start a new sync. The new task may not have been synced."); + } else { + println!("Done syncing the new task '{}'", new_name); + } + provider.local().save_to_folder().unwrap(); + + println!("Done. You can start this example again to see the cache being restored from its current saved state") } diff --git a/src/utils.rs b/src/utils.rs index 825a319..9aba3a5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,141 +1,150 @@ -///! Some utility functions - -use std::collections::{HashMap, HashSet}; -use std::sync::{Arc, Mutex}; -use std::hash::Hash; - -use minidom::Element; - -use crate::traits::CompleteCalendar; -use crate::traits::DavCalendar; -use crate::calendar::CalendarId; -use crate::Item; -use crate::item::SyncStatus; - -/// Walks an XML tree and returns every element that has the given name -pub fn find_elems>(root: &Element, searched_name: S) -> Vec<&Element> { - let searched_name = searched_name.as_ref(); - let mut elems: Vec<&Element> = Vec::new(); - - for el in root.children() { - if el.name() == searched_name { - elems.push(el); - } else { - let ret = find_elems(el, searched_name); - elems.extend(ret); - } - } - elems -} - -/// Walks an XML tree until it finds an elements with the given name -pub fn find_elem>(root: &Element, searched_name: S) -> Option<&Element> { - let searched_name = searched_name.as_ref(); - if root.name() == searched_name { - return Some(root); - } - - for el in root.children() { - if el.name() == searched_name { - return Some(el); - } else { - let ret = find_elem(el, searched_name); - if ret.is_some() { - return ret; - } - } - } - None -} - - -pub fn print_xml(element: &Element) { - use std::io::Write; - let mut writer = std::io::stdout(); - - let mut xml_writer = minidom::quick_xml::Writer::new_with_indent( - std::io::stdout(), - 0x20, 4 - ); - let _ = element.to_writer(&mut xml_writer); - let _ = writer.write(&[0x0a]); -} - -/// A debug utility that pretty-prints calendars -pub async fn print_calendar_list(cals: &HashMap>>) -where - C: CompleteCalendar, -{ - for (id, cal) in cals { - println!("CAL {} ({})", cal.lock().unwrap().name(), id); - match cal.lock().unwrap().get_items().await { - Err(_err) => continue, - Ok(map) => { - for (_, item) in map { - print_task(item); - } - }, - } - } -} - -/// A debug utility that pretty-prints calendars -pub async fn print_dav_calendar_list(cals: &HashMap>>) -where - C: DavCalendar, -{ - for (id, cal) in cals { - println!("CAL {} ({})", cal.lock().unwrap().name(), id); - match cal.lock().unwrap().get_item_version_tags().await { - Err(_err) => continue, - Ok(map) => { - for (id, version_tag) in map { - println!(" * {} (version {:?})", id, version_tag); - } - }, - } - } -} - -pub fn print_task(item: &Item) { - match item { - Item::Task(task) => { - let completion = if task.completed() { "✓" } else { " " }; - let sync = match task.sync_status() { - SyncStatus::NotSynced => ".", - SyncStatus::Synced(_) => "=", - SyncStatus::LocallyModified(_) => "~", - SyncStatus::LocallyDeleted(_) => "x", - }; - println!(" {}{} {}\t{}", completion, sync, task.name(), task.id()); - }, - _ => return, - } -} - - -/// Compare keys of two hashmaps for equality -pub fn keys_are_the_same(left: &HashMap, right: &HashMap) -> bool -where - T: Hash + Eq + Clone + std::fmt::Display, -{ - if left.len() != right.len() { - log::debug!("Count of keys mismatch: {} and {}", left.len(), right.len()); - return false; - } - - let keys_l: HashSet = left.keys().cloned().collect(); - let keys_r: HashSet = right.keys().cloned().collect(); - let result = keys_l == keys_r; - if result == false { - log::debug!("Keys of a map mismatch"); - for key in keys_l { - log::debug!(" left: {}", key); - } - log::debug!("RIGHT:"); - for key in keys_r { - log::debug!(" right: {}", key); - } - } - result -} +///! Some utility functions + +use std::collections::{HashMap, HashSet}; +use std::sync::{Arc, Mutex}; +use std::hash::Hash; +use std::io::{stdin, stdout, Read, Write}; + +use minidom::Element; + +use crate::traits::CompleteCalendar; +use crate::traits::DavCalendar; +use crate::calendar::CalendarId; +use crate::Item; +use crate::item::SyncStatus; + +/// Walks an XML tree and returns every element that has the given name +pub fn find_elems>(root: &Element, searched_name: S) -> Vec<&Element> { + let searched_name = searched_name.as_ref(); + let mut elems: Vec<&Element> = Vec::new(); + + for el in root.children() { + if el.name() == searched_name { + elems.push(el); + } else { + let ret = find_elems(el, searched_name); + elems.extend(ret); + } + } + elems +} + +/// Walks an XML tree until it finds an elements with the given name +pub fn find_elem>(root: &Element, searched_name: S) -> Option<&Element> { + let searched_name = searched_name.as_ref(); + if root.name() == searched_name { + return Some(root); + } + + for el in root.children() { + if el.name() == searched_name { + return Some(el); + } else { + let ret = find_elem(el, searched_name); + if ret.is_some() { + return ret; + } + } + } + None +} + + +pub fn print_xml(element: &Element) { + let mut writer = std::io::stdout(); + + let mut xml_writer = minidom::quick_xml::Writer::new_with_indent( + std::io::stdout(), + 0x20, 4 + ); + let _ = element.to_writer(&mut xml_writer); + let _ = writer.write(&[0x0a]); +} + +/// A debug utility that pretty-prints calendars +pub async fn print_calendar_list(cals: &HashMap>>) +where + C: CompleteCalendar, +{ + for (id, cal) in cals { + println!("CAL {} ({})", cal.lock().unwrap().name(), id); + match cal.lock().unwrap().get_items().await { + Err(_err) => continue, + Ok(map) => { + for (_, item) in map { + print_task(item); + } + }, + } + } +} + +/// A debug utility that pretty-prints calendars +pub async fn print_dav_calendar_list(cals: &HashMap>>) +where + C: DavCalendar, +{ + for (id, cal) in cals { + println!("CAL {} ({})", cal.lock().unwrap().name(), id); + match cal.lock().unwrap().get_item_version_tags().await { + Err(_err) => continue, + Ok(map) => { + for (id, version_tag) in map { + println!(" * {} (version {:?})", id, version_tag); + } + }, + } + } +} + +pub fn print_task(item: &Item) { + match item { + Item::Task(task) => { + let completion = if task.completed() { "✓" } else { " " }; + let sync = match task.sync_status() { + SyncStatus::NotSynced => ".", + SyncStatus::Synced(_) => "=", + SyncStatus::LocallyModified(_) => "~", + SyncStatus::LocallyDeleted(_) => "x", + }; + println!(" {}{} {}\t{}", completion, sync, task.name(), task.id()); + }, + _ => return, + } +} + + +/// Compare keys of two hashmaps for equality +pub fn keys_are_the_same(left: &HashMap, right: &HashMap) -> bool +where + T: Hash + Eq + Clone + std::fmt::Display, +{ + if left.len() != right.len() { + log::debug!("Count of keys mismatch: {} and {}", left.len(), right.len()); + return false; + } + + let keys_l: HashSet = left.keys().cloned().collect(); + let keys_r: HashSet = right.keys().cloned().collect(); + let result = keys_l == keys_r; + if result == false { + log::debug!("Keys of a map mismatch"); + for key in keys_l { + log::debug!(" left: {}", key); + } + log::debug!("RIGHT:"); + for key in keys_r { + log::debug!(" right: {}", key); + } + } + result +} + + +/// Wait for the user to press enter +pub fn pause() { + let mut stdout = stdout(); + stdout.write_all(b"Press Enter to continue...").unwrap(); + stdout.flush().unwrap(); + stdin().read_exact(&mut [0]).unwrap(); +}