1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use super::PageRange;
use crate::digest::PageDigest;

/// An owned point-in-time snapshot of the [`PageRange`] returned from a call to
/// [`MerkleSearchTree::serialise_page_ranges()`].
///
/// A [`PageRangeSnapshot`] clones all [`PageRange`] bounding keys, and
/// therefore can only be generated if the key type `K` implements [`Clone`].
///
/// ```
/// # use merkle_search_tree::{*, diff::*};
/// #
/// let mut t = MerkleSearchTree::default();
/// t.upsert("bananas", &42);
///
/// // Rehash the tree before generating the page ranges
/// let _ = t.root_hash();
///
/// // Generate the hashes & page ranges, immutably borrowing the tree
/// let ranges = t.serialise_page_ranges().unwrap();
///
/// // Obtain an owned PageRangeSnapshot from the borrowed PageRange, in turn
/// // releasing the immutable reference to the tree.
/// let snap = PageRangeSnapshot::from(ranges);
///
/// // The tree is now mutable again.
/// t.upsert("platanos", &42);
/// ```
///
/// [`MerkleSearchTree::serialise_page_ranges()`]:
///     crate::MerkleSearchTree::serialise_page_ranges
#[derive(Debug, Clone, PartialEq)]
pub struct PageRangeSnapshot<K>(Vec<OwnedPageRange<K>>);

impl<K> PageRangeSnapshot<K> {
    /// Return an iterator of [`PageRange`] from the snapshot content.
    pub fn iter(&self) -> impl Iterator<Item = PageRange<'_, K>>
    where
        K: PartialOrd,
    {
        self.0
            .iter()
            .map(|v| PageRange::new(&v.start, &v.end, v.hash.clone()))
    }
}

impl<'a, K> From<Vec<PageRange<'a, K>>> for PageRangeSnapshot<K>
where
    K: Clone,
{
    fn from(value: Vec<PageRange<'a, K>>) -> Self {
        Self(value.into_iter().map(OwnedPageRange::from).collect())
    }
}

/// An internal type holding an owned key interval & page hash.
#[derive(Debug, Clone, PartialEq, Eq)]
struct OwnedPageRange<K> {
    start: K,
    end: K,
    hash: PageDigest,
}

impl<'a, K> From<PageRange<'a, K>> for OwnedPageRange<K>
where
    K: Clone,
{
    fn from(v: PageRange<'a, K>) -> Self {
        Self {
            start: v.start().clone(),
            end: v.end().clone(),
            hash: v.into_hash(),
        }
    }
}

#[cfg(test)]
mod tests {
    use assert_matches::assert_matches;

    use super::*;
    use crate::{diff::diff, MerkleSearchTree};

    #[test]
    fn test_owned_usage() {
        let mut a = MerkleSearchTree::default();
        let mut b = MerkleSearchTree::default();

        a.upsert("bananas", &42);
        b.upsert("bananas", &24);

        // Rehash the tree
        let _ = a.root_hash();
        let _ = b.root_hash();

        // Generate owned snapshots from the borrowed page ranges
        let snap_a = PageRangeSnapshot::from(a.serialise_page_ranges().unwrap());
        let snap_b = PageRangeSnapshot::from(b.serialise_page_ranges().unwrap());

        // Tree should be mutable whilst snapshots are in scope
        a.upsert("bananas", &13);
        b.upsert("bananas", &13);

        // Which should be usable for diff generation (and not reflect the
        // updated state since the trees were mutated).
        assert_matches!(diff(snap_a.iter(), snap_b.iter()).as_slice(), [range] => {
            assert_eq!(range.start(), &"bananas");
            assert_eq!(range.end(), &"bananas");
        });
    }
}