Skip to main content

icydb_core/traits/
view.rs

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