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#[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#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
47pub enum MapPatch<K, V> {
48    Upsert { key: K, value: V },
49    Remove { key: K },
50    Overwrite { entries: Vec<(K, V)> },
51    Clear,
52}
53
54impl<K, V> From<(K, Option<V>)> for MapPatch<K, V> {
55    fn from((key, value): (K, Option<V>)) -> Self {
56        match value {
57            Some(value) => Self::Upsert { key, value },
58            None => Self::Remove { key },
59        }
60    }
61}
62
63///
64/// TESTS
65///
66
67#[cfg(test)]
68mod test {
69    use super::{ListPatch, MapPatch, SetPatch};
70    use crate::traits::UpdateView;
71    use std::collections::{HashMap, HashSet};
72
73    #[test]
74    fn vec_partial_patches() {
75        let mut values = vec![10u8, 20, 30];
76        let patches = vec![
77            ListPatch::Update {
78                index: 1,
79                patch: 99,
80            },
81            ListPatch::Insert {
82                index: 1,
83                value: 11,
84            },
85            ListPatch::Remove { index: 0 },
86        ];
87
88        values.merge(patches);
89        assert_eq!(values, vec![11, 99, 30]);
90    }
91
92    #[test]
93    fn vec_overwrite_replaces_contents() {
94        let mut values = vec![1u8, 2, 3];
95        let patches = vec![ListPatch::Overwrite {
96            values: vec![9u8, 8],
97        }];
98
99        values.merge(patches);
100        assert_eq!(values, vec![9, 8]);
101    }
102
103    #[test]
104    fn set_insert_remove_without_clear() {
105        let mut set: HashSet<u8> = [1, 2, 3].into_iter().collect();
106        let patches = vec![SetPatch::Remove(2), SetPatch::Insert(4)];
107
108        set.merge(patches);
109        let expected: HashSet<u8> = [1, 3, 4].into_iter().collect();
110        assert_eq!(set, expected);
111    }
112
113    #[test]
114    fn set_overwrite_replaces_contents() {
115        let mut set: HashSet<u8> = [1, 2, 3].into_iter().collect();
116        let patches = vec![SetPatch::Overwrite {
117            values: vec![3u8, 4, 5],
118        }];
119
120        set.merge(patches);
121        let expected: HashSet<u8> = [3, 4, 5].into_iter().collect();
122        assert_eq!(set, expected);
123    }
124
125    #[test]
126    fn map_upsert_in_place_and_remove() {
127        let mut map: HashMap<String, u8> = [("a".into(), 1u8), ("keep".into(), 9u8)]
128            .into_iter()
129            .collect();
130
131        let patches = vec![
132            MapPatch::Upsert {
133                key: "a".to_string(),
134                value: 5u8,
135            },
136            MapPatch::Remove {
137                key: "keep".to_string(),
138            },
139            MapPatch::Upsert {
140                key: "insert".to_string(),
141                value: 7u8,
142            },
143        ];
144
145        map.merge(patches);
146
147        assert_eq!(map.get("a"), Some(&5));
148        assert_eq!(map.get("insert"), Some(&7));
149        assert!(!map.contains_key("keep"));
150    }
151
152    #[test]
153    fn map_overwrite_replaces_contents() {
154        let mut map: HashMap<String, u8> = [("keep".into(), 1u8), ("drop".into(), 2u8)]
155            .into_iter()
156            .collect();
157
158        let patches = vec![MapPatch::Overwrite {
159            entries: vec![
160                ("first".to_string(), 9u8),
161                ("second".to_string(), 5u8),
162                ("first".to_string(), 1u8),
163            ],
164        }];
165
166        map.merge(patches);
167
168        assert_eq!(map.get("first"), Some(&1));
169        assert_eq!(map.get("second"), Some(&5));
170        assert!(!map.contains_key("keep"));
171        assert!(!map.contains_key("drop"));
172    }
173}