1use crate::traits::{CreateView, FilterView, UpdateView, View as OtherView};
2use candid::CandidType;
3use serde::{Deserialize, Serialize};
4
5pub 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#[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#[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#[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#[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}