icydb_core/
view.rs

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