Skip to main content

icydb_core/
view.rs

1use crate::traits::{CreateView, UpdateView, View as OtherView};
2use candid::CandidType;
3use serde::{Deserialize, Serialize};
4
5///
6/// Type Aliases
7///
8
9pub type View<T> = <T as OtherView>::ViewType;
10pub type Create<T> = <T as CreateView>::CreateViewType;
11pub type Update<T> = <T as UpdateView>::UpdateViewType;
12
13///
14/// ListPatch
15///
16
17/// Patches apply sequentially; indices outside the current length are clamped to the tail and
18/// invalid removals are ignored.
19#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
20pub enum ListPatch<U> {
21    Update { index: usize, patch: U },
22    Insert { index: usize, value: U },
23    Push { value: U },
24    Overwrite { values: Vec<U> },
25    Remove { index: usize },
26    Clear,
27}
28
29///
30/// SetPatch
31///
32
33/// Set operations applied in-order; `Overwrite` replaces the entire set.
34#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
35pub enum SetPatch<U> {
36    Insert(U),
37    Remove(U),
38    Overwrite { values: Vec<U> },
39    Clear,
40}
41
42///
43/// MapPatch
44///
45
46/// Map mutations applied sequentially; later operations win. `Overwrite` replaces
47/// the entire map with the provided entries.
48#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
49pub enum MapPatch<K, V> {
50    Upsert { key: K, value: V },
51    Remove { key: K },
52    Overwrite { entries: Vec<(K, V)> },
53    Clear,
54}
55
56impl<K, V> From<(K, Option<V>)> for MapPatch<K, V> {
57    fn from((key, value): (K, Option<V>)) -> Self {
58        match value {
59            Some(value) => Self::Upsert { key, value },
60            None => Self::Remove { key },
61        }
62    }
63}
64
65///
66/// TESTS
67///
68
69#[cfg(test)]
70mod test {
71    use super::*;
72    use std::collections::{HashMap, HashSet};
73
74    #[test]
75    fn vec_partial_patches() {
76        let mut values = vec![10u8, 20, 30];
77        let patches = vec![
78            ListPatch::Update {
79                index: 1,
80                patch: 99,
81            },
82            ListPatch::Insert {
83                index: 1,
84                value: 11,
85            },
86            ListPatch::Remove { index: 0 },
87        ];
88
89        values.merge(patches).expect("vec merge should succeed");
90        assert_eq!(values, vec![11, 99, 30]);
91    }
92
93    #[test]
94    fn vec_overwrite_replaces_contents() {
95        let mut values = vec![1u8, 2, 3];
96        let patches = vec![ListPatch::Overwrite {
97            values: vec![9u8, 8],
98        }];
99
100        values
101            .merge(patches)
102            .expect("vec overwrite merge should succeed");
103        assert_eq!(values, vec![9, 8]);
104    }
105
106    #[test]
107    fn set_insert_remove_without_clear() {
108        let mut set: HashSet<u8> = [1, 2, 3].into_iter().collect();
109        let patches = vec![SetPatch::Remove(2), SetPatch::Insert(4)];
110
111        set.merge(patches).expect("set merge should succeed");
112        let expected: HashSet<u8> = [1, 3, 4].into_iter().collect();
113        assert_eq!(set, expected);
114    }
115
116    #[test]
117    fn set_overwrite_replaces_contents() {
118        let mut set: HashSet<u8> = [1, 2, 3].into_iter().collect();
119        let patches = vec![SetPatch::Overwrite {
120            values: vec![3u8, 4, 5],
121        }];
122
123        set.merge(patches)
124            .expect("set overwrite merge should succeed");
125        let expected: HashSet<u8> = [3, 4, 5].into_iter().collect();
126        assert_eq!(set, expected);
127    }
128
129    #[test]
130    fn map_upsert_in_place_and_remove() {
131        let mut map: HashMap<String, u8> = [("a".into(), 1u8), ("keep".into(), 9u8)]
132            .into_iter()
133            .collect();
134
135        let patches = vec![
136            MapPatch::Upsert {
137                key: "a".to_string(),
138                value: 5u8,
139            },
140            MapPatch::Remove {
141                key: "keep".to_string(),
142            },
143            MapPatch::Upsert {
144                key: "insert".to_string(),
145                value: 7u8,
146            },
147        ];
148
149        map.merge(patches).expect("map merge should succeed");
150
151        assert_eq!(map.get("a"), Some(&5));
152        assert_eq!(map.get("insert"), Some(&7));
153        assert!(!map.contains_key("keep"));
154    }
155
156    #[test]
157    fn map_overwrite_replaces_contents() {
158        let mut map: HashMap<String, u8> = [("keep".into(), 1u8), ("drop".into(), 2u8)]
159            .into_iter()
160            .collect();
161
162        let patches = vec![MapPatch::Overwrite {
163            entries: vec![
164                ("first".to_string(), 9u8),
165                ("second".to_string(), 5u8),
166                ("first".to_string(), 1u8),
167            ],
168        }];
169
170        map.merge(patches)
171            .expect("map overwrite merge should succeed");
172
173        assert_eq!(map.get("first"), Some(&1));
174        assert_eq!(map.get("second"), Some(&5));
175        assert!(!map.contains_key("keep"));
176        assert!(!map.contains_key("drop"));
177    }
178}