email/folder/sync/
patch.rs

1//! Module dedicated to email folders synchronization patch.
2//!
3//! The core structure of the module is the [`FolderSyncPatch`], which
4//! represents a list of changes (hunks).
5//!
6//! You also have access to a [`FolderSyncPatchManager`] which helps
7//! you to build and to apply a folder patch.
8
9use std::collections::{BTreeMap, BTreeSet};
10
11use super::hunk::{FolderName, FolderSyncHunk, FoldersName};
12use crate::sync::SyncDestination;
13
14/// A folder synchronization patch is just a list of folder
15/// synchronization hunks (changes).
16pub type FolderSyncPatch = BTreeSet<FolderSyncHunk>;
17
18/// A folder synchronization patches associates a folder with its own
19/// patch.
20pub type FolderSyncPatches = BTreeMap<FolderName, FolderSyncPatch>;
21
22/// Folder synchronization patch builder.
23///
24/// Contains the core algorithm of the folder synchronization. It has
25/// been exported in a dedicated function so that it can be easily
26/// tested.
27pub fn build(
28    local_cache: FoldersName,
29    local: FoldersName,
30    remote_cache: FoldersName,
31    remote: FoldersName,
32) -> FolderSyncPatches {
33    let mut folders = BTreeSet::new();
34
35    // Gathers all existing folders name.
36    folders.extend(local_cache.clone());
37    folders.extend(local.clone());
38    folders.extend(remote_cache.clone());
39    folders.extend(remote.clone());
40
41    // Given the matrix local_cache × local × remote_cache × remote,
42    // checks every 2⁴ = 16 possibilities:
43    let patches = folders.into_iter().map(|folder| {
44        let local_cache = local_cache.get(&folder);
45        let local = local.get(&folder);
46        let remote_cache = remote_cache.get(&folder);
47        let remote = remote.get(&folder);
48
49        let patch = match (local_cache, local, remote_cache, remote) {
50            // 0000
51            (None, None, None, None) => BTreeSet::from_iter([]),
52
53            // 0001
54            (None, None, None, Some(_)) => BTreeSet::from_iter([
55                FolderSyncHunk::Cache(folder.clone(), SyncDestination::Left),
56                FolderSyncHunk::Create(folder.clone(), SyncDestination::Left),
57                FolderSyncHunk::Cache(folder.clone(), SyncDestination::Right),
58            ]),
59
60            // 0010
61            (None, None, Some(_), None) => BTreeSet::from_iter([FolderSyncHunk::Uncache(
62                folder.clone(),
63                SyncDestination::Right,
64            )]),
65
66            // 0011
67            (None, None, Some(_), Some(_)) => BTreeSet::from_iter([
68                FolderSyncHunk::Cache(folder.clone(), SyncDestination::Left),
69                FolderSyncHunk::Create(folder.clone(), SyncDestination::Left),
70            ]),
71
72            // 0100
73            (None, Some(_), None, None) => BTreeSet::from_iter([
74                FolderSyncHunk::Cache(folder.clone(), SyncDestination::Left),
75                FolderSyncHunk::Cache(folder.clone(), SyncDestination::Right),
76                FolderSyncHunk::Create(folder.clone(), SyncDestination::Right),
77            ]),
78
79            // 0101
80            (None, Some(_), None, Some(_)) => BTreeSet::from_iter([
81                FolderSyncHunk::Cache(folder.clone(), SyncDestination::Left),
82                FolderSyncHunk::Cache(folder.clone(), SyncDestination::Right),
83            ]),
84
85            // 0110
86            (None, Some(_), Some(_), None) => BTreeSet::from_iter([
87                FolderSyncHunk::Cache(folder.clone(), SyncDestination::Left),
88                FolderSyncHunk::Create(folder.clone(), SyncDestination::Right),
89            ]),
90
91            // 0111
92            (None, Some(_), Some(_), Some(_)) => {
93                BTreeSet::from_iter([FolderSyncHunk::Cache(folder.clone(), SyncDestination::Left)])
94            }
95
96            // 1000
97            (Some(_), None, None, None) => BTreeSet::from_iter([FolderSyncHunk::Uncache(
98                folder.clone(),
99                SyncDestination::Left,
100            )]),
101
102            // 1001
103            (Some(_), None, None, Some(_)) => BTreeSet::from_iter([
104                FolderSyncHunk::Create(folder.clone(), SyncDestination::Left),
105                FolderSyncHunk::Cache(folder.clone(), SyncDestination::Right),
106            ]),
107
108            // 1010
109            (Some(_), None, Some(_), None) => BTreeSet::from_iter([
110                FolderSyncHunk::Uncache(folder.clone(), SyncDestination::Left),
111                FolderSyncHunk::Uncache(folder.clone(), SyncDestination::Right),
112            ]),
113
114            // 1011
115            (Some(_), None, Some(_), Some(_)) => BTreeSet::from_iter([
116                FolderSyncHunk::Uncache(folder.clone(), SyncDestination::Left),
117                FolderSyncHunk::Uncache(folder.clone(), SyncDestination::Right),
118                FolderSyncHunk::Delete(folder.clone(), SyncDestination::Right),
119            ]),
120
121            // 1100
122            (Some(_), Some(_), None, None) => BTreeSet::from_iter([
123                FolderSyncHunk::Cache(folder.clone(), SyncDestination::Right),
124                FolderSyncHunk::Create(folder.clone(), SyncDestination::Right),
125            ]),
126
127            // 1101
128            (Some(_), Some(_), None, Some(_)) => BTreeSet::from_iter([FolderSyncHunk::Cache(
129                folder.clone(),
130                SyncDestination::Right,
131            )]),
132
133            // 1110
134            (Some(_), Some(_), Some(_), None) => BTreeSet::from_iter([
135                FolderSyncHunk::Uncache(folder.clone(), SyncDestination::Left),
136                FolderSyncHunk::Delete(folder.clone(), SyncDestination::Left),
137                FolderSyncHunk::Uncache(folder.clone(), SyncDestination::Right),
138            ]),
139
140            // 1111
141            (Some(_), Some(_), Some(_), Some(_)) => BTreeSet::from_iter([]),
142        };
143
144        (folder, patch)
145    });
146
147    BTreeMap::from_iter(patches)
148}
149
150#[cfg(test)]
151mod tests {
152    use std::collections::{BTreeMap, BTreeSet};
153
154    use super::{FolderSyncHunk, FoldersName};
155    use crate::sync::SyncDestination;
156
157    #[test]
158    fn build_folder_patch() {
159        // 0000
160        assert_eq!(
161            super::build(
162                FoldersName::default(),
163                FoldersName::default(),
164                FoldersName::default(),
165                FoldersName::default(),
166            ),
167            BTreeMap::new()
168        );
169
170        // 0001
171        assert_eq!(
172            super::build(
173                FoldersName::default(),
174                FoldersName::default(),
175                FoldersName::default(),
176                FoldersName::from_iter(["folder".into()]),
177            ),
178            BTreeMap::from_iter([(
179                "folder".into(),
180                BTreeSet::from_iter([
181                    FolderSyncHunk::Cache("folder".into(), SyncDestination::Left),
182                    FolderSyncHunk::Create("folder".into(), SyncDestination::Left),
183                    FolderSyncHunk::Cache("folder".into(), SyncDestination::Right),
184                ])
185            )]),
186        );
187
188        // 0010
189        assert_eq!(
190            super::build(
191                FoldersName::default(),
192                FoldersName::default(),
193                FoldersName::from_iter(["folder".into()]),
194                FoldersName::default(),
195            ),
196            BTreeMap::from_iter([(
197                "folder".into(),
198                BTreeSet::from_iter([FolderSyncHunk::Uncache(
199                    "folder".into(),
200                    SyncDestination::Right
201                )]),
202            )]),
203        );
204
205        // 0011
206        assert_eq!(
207            super::build(
208                FoldersName::default(),
209                FoldersName::default(),
210                FoldersName::from_iter(["folder".into()]),
211                FoldersName::from_iter(["folder".into()]),
212            ),
213            BTreeMap::from_iter([(
214                "folder".into(),
215                BTreeSet::from_iter([
216                    FolderSyncHunk::Cache("folder".into(), SyncDestination::Left),
217                    FolderSyncHunk::Create("folder".into(), SyncDestination::Left),
218                ]),
219            )]),
220        );
221
222        // 0100
223        assert_eq!(
224            super::build(
225                FoldersName::default(),
226                FoldersName::from_iter(["folder".into()]),
227                FoldersName::default(),
228                FoldersName::default(),
229            ),
230            BTreeMap::from_iter([(
231                "folder".into(),
232                BTreeSet::from_iter([
233                    FolderSyncHunk::Cache("folder".into(), SyncDestination::Left),
234                    FolderSyncHunk::Cache("folder".into(), SyncDestination::Right),
235                    FolderSyncHunk::Create("folder".into(), SyncDestination::Right),
236                ]),
237            )]),
238        );
239
240        // 0101
241        assert_eq!(
242            super::build(
243                FoldersName::default(),
244                FoldersName::from_iter(["folder".into()]),
245                FoldersName::default(),
246                FoldersName::from_iter(["folder".into()]),
247            ),
248            BTreeMap::from_iter([(
249                "folder".into(),
250                BTreeSet::from_iter([
251                    FolderSyncHunk::Cache("folder".into(), SyncDestination::Left),
252                    FolderSyncHunk::Cache("folder".into(), SyncDestination::Right),
253                ]),
254            )]),
255        );
256
257        // 0110
258        assert_eq!(
259            super::build(
260                FoldersName::default(),
261                FoldersName::from_iter(["folder".into()]),
262                FoldersName::from_iter(["folder".into()]),
263                FoldersName::default(),
264            ),
265            BTreeMap::from_iter([(
266                "folder".into(),
267                BTreeSet::from_iter([
268                    FolderSyncHunk::Cache("folder".into(), SyncDestination::Left),
269                    FolderSyncHunk::Create("folder".into(), SyncDestination::Right),
270                ]),
271            )]),
272        );
273
274        // 0111
275        assert_eq!(
276            super::build(
277                FoldersName::default(),
278                FoldersName::from_iter(["folder".into()]),
279                FoldersName::from_iter(["folder".into()]),
280                FoldersName::from_iter(["folder".into()]),
281            ),
282            BTreeMap::from_iter([(
283                "folder".into(),
284                BTreeSet::from_iter([FolderSyncHunk::Cache(
285                    "folder".into(),
286                    SyncDestination::Left
287                )]),
288            )]),
289        );
290
291        // 1000
292        assert_eq!(
293            super::build(
294                FoldersName::from_iter(["folder".into()]),
295                FoldersName::default(),
296                FoldersName::default(),
297                FoldersName::default(),
298            ),
299            BTreeMap::from_iter([(
300                "folder".into(),
301                BTreeSet::from_iter([FolderSyncHunk::Uncache(
302                    "folder".into(),
303                    SyncDestination::Left
304                )]),
305            )]),
306        );
307
308        // 1001
309        assert_eq!(
310            super::build(
311                FoldersName::from_iter(["folder".into()]),
312                FoldersName::default(),
313                FoldersName::default(),
314                FoldersName::from_iter(["folder".into()]),
315            ),
316            BTreeMap::from_iter([(
317                "folder".into(),
318                BTreeSet::from_iter([
319                    FolderSyncHunk::Create("folder".into(), SyncDestination::Left),
320                    FolderSyncHunk::Cache("folder".into(), SyncDestination::Right),
321                ]),
322            )]),
323        );
324
325        // 1010
326        assert_eq!(
327            super::build(
328                FoldersName::from_iter(["folder".into()]),
329                FoldersName::default(),
330                FoldersName::from_iter(["folder".into()]),
331                FoldersName::default(),
332            ),
333            BTreeMap::from_iter([(
334                "folder".into(),
335                BTreeSet::from_iter([
336                    FolderSyncHunk::Uncache("folder".into(), SyncDestination::Left),
337                    FolderSyncHunk::Uncache("folder".into(), SyncDestination::Right),
338                ]),
339            )]),
340        );
341
342        // 1011
343        assert_eq!(
344            super::build(
345                FoldersName::from_iter(["folder".into()]),
346                FoldersName::default(),
347                FoldersName::from_iter(["folder".into()]),
348                FoldersName::from_iter(["folder".into()]),
349            ),
350            BTreeMap::from_iter([(
351                "folder".into(),
352                BTreeSet::from_iter([
353                    FolderSyncHunk::Uncache("folder".into(), SyncDestination::Left),
354                    FolderSyncHunk::Uncache("folder".into(), SyncDestination::Right),
355                    FolderSyncHunk::Delete("folder".into(), SyncDestination::Right),
356                ]),
357            )]),
358        );
359
360        // 1100
361        assert_eq!(
362            super::build(
363                FoldersName::from_iter(["folder".into()]),
364                FoldersName::from_iter(["folder".into()]),
365                FoldersName::default(),
366                FoldersName::default(),
367            ),
368            BTreeMap::from_iter([(
369                "folder".into(),
370                BTreeSet::from_iter([
371                    FolderSyncHunk::Cache("folder".into(), SyncDestination::Right),
372                    FolderSyncHunk::Create("folder".into(), SyncDestination::Right),
373                ]),
374            )]),
375        );
376
377        // 1101
378        assert_eq!(
379            super::build(
380                FoldersName::from_iter(["folder".into()]),
381                FoldersName::from_iter(["folder".into()]),
382                FoldersName::default(),
383                FoldersName::from_iter(["folder".into()]),
384            ),
385            BTreeMap::from_iter([(
386                "folder".into(),
387                BTreeSet::from_iter([FolderSyncHunk::Cache(
388                    "folder".into(),
389                    SyncDestination::Right
390                )]),
391            )]),
392        );
393
394        // 1110
395        assert_eq!(
396            super::build(
397                FoldersName::from_iter(["folder".into()]),
398                FoldersName::from_iter(["folder".into()]),
399                FoldersName::from_iter(["folder".into()]),
400                FoldersName::default(),
401            ),
402            BTreeMap::from_iter([(
403                "folder".into(),
404                BTreeSet::from_iter([
405                    FolderSyncHunk::Uncache("folder".into(), SyncDestination::Left),
406                    FolderSyncHunk::Delete("folder".into(), SyncDestination::Left),
407                    FolderSyncHunk::Uncache("folder".into(), SyncDestination::Right),
408                ]),
409            )]),
410        );
411
412        // 1111
413        assert_eq!(
414            super::build(
415                FoldersName::from_iter(["folder".into()]),
416                FoldersName::from_iter(["folder".into()]),
417                FoldersName::from_iter(["folder".into()]),
418                FoldersName::from_iter(["folder".into()]),
419            ),
420            BTreeMap::from_iter([("folder".into(), BTreeSet::from_iter([]))])
421        );
422    }
423}