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)]
35pub enum SetPatch<U> {
36 Insert(U),
37 Remove(U),
38 Overwrite { values: Vec<U> },
39 Clear,
40}
41
42#[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#[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}