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);
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.merge(patches);
101        assert_eq!(values, vec![9, 8]);
102    }
103
104    #[test]
105    fn set_insert_remove_without_clear() {
106        let mut set: HashSet<u8> = [1, 2, 3].into_iter().collect();
107        let patches = vec![SetPatch::Remove(2), SetPatch::Insert(4)];
108
109        set.merge(patches);
110        let expected: HashSet<u8> = [1, 3, 4].into_iter().collect();
111        assert_eq!(set, expected);
112    }
113
114    #[test]
115    fn set_overwrite_replaces_contents() {
116        let mut set: HashSet<u8> = [1, 2, 3].into_iter().collect();
117        let patches = vec![SetPatch::Overwrite {
118            values: vec![3u8, 4, 5],
119        }];
120
121        set.merge(patches);
122        let expected: HashSet<u8> = [3, 4, 5].into_iter().collect();
123        assert_eq!(set, expected);
124    }
125
126    #[test]
127    fn map_upsert_in_place_and_remove() {
128        let mut map: HashMap<String, u8> = [("a".into(), 1u8), ("keep".into(), 9u8)]
129            .into_iter()
130            .collect();
131
132        let patches = vec![
133            MapPatch::Upsert {
134                key: "a".to_string(),
135                value: 5u8,
136            },
137            MapPatch::Remove {
138                key: "keep".to_string(),
139            },
140            MapPatch::Upsert {
141                key: "insert".to_string(),
142                value: 7u8,
143            },
144        ];
145
146        map.merge(patches);
147
148        assert_eq!(map.get("a"), Some(&5));
149        assert_eq!(map.get("insert"), Some(&7));
150        assert!(!map.contains_key("keep"));
151    }
152
153    #[test]
154    fn map_overwrite_replaces_contents() {
155        let mut map: HashMap<String, u8> = [("keep".into(), 1u8), ("drop".into(), 2u8)]
156            .into_iter()
157            .collect();
158
159        let patches = vec![MapPatch::Overwrite {
160            entries: vec![
161                ("first".to_string(), 9u8),
162                ("second".to_string(), 5u8),
163                ("first".to_string(), 1u8),
164            ],
165        }];
166
167        map.merge(patches);
168
169        assert_eq!(map.get("first"), Some(&1));
170        assert_eq!(map.get("second"), Some(&5));
171        assert!(!map.contains_key("keep"));
172        assert!(!map.contains_key("drop"));
173    }
174}