Skip to main content

icydb_core/traits/
view.rs

1use crate::{
2    patch::{
3        ListPatch, MapPatch, MergePatchError, SetPatch,
4        merge::{
5            merge_atomic, merge_btree_map, merge_btree_set, merge_hash_map, merge_hash_set,
6            merge_option, merge_vec,
7        },
8    },
9    traits::Atomic,
10};
11use candid::CandidType;
12use std::{
13    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
14    hash::{BuildHasher, Hash},
15};
16
17///
18/// AsView
19///
20/// Recursive for all field/value nodes
21/// `from_view` is infallible; view values are treated as canonical.
22///
23
24pub trait AsView: Sized {
25    type ViewType: Default;
26
27    fn as_view(&self) -> Self::ViewType;
28    fn from_view(view: Self::ViewType) -> Self;
29}
30
31impl AsView for () {
32    type ViewType = Self;
33
34    fn as_view(&self) -> Self::ViewType {}
35    fn from_view((): Self::ViewType) -> Self {}
36}
37
38impl AsView for String {
39    type ViewType = Self;
40
41    fn as_view(&self) -> Self::ViewType {
42        self.clone()
43    }
44
45    fn from_view(view: Self::ViewType) -> Self {
46        view
47    }
48}
49
50// Make Box<T> *not* appear in the view type
51impl<T: AsView> AsView for Box<T> {
52    type ViewType = T::ViewType;
53
54    fn as_view(&self) -> Self::ViewType {
55        // Delegate to inner value
56        T::as_view(self.as_ref())
57    }
58
59    fn from_view(view: Self::ViewType) -> Self {
60        // Re-box after reconstructing inner
61        Self::new(T::from_view(view))
62    }
63}
64
65impl<T: AsView> AsView for Option<T> {
66    type ViewType = Option<T::ViewType>;
67
68    fn as_view(&self) -> Self::ViewType {
69        self.as_ref().map(AsView::as_view)
70    }
71
72    fn from_view(view: Self::ViewType) -> Self {
73        view.map(T::from_view)
74    }
75}
76
77impl<T: AsView> AsView for Vec<T> {
78    type ViewType = Vec<T::ViewType>;
79
80    fn as_view(&self) -> Self::ViewType {
81        self.iter().map(AsView::as_view).collect()
82    }
83
84    fn from_view(view: Self::ViewType) -> Self {
85        view.into_iter().map(T::from_view).collect()
86    }
87}
88
89impl<T, S> AsView for HashSet<T, S>
90where
91    T: AsView + Eq + Hash + Clone,
92    S: BuildHasher + Default,
93{
94    type ViewType = Vec<T::ViewType>;
95
96    fn as_view(&self) -> Self::ViewType {
97        self.iter().map(AsView::as_view).collect()
98    }
99
100    fn from_view(view: Self::ViewType) -> Self {
101        view.into_iter().map(T::from_view).collect()
102    }
103}
104
105impl<K, V, S> AsView for HashMap<K, V, S>
106where
107    K: AsView + Eq + Hash + Clone,
108    V: AsView,
109    S: BuildHasher + Default,
110{
111    type ViewType = Vec<(K::ViewType, V::ViewType)>;
112
113    fn as_view(&self) -> Self::ViewType {
114        self.iter()
115            .map(|(k, v)| (k.as_view(), v.as_view()))
116            .collect()
117    }
118
119    fn from_view(view: Self::ViewType) -> Self {
120        view.into_iter()
121            .map(|(k, v)| (K::from_view(k), V::from_view(v)))
122            .collect()
123    }
124}
125
126impl<T> AsView for BTreeSet<T>
127where
128    T: AsView + Ord + Clone,
129{
130    type ViewType = Vec<T::ViewType>;
131
132    fn as_view(&self) -> Self::ViewType {
133        self.iter().map(AsView::as_view).collect()
134    }
135
136    fn from_view(view: Self::ViewType) -> Self {
137        view.into_iter().map(T::from_view).collect()
138    }
139}
140
141impl<K, V> AsView for BTreeMap<K, V>
142where
143    K: AsView + Ord + Clone,
144    V: AsView,
145{
146    type ViewType = Vec<(K::ViewType, V::ViewType)>;
147
148    fn as_view(&self) -> Self::ViewType {
149        self.iter()
150            .map(|(k, v)| (k.as_view(), v.as_view()))
151            .collect()
152    }
153
154    fn from_view(view: Self::ViewType) -> Self {
155        view.into_iter()
156            .map(|(k, v)| (K::from_view(k), V::from_view(v)))
157            .collect()
158    }
159}
160
161#[macro_export]
162macro_rules! impl_view {
163    ($($type:ty),*) => {
164        $(
165            impl AsView for $type {
166                type ViewType = Self;
167
168                fn as_view(&self) -> Self::ViewType {
169                    *self
170                }
171
172                fn from_view(view: Self::ViewType) -> Self {
173                    view
174                }
175            }
176        )*
177    };
178}
179
180impl_view!(bool, i8, i16, i32, i64, u8, u16, u32, u64);
181
182impl AsView for f32 {
183    type ViewType = Self;
184
185    fn as_view(&self) -> Self::ViewType {
186        *self
187    }
188
189    fn from_view(view: Self::ViewType) -> Self {
190        if view.is_finite() {
191            if view == 0.0 { 0.0 } else { view }
192        } else {
193            0.0
194        }
195    }
196}
197
198impl AsView for f64 {
199    type ViewType = Self;
200
201    fn as_view(&self) -> Self::ViewType {
202        *self
203    }
204
205    fn from_view(view: Self::ViewType) -> Self {
206        if view.is_finite() {
207            if view == 0.0 { 0.0 } else { view }
208        } else {
209            0.0
210        }
211    }
212}
213
214///
215/// CreateView
216///
217
218pub trait CreateView: AsView {
219    /// Payload accepted when creating this value.
220    ///
221    /// This is often equal to ViewType, but may differ
222    /// (e.g. Option<T>, defaults, omissions).
223    type CreateViewType: CandidType + Default;
224
225    fn from_create_view(view: Self::CreateViewType) -> Self;
226}
227
228///
229/// UpdateView
230///
231
232pub trait UpdateView: AsView {
233    /// A view payload that may be applied to `Self`.
234    type UpdateViewType: CandidType + Default;
235    fn merge(&mut self, patch: Self::UpdateViewType) -> Result<(), MergePatchError>;
236}
237
238impl<T> UpdateView for T
239where
240    T: Atomic + AsView + CandidType + Default,
241{
242    type UpdateViewType = Self;
243
244    fn merge(&mut self, patch: Self::UpdateViewType) -> Result<(), MergePatchError> {
245        merge_atomic(self, patch);
246
247        Ok(())
248    }
249}
250
251impl<T> UpdateView for Option<T>
252where
253    T: UpdateView + Default,
254{
255    type UpdateViewType = Option<T::UpdateViewType>;
256
257    fn merge(&mut self, patch: Self::UpdateViewType) -> Result<(), MergePatchError> {
258        merge_option(self, patch)
259    }
260}
261
262impl<T> UpdateView for Vec<T>
263where
264    T: UpdateView + Default,
265{
266    type UpdateViewType = Vec<ListPatch<T::UpdateViewType>>;
267
268    fn merge(&mut self, patch: Self::UpdateViewType) -> Result<(), MergePatchError> {
269        merge_vec(self, patch)
270    }
271}
272
273impl<T, S> UpdateView for HashSet<T, S>
274where
275    T: UpdateView + Clone + Default + Eq + Hash,
276    S: BuildHasher + Default,
277{
278    type UpdateViewType = Vec<SetPatch<T::UpdateViewType>>;
279
280    fn merge(&mut self, patch: Self::UpdateViewType) -> Result<(), MergePatchError> {
281        merge_hash_set(self, patch)
282    }
283}
284
285impl<K, V, S> UpdateView for HashMap<K, V, S>
286where
287    K: UpdateView + Clone + Default + Eq + Hash,
288    V: UpdateView + Default,
289    S: BuildHasher + Default,
290{
291    type UpdateViewType = Vec<MapPatch<K::UpdateViewType, V::UpdateViewType>>;
292
293    fn merge(&mut self, patch: Self::UpdateViewType) -> Result<(), MergePatchError> {
294        merge_hash_map(self, patch)
295    }
296}
297
298impl<T> UpdateView for BTreeSet<T>
299where
300    T: UpdateView + Clone + Default + Ord,
301{
302    type UpdateViewType = Vec<SetPatch<T::UpdateViewType>>;
303
304    fn merge(&mut self, patch: Self::UpdateViewType) -> Result<(), MergePatchError> {
305        merge_btree_set(self, patch)
306    }
307}
308
309impl<K, V> UpdateView for BTreeMap<K, V>
310where
311    K: UpdateView + Clone + Default + Ord,
312    V: UpdateView + Default,
313{
314    type UpdateViewType = Vec<MapPatch<K::UpdateViewType, V::UpdateViewType>>;
315
316    fn merge(&mut self, patch: Self::UpdateViewType) -> Result<(), MergePatchError> {
317        merge_btree_map(self, patch)
318    }
319}