Skip to main content

icydb_core/traits/
view.rs

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