Syncs without handling conflicts
This commit is contained in:
parent
4dd0bea78b
commit
6fe9b71e16
4 changed files with 131 additions and 7 deletions
|
@ -14,9 +14,10 @@ pub mod calendar;
|
||||||
pub use calendar::Calendar;
|
pub use calendar::Calendar;
|
||||||
mod task;
|
mod task;
|
||||||
pub use task::Task;
|
pub use task::Task;
|
||||||
|
pub mod provider;
|
||||||
|
pub use provider::Provider;
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod provider;
|
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
|
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|
100
src/provider.rs
100
src/provider.rs
|
@ -1,3 +1,101 @@
|
||||||
//! This modules abstracts data sources and merges them in a single virtual one
|
//! This modules abstracts data sources and merges them in a single virtual one
|
||||||
|
|
||||||
pub struct Provider {}
|
use std::error::Error;
|
||||||
|
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
use crate::traits::CalDavSource;
|
||||||
|
use crate::Calendar;
|
||||||
|
use crate::Task;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Provider<S, L>
|
||||||
|
where
|
||||||
|
S: CalDavSource,
|
||||||
|
L: CalDavSource,
|
||||||
|
{
|
||||||
|
/// The remote server
|
||||||
|
server: S,
|
||||||
|
/// The local cache
|
||||||
|
local: L,
|
||||||
|
|
||||||
|
/// The last time the provider successfully synchronized both sources
|
||||||
|
last_sync: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S,L> Provider<S, L>
|
||||||
|
where
|
||||||
|
S: CalDavSource,
|
||||||
|
L: CalDavSource,
|
||||||
|
{
|
||||||
|
/// Create a provider that will merge both sources
|
||||||
|
pub fn new(server: S, local: L, last_sync: DateTime<Utc>) -> Self {
|
||||||
|
Self { server, local, last_sync }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn server(&self) -> &S { &self.server }
|
||||||
|
pub fn local(&self) -> &L { &self.local }
|
||||||
|
|
||||||
|
pub async fn sync(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
self.pull_items_from_server().await?;
|
||||||
|
self.resolve_conflicts().await;
|
||||||
|
self.push_items_to_server().await;
|
||||||
|
|
||||||
|
// what to do with errors? Return Err directly? Go ahead as far as we can?
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn pull_items_from_server(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
let cals_server = self.server.get_calendars_mut().await?;
|
||||||
|
|
||||||
|
for cal_server in cals_server {
|
||||||
|
let cal_local = match self.local.get_calendar_mut(cal_server.url().clone()).await {
|
||||||
|
None => {
|
||||||
|
log::error!("TODO: implement here");
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
Some(cal) => cal,
|
||||||
|
};
|
||||||
|
|
||||||
|
let server_mod = cal_server.get_tasks_modified_since(Some(self.last_sync), None);
|
||||||
|
let local_mod = cal_local.get_tasks_modified_since(Some(self.last_sync), None);
|
||||||
|
|
||||||
|
let mut tasks_to_add_to_local = Vec::new();
|
||||||
|
for (new_id, new_item) in &server_mod {
|
||||||
|
tasks_to_add_to_local.push((*new_item).clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tasks_to_add_to_server = Vec::new();
|
||||||
|
for (new_id, new_item) in &local_mod {
|
||||||
|
if server_mod.contains_key(new_id) {
|
||||||
|
log::warn!("Conflict for task {} ({}). Using the server version.", new_item.name(), new_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tasks_to_add_to_server.push((*new_item).clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
move_to_calendar(&mut tasks_to_add_to_local, cal_local);
|
||||||
|
move_to_calendar(&mut tasks_to_add_to_server, cal_server);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn resolve_conflicts(&mut self) {
|
||||||
|
log::error!("We should do something here");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn push_items_to_server(&mut self) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn move_to_calendar(tasks: &mut Vec<Task>, calendar: &mut Calendar) {
|
||||||
|
while tasks.len() > 0 {
|
||||||
|
let task = tasks.remove(0);
|
||||||
|
calendar.add_task(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
10
src/task.rs
10
src/task.rs
|
@ -1,10 +1,12 @@
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use chrono::{Utc, DateTime};
|
use chrono::{Utc, DateTime};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
// TODO: turn into this one day
|
// TODO: turn into this one day
|
||||||
// pub type TaskId = String; // This is an HTML "etag"
|
// pub type TaskId = String; // This is an HTML "etag"
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
pub struct TaskId {
|
pub struct TaskId {
|
||||||
content: String,
|
content: String,
|
||||||
}
|
}
|
||||||
|
@ -14,6 +16,12 @@ impl TaskId{
|
||||||
Self { content:u }
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A to-do task
|
/// A to-do task
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -18,18 +18,35 @@ async fn test_sync() {
|
||||||
let mut provider = populate_test_provider().await;
|
let mut provider = populate_test_provider().await;
|
||||||
provider.sync().await.unwrap();
|
provider.sync().await.unwrap();
|
||||||
|
|
||||||
let cal_server = provider.server().get_calendars().await.unwrap();
|
let cals_server = provider.server().get_calendars().await.unwrap();
|
||||||
let cal_local = provider.local().get_calendars().await.unwrap();
|
let cals_local = provider.local().get_calendars().await.unwrap();
|
||||||
assert_eq!(cal_server, cal_local, "{:#?}\n{:#?}", cal_server, cal_local);
|
print_calendar_list(cals_local);
|
||||||
|
panic!();
|
||||||
|
|
||||||
|
//assert_eq!(cal_server, cal_local, "{:#?}\n{:#?}", cal_server, cal_local);
|
||||||
|
|
||||||
panic!("TODO: also check that the contents are expected!");
|
panic!("TODO: also check that the contents are expected!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A debug utility that pretty-prints calendars
|
||||||
|
fn print_calendar_list(cals: &Vec<Calendar>) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Populate sources with the following:
|
/// Populate sources with the following:
|
||||||
/// * At the last sync: both sources had A, B, C, D, E, F, G, H at last sync
|
/// * At the last sync: both sources had A, B, C, D, E, F, G, H at last sync
|
||||||
/// * Before the newer sync, this will be the content of the sources:
|
/// * Before the newer sync, this will be the content of the sources:
|
||||||
/// * server: A, C, D, E', F', G~, H , I
|
/// * server: A, C, D, E', F', G~, H , I
|
||||||
/// * cache: A, B, D', E, F'', G , H~, J
|
/// * cache: A, B, D', E, F'', G , H~, J
|
||||||
|
///
|
||||||
|
/// Hence, here is the expected result after the sync:
|
||||||
|
/// * both: A, D', E', F', G~, H~, I, J
|
||||||
///
|
///
|
||||||
/// Notes:
|
/// Notes:
|
||||||
/// * X': name has been modified since the last sync
|
/// * X': name has been modified since the last sync
|
||||||
|
|
Loading…
Add table
Reference in a new issue