Skip to main content

icydb_core/traits/
view.rs

1use crate::view::{ListPatch, MapPatch, SetPatch};
2use candid::CandidType;
3use std::{
4    collections::{HashMap, HashSet, hash_map::Entry as HashMapEntry},
5    hash::{BuildHasher, Hash},
6    iter::IntoIterator,
7};
8
9///
10/// View
11/// Recursive for all field/value nodes
12/// `from_view` is infallible; view values are treated as canonical.
13///
14
15pub trait View: Sized {
16    type ViewType: Default;
17
18    fn to_view(&self) -> Self::ViewType;
19    fn from_view(view: Self::ViewType) -> Self;
20}
21
22impl View for () {
23    type ViewType = Self;
24
25    fn to_view(&self) -> Self::ViewType {}
26
27    fn from_view((): Self::ViewType) -> Self {}
28}
29
30impl View for String {
31    type ViewType = Self;
32
33    fn to_view(&self) -> Self::ViewType {
34        self.clone()
35    }
36
37    fn from_view(view: Self::ViewType) -> Self {
38        view
39    }
40}
41
42// Make Box<T> *not* appear in the view type
43impl<T: View> View for Box<T> {
44    type ViewType = T::ViewType;
45
46    fn to_view(&self) -> Self::ViewType {
47        // Delegate to inner value
48        T::to_view(self.as_ref())
49    }
50
51    fn from_view(view: Self::ViewType) -> Self {
52        // Re-box after reconstructing inner
53        Self::new(T::from_view(view))
54    }
55}
56
57impl<T: View> View for Option<T> {
58    type ViewType = Option<T::ViewType>;
59
60    fn to_view(&self) -> Self::ViewType {
61        self.as_ref().map(View::to_view)
62    }
63
64    fn from_view(view: Self::ViewType) -> Self {
65        view.map(T::from_view)
66    }
67}
68
69impl<T: View> View for Vec<T> {
70    type ViewType = Vec<T::ViewType>;
71
72    fn to_view(&self) -> Self::ViewType {
73        self.iter().map(View::to_view).collect()
74    }
75
76    fn from_view(view: Self::ViewType) -> Self {
77        view.into_iter().map(T::from_view).collect()
78    }
79}
80
81impl<T, S> View for HashSet<T, S>
82where
83    T: View + Eq + Hash + Clone,
84    S: BuildHasher + Default,
85{
86    type ViewType = Vec<T::ViewType>;
87
88    fn to_view(&self) -> Self::ViewType {
89        self.iter().map(View::to_view).collect()
90    }
91
92    fn from_view(view: Self::ViewType) -> Self {
93        view.into_iter().map(T::from_view).collect()
94    }
95}
96
97impl<K, V, S> View for HashMap<K, V, S>
98where
99    K: View + Eq + Hash + Clone,
100    V: View,
101    S: BuildHasher + Default,
102{
103    type ViewType = Vec<(K::ViewType, V::ViewType)>;
104
105    fn to_view(&self) -> Self::ViewType {
106        self.iter()
107            .map(|(k, v)| (k.to_view(), v.to_view()))
108            .collect()
109    }
110
111    fn from_view(view: Self::ViewType) -> Self {
112        view.into_iter()
113            .map(|(k, v)| (K::from_view(k), V::from_view(v)))
114            .collect()
115    }
116}
117
118#[macro_export]
119macro_rules! impl_view {
120    ($($type:ty),*) => {
121        $(
122            impl View for $type {
123                type ViewType = Self;
124
125                fn to_view(&self) -> Self::ViewType {
126                    *self
127                }
128
129                fn from_view(view: Self::ViewType) -> Self {
130                    view
131                }
132            }
133        )*
134    };
135}
136
137impl_view!(bool, i8, i16, i32, i64, u8, u16, u32, u64);
138
139impl View for f32 {
140    type ViewType = Self;
141
142    fn to_view(&self) -> Self::ViewType {
143        *self
144    }
145
146    fn from_view(view: Self::ViewType) -> Self {
147        if view.is_finite() {
148            if view == 0.0 { 0.0 } else { view }
149        } else {
150            0.0
151        }
152    }
153}
154
155impl View for f64 {
156    type ViewType = Self;
157
158    fn to_view(&self) -> Self::ViewType {
159        *self
160    }
161
162    fn from_view(view: Self::ViewType) -> Self {
163        if view.is_finite() {
164            if view == 0.0 { 0.0 } else { view }
165        } else {
166            0.0
167        }
168    }
169}
170
171///
172/// CreateView
173///
174
175pub trait CreateView {
176    type CreateViewType: CandidType + Default;
177}
178
179///
180/// UpdateView
181///
182
183pub trait UpdateView {
184    type UpdateViewType: CandidType + Default;
185
186    /// merge the updateview into self
187    fn merge(&mut self, _: Self::UpdateViewType) {}
188}
189
190impl<T> UpdateView for Option<T>
191where
192    T: UpdateView + Default,
193{
194    type UpdateViewType = Option<T::UpdateViewType>;
195
196    fn merge(&mut self, update: Self::UpdateViewType) {
197        match update {
198            None => {
199                // Field was provided (outer Some), inner None means explicit delete
200                *self = None;
201            }
202            Some(inner_update) => {
203                if let Some(inner_value) = self.as_mut() {
204                    inner_value.merge(inner_update);
205                } else {
206                    let mut new_value = T::default();
207                    new_value.merge(inner_update);
208                    *self = Some(new_value);
209                }
210            }
211        }
212    }
213}
214
215impl<T> UpdateView for Vec<T>
216where
217    T: UpdateView + Default,
218{
219    // Payload is T::UpdateViewType, which *is* CandidType
220    type UpdateViewType = Vec<ListPatch<T::UpdateViewType>>;
221
222    fn merge(&mut self, patches: Self::UpdateViewType) {
223        for patch in patches {
224            match patch {
225                ListPatch::Update { index, patch } => {
226                    if let Some(elem) = self.get_mut(index) {
227                        elem.merge(patch);
228                    }
229                }
230                ListPatch::Insert { index, value } => {
231                    let mut elem = T::default();
232                    elem.merge(value);
233                    let idx = index.min(self.len());
234                    self.insert(idx, elem);
235                }
236                ListPatch::Push { value } => {
237                    let mut elem = T::default();
238                    elem.merge(value);
239                    self.push(elem);
240                }
241                ListPatch::Overwrite { values } => {
242                    self.clear();
243                    self.reserve(values.len());
244
245                    for value in values {
246                        let mut elem = T::default();
247                        elem.merge(value);
248                        self.push(elem);
249                    }
250                }
251                ListPatch::Remove { index } => {
252                    if index < self.len() {
253                        self.remove(index);
254                    }
255                }
256                ListPatch::Clear => self.clear(),
257            }
258        }
259    }
260}
261
262impl<T, S> UpdateView for HashSet<T, S>
263where
264    T: UpdateView + Default + Eq + Hash,
265    S: BuildHasher + Default,
266{
267    type UpdateViewType = Vec<SetPatch<T::UpdateViewType>>;
268
269    fn merge(&mut self, patches: Self::UpdateViewType) {
270        for patch in patches {
271            match patch {
272                SetPatch::Insert(value) => {
273                    let mut elem = T::default();
274                    elem.merge(value);
275                    self.insert(elem);
276                }
277                SetPatch::Remove(value) => {
278                    let mut elem = T::default();
279                    elem.merge(value);
280                    self.remove(&elem);
281                }
282                SetPatch::Overwrite { values } => {
283                    self.clear();
284
285                    for value in values {
286                        let mut elem = T::default();
287                        elem.merge(value);
288                        self.insert(elem);
289                    }
290                }
291                SetPatch::Clear => self.clear(),
292            }
293        }
294    }
295}
296
297impl<K, V, S> UpdateView for HashMap<K, V, S>
298where
299    K: UpdateView + Default + Eq + Hash,
300    V: UpdateView + Default,
301    S: BuildHasher + Default,
302{
303    type UpdateViewType = Vec<MapPatch<K::UpdateViewType, V::UpdateViewType>>;
304
305    fn merge(&mut self, patches: Self::UpdateViewType) {
306        for patch in patches {
307            match patch {
308                MapPatch::Upsert { key, value } => {
309                    let mut key_value = K::default();
310                    key_value.merge(key);
311
312                    match self.entry(key_value) {
313                        HashMapEntry::Occupied(mut slot) => {
314                            slot.get_mut().merge(value);
315                        }
316                        HashMapEntry::Vacant(slot) => {
317                            let mut value_value = V::default();
318                            value_value.merge(value);
319                            slot.insert(value_value);
320                        }
321                    }
322                }
323                MapPatch::Remove { key } => {
324                    let mut key_value = K::default();
325                    key_value.merge(key);
326                    self.remove(&key_value);
327                }
328                MapPatch::Overwrite { entries } => {
329                    self.clear();
330                    self.reserve(entries.len());
331
332                    for (key, value) in entries {
333                        let mut key_value = K::default();
334                        key_value.merge(key);
335
336                        let mut value_value = V::default();
337                        value_value.merge(value);
338                        self.insert(key_value, value_value);
339                    }
340                }
341                MapPatch::Clear => self.clear(),
342            }
343        }
344    }
345}
346
347macro_rules! impl_update_view {
348    ($($type:ty),*) => {
349        $(
350            impl UpdateView for $type {
351                type UpdateViewType = Self;
352
353                fn merge(&mut self, update: Self::UpdateViewType) {
354                    *self = update;
355                }
356            }
357        )*
358    };
359}
360
361impl_update_view!(bool, i8, i16, i32, i64, u8, u16, u32, u64, String);