augr_core/
repository.rs

1pub mod event;
2pub mod timesheet;
3
4use crate::{Meta, Patch, PatchRef, Store};
5use snafu::{ResultExt, Snafu};
6use std::collections::{BTreeSet, VecDeque};
7use timesheet::{Error as TimesheetError, PatchedTimesheet};
8
9#[derive(Eq, PartialEq, Debug, Snafu)]
10pub enum Error<IE>
11where
12    IE: std::error::Error + 'static,
13{
14    #[snafu(display("Unable to load metadata: {}", source))]
15    LoadMeta { source: IE },
16
17    #[snafu(display("Unable to save metadata: {}", source))]
18    SaveMeta { source: IE },
19
20    #[snafu(display("Unable to save patch {} to disk: {}", patch, source))]
21    SavePatch { source: IE, patch: PatchRef },
22
23    #[snafu(display("Unable to load patch {}: {}", patch, source))]
24    PatchNotFound { source: IE, patch: PatchRef },
25
26    #[snafu(display("Patch {} already loaded", patch))]
27    PatchAlreadyLoaded { patch: PatchRef },
28
29    #[snafu(display("Parents of patch {} are not loaded", patch))]
30    MissingParentPatches {
31        patch: PatchRef,
32        parents: Vec<PatchRef>,
33    },
34
35    #[snafu(display("Patch {} could not be applied to timesheet: {:?}", patch, conflicts))]
36    PatchingTimesheet {
37        conflicts: Vec<TimesheetError>,
38        patch: PatchRef,
39    },
40
41    #[snafu(display("IOError: {}", source))]
42    IOError { source: IE },
43}
44
45#[derive(Debug)]
46pub struct Repository<S: Store> {
47    store: S,
48    patches_loaded: BTreeSet<PatchRef>,
49    timesheet: PatchedTimesheet,
50}
51
52impl<S> Repository<S>
53where
54    S: Store,
55    <S as Store>::Error: 'static,
56{
57    #[cfg_attr(feature = "flame_it", flame)]
58    pub fn from_store(store: S) -> Result<Self, Vec<Error<S::Error>>> {
59        let mut repo = Self {
60            store,
61            patches_loaded: BTreeSet::new(),
62            timesheet: PatchedTimesheet::new(),
63        };
64        repo.load_all_patches()?;
65        Ok(repo)
66    }
67
68    #[cfg_attr(feature = "flame_it", flame)]
69    pub fn save_meta(&mut self) -> Result<(), Error<S::Error>> {
70        let mut meta = Meta::new();
71        for p in self.patches_loaded.iter() {
72            meta.add_patch(p.clone());
73        }
74        self.store.save_meta(&meta).context(SaveMeta {})
75    }
76
77    pub fn add_patch(&mut self, patch: Patch) -> Result<(), Error<S::Error>> {
78        self.load_patch(patch.clone())?;
79        self.store.add_patch(&patch).context(SavePatch {
80            patch: *patch.patch_ref(),
81        })?;
82        Ok(())
83    }
84
85    #[cfg_attr(feature = "flame_it", flame)]
86    pub fn load_patch(&mut self, patch: Patch) -> Result<(), Error<S::Error>> {
87        // Don't apply patches twice
88        if self.patches_loaded.contains(patch.patch_ref()) {
89            return Err(Error::PatchAlreadyLoaded {
90                patch: *patch.patch_ref(),
91            });
92        }
93
94        // Check that all of the patches parent patches have been loaded
95        let mut missing_patches = Vec::new();
96        for parent_patch_ref in patch.parents() {
97            if !self.patches_loaded.contains(&parent_patch_ref) {
98                missing_patches.push(parent_patch_ref);
99            }
100        }
101        if !missing_patches.is_empty() {
102            return Err(Error::MissingParentPatches {
103                patch: *patch.patch_ref(),
104                parents: missing_patches,
105            });
106        }
107
108        // Mark patch as loaded
109        self.patches_loaded.insert(patch.patch_ref().clone());
110
111        self.timesheet
112            .apply_patch(&patch)
113            .map_err(|conflicts| Error::PatchingTimesheet {
114                patch: *patch.patch_ref(),
115                conflicts,
116            })
117    }
118
119    pub fn timesheet(&self) -> &PatchedTimesheet {
120        &self.timesheet
121    }
122
123    #[cfg_attr(feature = "flame_it", flame)]
124    fn load_patches(
125        &mut self,
126        patches: impl Iterator<Item = PatchRef>,
127    ) -> Result<(), Vec<Error<S::Error>>> {
128        let mut errors = Vec::new();
129
130        let mut error_on_loading: BTreeSet<PatchRef> = BTreeSet::new();
131
132        let mut patches_to_load: VecDeque<PatchRef> = patches.collect();
133        while let Some(patch_ref) = patches_to_load.pop_front() {
134            // Don't load patches that have already been loaded
135            if self.patches_loaded.contains(&patch_ref) {
136                continue;
137            }
138
139            let patch = match self.store.get_patch(&patch_ref) {
140                Ok(p) => p,
141                Err(source) => {
142                    errors.push(Error::PatchNotFound {
143                        source,
144                        patch: patch_ref,
145                    });
146                    continue;
147                }
148            };
149
150            match self.load_patch(patch) {
151                Ok(()) => {}
152                Err(Error::MissingParentPatches { parents, .. }) => {
153                    for parent in parents {
154                        if !error_on_loading.contains(&parent) {
155                            patches_to_load.push_back(parent);
156                        }
157                    }
158                    patches_to_load.push_back(patch_ref);
159                }
160                Err(Error::PatchAlreadyLoaded { .. }) => {}
161                Err(patch_errors) => {
162                    errors.push(patch_errors);
163                    error_on_loading.insert(patch_ref);
164                }
165            }
166        }
167
168        if !errors.is_empty() {
169            Err(errors)
170        } else {
171            Ok(())
172        }
173    }
174
175    #[cfg_attr(feature = "flame_it", flame)]
176    fn load_all_patches(&mut self) -> Result<(), Vec<Error<S::Error>>> {
177        let meta = self
178            .store
179            .get_meta()
180            .context(LoadMeta {})
181            .map_err(|e| vec![e])?;
182
183        self.load_patches(meta.patches().cloned())
184    }
185}
186
187use crate::store::sync_folder_store::{SyncFolderStore, SyncFolderStoreError};
188
189impl Repository<SyncFolderStore> {
190    #[cfg_attr(feature = "flame_it", flame)]
191    pub fn try_sync_data(&mut self) -> Result<(), Vec<Error<SyncFolderStoreError>>> {
192        let metas = self
193            .store
194            .get_other_metas()
195            .context(IOError {})
196            .map_err(|e| vec![e])?;
197
198        let patches_to_load: Vec<PatchRef> = metas
199            .filter_map(|x| x.ok())
200            .flat_map(|meta| meta.patches().copied().collect::<Vec<_>>().into_iter())
201            .collect();
202
203        self.load_patches(patches_to_load.into_iter())
204    }
205}