1use crate::traits::{CreateView, 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;
12
13#[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#[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)]
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#[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).expect("vec merge should succeed");
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
101 .merge(patches)
102 .expect("vec overwrite merge should succeed");
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).expect("set merge should succeed");
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 .expect("set overwrite merge should succeed");
125 let expected: HashSet<u8> = [3, 4, 5].into_iter().collect();
126 assert_eq!(set, expected);
127 }
128
129 #[test]
130 fn map_upsert_in_place_and_remove() {
131 let mut map: HashMap<String, u8> = [("a".into(), 1u8), ("keep".into(), 9u8)]
132 .into_iter()
133 .collect();
134
135 let patches = vec![
136 MapPatch::Upsert {
137 key: "a".to_string(),
138 value: 5u8,
139 },
140 MapPatch::Remove {
141 key: "keep".to_string(),
142 },
143 MapPatch::Upsert {
144 key: "insert".to_string(),
145 value: 7u8,
146 },
147 ];
148
149 map.merge(patches).expect("map merge should succeed");
150
151 assert_eq!(map.get("a"), Some(&5));
152 assert_eq!(map.get("insert"), Some(&7));
153 assert!(!map.contains_key("keep"));
154 }
155
156 #[test]
157 fn map_overwrite_replaces_contents() {
158 let mut map: HashMap<String, u8> = [("keep".into(), 1u8), ("drop".into(), 2u8)]
159 .into_iter()
160 .collect();
161
162 let patches = vec![MapPatch::Overwrite {
163 entries: vec![
164 ("first".to_string(), 9u8),
165 ("second".to_string(), 5u8),
166 ("first".to_string(), 1u8),
167 ],
168 }];
169
170 map.merge(patches)
171 .expect("map overwrite merge should succeed");
172
173 assert_eq!(map.get("first"), Some(&1));
174 assert_eq!(map.get("second"), Some(&5));
175 assert!(!map.contains_key("keep"));
176 assert!(!map.contains_key("drop"));
177 }
178}