1use crate::{
2 error::{ErrorClass, ErrorOrigin, InternalError},
3 view::{ListPatch, MapPatch, SetPatch},
4};
5use candid::CandidType;
6use std::{
7 collections::{HashMap, HashSet, hash_map::Entry as HashMapEntry},
8 hash::{BuildHasher, Hash},
9 iter::IntoIterator,
10};
11use thiserror::Error as ThisError;
12
13pub trait View: Sized {
20 type ViewType: Default;
21
22 fn to_view(&self) -> Self::ViewType;
23 fn from_view(view: Self::ViewType) -> Result<Self, ViewError>;
24}
25
26#[derive(Debug, ThisError)]
32pub enum ViewError {
33 #[error("Float32 view value must be finite (got {value})")]
34 Float32NonFinite { value: f32 },
35
36 #[error("Float64 view value must be finite (got {value})")]
37 Float64NonFinite { value: f64 },
38}
39
40impl From<ViewError> for InternalError {
41 fn from(err: ViewError) -> Self {
42 Self::new(
43 ErrorClass::Unsupported,
44 ErrorOrigin::Interface,
45 err.to_string(),
46 )
47 }
48}
49
50impl View for String {
51 type ViewType = Self;
52
53 fn to_view(&self) -> Self::ViewType {
54 self.clone()
55 }
56
57 fn from_view(view: Self::ViewType) -> Result<Self, ViewError> {
58 Ok(view)
59 }
60}
61
62impl<T: View> View for Box<T> {
64 type ViewType = T::ViewType;
65
66 fn to_view(&self) -> Self::ViewType {
67 T::to_view(self.as_ref())
69 }
70
71 fn from_view(view: Self::ViewType) -> Result<Self, ViewError> {
72 Ok(Self::new(T::from_view(view)?))
74 }
75}
76
77impl<T: View> View for Option<T> {
78 type ViewType = Option<T::ViewType>;
79
80 fn to_view(&self) -> Self::ViewType {
81 self.as_ref().map(View::to_view)
82 }
83
84 fn from_view(view: Self::ViewType) -> Result<Self, ViewError> {
85 match view {
86 Some(inner) => Ok(Some(T::from_view(inner)?)),
87 None => Ok(None),
88 }
89 }
90}
91
92impl<T: View> View for Vec<T> {
93 type ViewType = Vec<T::ViewType>;
94
95 fn to_view(&self) -> Self::ViewType {
96 self.iter().map(View::to_view).collect()
97 }
98
99 fn from_view(view: Self::ViewType) -> Result<Self, ViewError> {
100 view.into_iter().map(T::from_view).collect()
101 }
102}
103
104impl<T, S> View for HashSet<T, S>
105where
106 T: View + Eq + Hash + Clone,
107 S: BuildHasher + Default,
108{
109 type ViewType = Vec<T::ViewType>;
110
111 fn to_view(&self) -> Self::ViewType {
112 self.iter().map(View::to_view).collect()
113 }
114
115 fn from_view(view: Self::ViewType) -> Result<Self, ViewError> {
116 view.into_iter().map(T::from_view).collect()
117 }
118}
119
120impl<K, V, S> View for HashMap<K, V, S>
121where
122 K: View + Eq + Hash + Clone,
123 V: View,
124 S: BuildHasher + Default,
125{
126 type ViewType = Vec<(K::ViewType, V::ViewType)>;
127
128 fn to_view(&self) -> Self::ViewType {
129 self.iter()
130 .map(|(k, v)| (k.to_view(), v.to_view()))
131 .collect()
132 }
133
134 fn from_view(view: Self::ViewType) -> Result<Self, ViewError> {
135 view.into_iter()
136 .map(|(k, v)| Ok((K::from_view(k)?, V::from_view(v)?)))
137 .collect()
138 }
139}
140
141#[macro_export]
142macro_rules! impl_view {
143 ($($type:ty),*) => {
144 $(
145 impl View for $type {
146 type ViewType = Self;
147
148 fn to_view(&self) -> Self::ViewType {
149 *self
150 }
151
152 fn from_view(view: Self::ViewType) -> Result<Self, ViewError> {
153 Ok(view)
154 }
155 }
156 )*
157 };
158}
159
160impl_view!(bool, i8, i16, i32, i64, u8, u16, u32, u64);
161
162impl View for f32 {
163 type ViewType = Self;
164
165 fn to_view(&self) -> Self::ViewType {
166 *self
167 }
168
169 fn from_view(view: Self::ViewType) -> Result<Self, ViewError> {
170 if view.is_finite() {
171 Ok(view)
172 } else {
173 Err(ViewError::Float32NonFinite { value: view })
174 }
175 }
176}
177
178impl View for f64 {
179 type ViewType = Self;
180
181 fn to_view(&self) -> Self::ViewType {
182 *self
183 }
184
185 fn from_view(view: Self::ViewType) -> Result<Self, ViewError> {
186 if view.is_finite() {
187 Ok(view)
188 } else {
189 Err(ViewError::Float64NonFinite { value: view })
190 }
191 }
192}
193
194pub trait CreateView {
199 type CreateViewType: CandidType + Default;
200}
201
202pub trait UpdateView {
207 type UpdateViewType: CandidType + Default;
208
209 fn merge(&mut self, _: Self::UpdateViewType) -> Result<(), ViewError> {
211 Ok(())
212 }
213}
214
215impl<T> UpdateView for Option<T>
216where
217 T: UpdateView + Default,
218{
219 type UpdateViewType = Option<T::UpdateViewType>;
220
221 fn merge(&mut self, update: Self::UpdateViewType) -> Result<(), ViewError> {
222 match update {
223 None => {
224 *self = None;
226 }
227 Some(inner_update) => {
228 if let Some(inner_value) = self.as_mut() {
229 inner_value.merge(inner_update)?;
230 } else {
231 let mut new_value = T::default();
232 new_value.merge(inner_update)?;
233 *self = Some(new_value);
234 }
235 }
236 }
237
238 Ok(())
239 }
240}
241
242impl<T> UpdateView for Vec<T>
243where
244 T: UpdateView + Default,
245{
246 type UpdateViewType = Vec<ListPatch<T::UpdateViewType>>;
248
249 fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), ViewError> {
250 for patch in patches {
251 match patch {
252 ListPatch::Update { index, patch } => {
253 if let Some(elem) = self.get_mut(index) {
254 elem.merge(patch)?;
255 }
256 }
257 ListPatch::Insert { index, value } => {
258 let mut elem = T::default();
259 elem.merge(value)?;
260 let idx = index.min(self.len());
261 self.insert(idx, elem);
262 }
263 ListPatch::Push { value } => {
264 let mut elem = T::default();
265 elem.merge(value)?;
266 self.push(elem);
267 }
268 ListPatch::Overwrite { values } => {
269 self.clear();
270 self.reserve(values.len());
271
272 for value in values {
273 let mut elem = T::default();
274 elem.merge(value)?;
275 self.push(elem);
276 }
277 }
278 ListPatch::Remove { index } => {
279 if index < self.len() {
280 self.remove(index);
281 }
282 }
283 ListPatch::Clear => self.clear(),
284 }
285 }
286
287 Ok(())
288 }
289}
290
291impl<T, S> UpdateView for HashSet<T, S>
292where
293 T: UpdateView + Default + Eq + Hash,
294 S: BuildHasher + Default,
295{
296 type UpdateViewType = Vec<SetPatch<T::UpdateViewType>>;
297
298 fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), ViewError> {
299 for patch in patches {
300 match patch {
301 SetPatch::Insert(value) => {
302 let mut elem = T::default();
303 elem.merge(value)?;
304 self.insert(elem);
305 }
306 SetPatch::Remove(value) => {
307 let mut elem = T::default();
308 elem.merge(value)?;
309 self.remove(&elem);
310 }
311 SetPatch::Overwrite { values } => {
312 self.clear();
313
314 for value in values {
315 let mut elem = T::default();
316 elem.merge(value)?;
317 self.insert(elem);
318 }
319 }
320 SetPatch::Clear => self.clear(),
321 }
322 }
323
324 Ok(())
325 }
326}
327
328impl<K, V, S> UpdateView for HashMap<K, V, S>
329where
330 K: UpdateView + Default + Eq + Hash,
331 V: UpdateView + Default,
332 S: BuildHasher + Default,
333{
334 type UpdateViewType = Vec<MapPatch<K::UpdateViewType, V::UpdateViewType>>;
335
336 fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), ViewError> {
337 for patch in patches {
338 match patch {
339 MapPatch::Upsert { key, value } => {
340 let mut key_value = K::default();
341 key_value.merge(key)?;
342
343 match self.entry(key_value) {
344 HashMapEntry::Occupied(mut slot) => {
345 slot.get_mut().merge(value)?;
346 }
347 HashMapEntry::Vacant(slot) => {
348 let mut value_value = V::default();
349 value_value.merge(value)?;
350 slot.insert(value_value);
351 }
352 }
353 }
354 MapPatch::Remove { key } => {
355 let mut key_value = K::default();
356 key_value.merge(key)?;
357 self.remove(&key_value);
358 }
359 MapPatch::Overwrite { entries } => {
360 self.clear();
361 self.reserve(entries.len());
362
363 for (key, value) in entries {
364 let mut key_value = K::default();
365 key_value.merge(key)?;
366
367 let mut value_value = V::default();
368 value_value.merge(value)?;
369 self.insert(key_value, value_value);
370 }
371 }
372 MapPatch::Clear => self.clear(),
373 }
374 }
375
376 Ok(())
377 }
378}
379
380macro_rules! impl_update_view {
381 ($($type:ty),*) => {
382 $(
383 impl UpdateView for $type {
384 type UpdateViewType = Self;
385
386 fn merge(&mut self, update: Self::UpdateViewType) -> Result<(), ViewError> {
387 *self = update;
388 Ok(())
389 }
390 }
391 )*
392 };
393}
394
395impl_update_view!(bool, i8, i16, i32, i64, u8, u16, u32, u64, String);