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);
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.merge(patches);
101 assert_eq!(values, vec![9, 8]);
102 }
103
104 #[test]
105 fn set_insert_remove_without_clear() {
106 let mut set: HashSet<u8> = [1, 2, 3].into_iter().collect();
107 let patches = vec![SetPatch::Remove(2), SetPatch::Insert(4)];
108
109 set.merge(patches);
110 let expected: HashSet<u8> = [1, 3, 4].into_iter().collect();
111 assert_eq!(set, expected);
112 }
113
114 #[test]
115 fn set_overwrite_replaces_contents() {
116 let mut set: HashSet<u8> = [1, 2, 3].into_iter().collect();
117 let patches = vec![SetPatch::Overwrite {
118 values: vec![3u8, 4, 5],
119 }];
120
121 set.merge(patches);
122 let expected: HashSet<u8> = [3, 4, 5].into_iter().collect();
123 assert_eq!(set, expected);
124 }
125
126 #[test]
127 fn map_upsert_in_place_and_remove() {
128 let mut map: HashMap<String, u8> = [("a".into(), 1u8), ("keep".into(), 9u8)]
129 .into_iter()
130 .collect();
131
132 let patches = vec![
133 MapPatch::Upsert {
134 key: "a".to_string(),
135 value: 5u8,
136 },
137 MapPatch::Remove {
138 key: "keep".to_string(),
139 },
140 MapPatch::Upsert {
141 key: "insert".to_string(),
142 value: 7u8,
143 },
144 ];
145
146 map.merge(patches);
147
148 assert_eq!(map.get("a"), Some(&5));
149 assert_eq!(map.get("insert"), Some(&7));
150 assert!(!map.contains_key("keep"));
151 }
152
153 #[test]
154 fn map_overwrite_replaces_contents() {
155 let mut map: HashMap<String, u8> = [("keep".into(), 1u8), ("drop".into(), 2u8)]
156 .into_iter()
157 .collect();
158
159 let patches = vec![MapPatch::Overwrite {
160 entries: vec![
161 ("first".to_string(), 9u8),
162 ("second".to_string(), 5u8),
163 ("first".to_string(), 1u8),
164 ],
165 }];
166
167 map.merge(patches);
168
169 assert_eq!(map.get("first"), Some(&1));
170 assert_eq!(map.get("second"), Some(&5));
171 assert!(!map.contains_key("keep"));
172 assert!(!map.contains_key("drop"));
173 }
174}