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 if self.patches_loaded.contains(patch.patch_ref()) {
89 return Err(Error::PatchAlreadyLoaded {
90 patch: *patch.patch_ref(),
91 });
92 }
93
94 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 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 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}