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::{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}