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)]
23pub enum ListPatch<U> {
24 Update { index: usize, patch: U },
25 Insert { index: usize, value: U },
26 Push { value: U },
27 Overwrite { values: Vec<U> },
28 Remove { index: usize },
29 Clear,
30}
31
32#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
38pub enum SetPatch<U> {
39 Insert(U),
40 Remove(U),
41 Overwrite { values: Vec<U> },
42 Clear,
43}
44
45#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
52pub enum MapPatch<K, V> {
53 Upsert { key: K, value: V },
54 Remove { key: K },
55 Overwrite { entries: Vec<(K, V)> },
56 Clear,
57}
58
59impl<K, V> From<(K, Option<V>)> for MapPatch<K, V> {
60 fn from((key, value): (K, Option<V>)) -> Self {
61 match value {
62 Some(value) => Self::Upsert { key, value },
63 None => Self::Remove { key },
64 }
65 }
66}
67
68#[cfg(test)]
73mod tests {
74 use super::*;
75 use std::collections::{HashMap, HashSet};
76
77 #[test]
78 fn vec_partial_patches() {
79 let mut values = vec![10u8, 20, 30];
80 let patches = vec![
81 ListPatch::Update {
82 index: 1,
83 patch: 99,
84 },
85 ListPatch::Insert {
86 index: 1,
87 value: 11,
88 },
89 ListPatch::Remove { index: 0 },
90 ];
91
92 values.merge(patches);
93 assert_eq!(values, vec![11, 99, 30]);
94 }
95
96 #[test]
97 fn vec_overwrite_replaces_contents() {
98 let mut values = vec![1u8, 2, 3];
99 let patches = vec![ListPatch::Overwrite {
100 values: vec![9u8, 8],
101 }];
102
103 values.merge(patches);
104 assert_eq!(values, vec![9, 8]);
105 }
106
107 #[test]
108 fn set_insert_remove_without_clear() {
109 let mut set: HashSet<u8> = [1, 2, 3].into_iter().collect();
110 let patches = vec![SetPatch::Remove(2), SetPatch::Insert(4)];
111
112 set.merge(patches);
113 let expected: HashSet<u8> = [1, 3, 4].into_iter().collect();
114 assert_eq!(set, expected);
115 }
116
117 #[test]
118 fn set_overwrite_replaces_contents() {
119 let mut set: HashSet<u8> = [1, 2, 3].into_iter().collect();
120 let patches = vec![SetPatch::Overwrite {
121 values: vec![3u8, 4, 5],
122 }];
123
124 set.merge(patches);
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);
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
172 assert_eq!(map.get("first"), Some(&1));
173 assert_eq!(map.get("second"), Some(&5));
174 assert!(!map.contains_key("keep"));
175 assert!(!map.contains_key("drop"));
176 }
177}