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::{ListPatch, MapPatch, SetPatch};
73    use crate::traits::UpdateView;
74    use std::collections::{HashMap, HashSet};
75
76    #[test]
77    fn vec_partial_patches() {
78        let mut values = vec![10u8, 20, 30];
79        let patches = vec![
80            ListPatch::Update {
81                index: 1,
82                patch: 99,
83            },
84            ListPatch::Insert {
85                index: 1,
86                value: 11,
87            },
88            ListPatch::Remove { index: 0 },
89        ];
90
91        values.merge(patches);
92        assert_eq!(values, vec![11, 99, 30]);
93    }
94
95    #[test]
96    fn vec_overwrite_replaces_contents() {
97        let mut values = vec![1u8, 2, 3];
98        let patches = vec![ListPatch::Overwrite {
99            values: vec![9u8, 8],
100        }];
101
102        values.merge(patches);
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);
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        let expected: HashSet<u8> = [3, 4, 5].into_iter().collect();
125        assert_eq!(set, expected);
126    }
127
128    #[test]
129    fn map_upsert_in_place_and_remove() {
130        let mut map: HashMap<String, u8> = [("a".into(), 1u8), ("keep".into(), 9u8)]
131            .into_iter()
132            .collect();
133
134        let patches = vec![
135            MapPatch::Upsert {
136                key: "a".to_string(),
137                value: 5u8,
138            },
139            MapPatch::Remove {
140                key: "keep".to_string(),
141            },
142            MapPatch::Upsert {
143                key: "insert".to_string(),
144                value: 7u8,
145            },
146        ];
147
148        map.merge(patches);
149
150        assert_eq!(map.get("a"), Some(&5));
151        assert_eq!(map.get("insert"), Some(&7));
152        assert!(!map.contains_key("keep"));
153    }
154
155    #[test]
156    fn map_overwrite_replaces_contents() {
157        let mut map: HashMap<String, u8> = [("keep".into(), 1u8), ("drop".into(), 2u8)]
158            .into_iter()
159            .collect();
160
161        let patches = vec![MapPatch::Overwrite {
162            entries: vec![
163                ("first".to_string(), 9u8),
164                ("second".to_string(), 5u8),
165                ("first".to_string(), 1u8),
166            ],
167        }];
168
169        map.merge(patches);
170
171        assert_eq!(map.get("first"), Some(&1));
172        assert_eq!(map.get("second"), Some(&5));
173        assert!(!map.contains_key("keep"));
174        assert!(!map.contains_key("drop"));
175    }
176}