diff --git a/cli/src/main.rs b/cli/src/main.rs index b7b59c4..5ffec85 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -58,6 +58,11 @@ pub enum Error { #[snafu(display("Error importing data: {}", source))] ImportError { source: Box }, + + #[snafu(display("Errors synchronizing data: {:?}", errors))] + SyncError { + errors: Vec>, + }, } fn main() { @@ -75,6 +80,7 @@ fn main() { fn run() -> Result<(), Error> { let opt = Opt::from_args(); + // Load config let conf_file = match opt.config { Some(config_path) => config_path, None => { @@ -82,31 +88,37 @@ fn run() -> Result<(), Error> { proj_dirs.config_dir().join("config.toml") } }; - let conf = config::load_config(&conf_file).context(GetConfig {})?; + // Load store for own data let store = SyncFolderStore::new(conf.sync_folder.into(), conf.device_id).should_init(true); let mut repo = Repository::from_store(store).unwrap(); + + // Synchronize data + repo.try_sync_data() + .map_err(|errors| Error::SyncError { errors })?; + repo.save_meta().unwrap(); + + // Convert abstract patch data structure into a more conventional format let eventgraph = repo.timesheet(); let timesheet = eventgraph .flatten() .map_err(|conflicts| Error::MergeConflicts { conflicts })?; + // Run command match opt.cmd.unwrap_or(Command::default()) { Command::Start(subcmd) => { let patches = subcmd.exec(×heet); for patch in patches { - let patch_ref = String::new(); - let res = repo.add_patch(patch).unwrap(); - println!("{}: {:?}", patch_ref, res); + println!("{}", patch.patch_ref()); + repo.add_patch(patch).unwrap(); } } Command::Import(subcmd) => { let patches = subcmd.exec(×heet).context(ImportError {})?; for patch in patches { - let patch_ref = String::new(); - let res = repo.add_patch(patch).unwrap(); - println!("{}: {:?}", patch_ref, res); + println!("{}", patch.patch_ref()); + repo.add_patch(patch).unwrap(); } } Command::Summary(subcmd) => subcmd.exec(×heet), @@ -114,6 +126,7 @@ fn run() -> Result<(), Error> { Command::Tags(subcmd) => subcmd.exec(×heet), }; + // Save which patches this device uses to disk repo.save_meta().unwrap(); Ok(()) diff --git a/core/src/repository.rs b/core/src/repository.rs index 1afc6ae..79cf10d 100644 --- a/core/src/repository.rs +++ b/core/src/repository.rs @@ -37,6 +37,9 @@ where conflicts: Vec, patch: PatchRef, }, + + #[snafu(display("IOError: {}", source))] + IOError { source: IE }, } #[derive(Debug)] @@ -114,17 +117,15 @@ where &self.timesheet } - fn load_all_patches(&mut self) -> Result<(), Vec>> { + fn load_patches( + &mut self, + patches: impl Iterator, + ) -> Result<(), Vec>> { let mut errors = Vec::new(); let mut error_on_loading: BTreeSet = BTreeSet::new(); - let meta = self - .store - .get_meta() - .context(LoadMeta {}) - .map_err(|e| vec![e])?; - let mut patches_to_load: VecDeque = meta.patches().cloned().collect(); + let mut patches_to_load: VecDeque = patches.collect(); while let Some(patch_ref) = patches_to_load.pop_front() { let patch = match self.store.get_patch(&patch_ref) { Ok(p) => p, @@ -161,4 +162,38 @@ where Ok(()) } } + + fn load_all_patches(&mut self) -> Result<(), Vec>> { + let meta = self + .store + .get_meta() + .context(LoadMeta {}) + .map_err(|e| vec![e])?; + + self.load_patches(meta.patches().cloned()) + } +} + +use crate::store::sync_folder_store::{SyncFolderStore, SyncFolderStoreError}; + +impl Repository { + pub fn try_sync_data(&mut self) -> Result<(), Vec>> { + let metas = self + .store + .get_other_metas() + .context(IOError {}) + .map_err(|e| vec![e])?; + + let patches_to_load: Vec = metas + .filter_map(|x| x.ok()) + .flat_map(|meta| { + meta.patches() + .map(|x| x.clone()) + .collect::>() + .into_iter() + }) + .collect(); + + self.load_patches(patches_to_load.into_iter()) + } } diff --git a/core/src/store/sync_folder_store.rs b/core/src/store/sync_folder_store.rs index bedaf13..92563e7 100644 --- a/core/src/store/sync_folder_store.rs +++ b/core/src/store/sync_folder_store.rs @@ -47,6 +47,9 @@ pub enum SyncFolderStoreError { source: std::io::Error, path: PathBuf, }, + + #[snafu(display("IO error: {}", source))] + IOError { source: std::io::Error }, } impl SyncFolderStore { @@ -70,6 +73,30 @@ impl SyncFolderStore { .join(self.device_id.clone()) .with_extension("toml") } + + pub fn get_other_metas( + &self, + ) -> Result>, SyncFolderStoreError> + { + let meta_folder = self.root_folder.join("meta"); + let meta_file = self.meta_file_path(); + + let sync_folder_items = meta_folder.read_dir().context(IOError {})?; + let iter = sync_folder_items + .filter_map(|d| d.ok()) + .filter(move |dir_entry| dir_entry.path() != meta_file) + .map(|dir_entry| { + let path = dir_entry.path(); + let contents = read_to_string(&path).context(ReadFile { path: path.clone() })?; + + let meta = toml::de::from_str(&contents).context(DeserializeMeta { + device_id: path.display().to_string(), + })?; + + Ok(meta) + }); + Ok(iter) + } } impl Store for SyncFolderStore {