Skip to main content

icydb_core/traits/
view.rs

1use crate::view::{ListPatch, MapPatch, SetPatch};
2use candid::CandidType;
3use std::{
4    collections::{
5        BTreeMap, BTreeSet, HashMap, HashSet, btree_map::Entry as BTreeMapEntry,
6        hash_map::Entry as HashMapEntry,
7    },
8    hash::{BuildHasher, Hash},
9    iter::IntoIterator,
10};
11
12///
13/// AsView
14///
15/// Recursive for all field/value nodes
16/// `from_view` is infallible; view values are treated as canonical.
17///
18
19pub trait AsView: Sized {
20    type ViewType: Default;
21
22    fn as_view(&self) -> Self::ViewType;
23    fn from_view(view: Self::ViewType) -> Self;
24}
25
26impl AsView for () {
27    type ViewType = Self;
28
29    fn as_view(&self) -> Self::ViewType {}
30
31    fn from_view((): Self::ViewType) -> Self {}
32}
33
34impl AsView for String {
35    type ViewType = Self;
36
37    fn as_view(&self) -> Self::ViewType {
38        self.clone()
39    }
40
41    fn from_view(view: Self::ViewType) -> Self {
42        view
43    }
44}
45
46// Make Box<T> *not* appear in the view type
47impl<T: AsView> AsView for Box<T> {
48    type ViewType = T::ViewType;
49
50    fn as_view(&self) -> Self::ViewType {
51        // Delegate to inner value
52        T::as_view(self.as_ref())
53    }
54
55    fn from_view(view: Self::ViewType) -> Self {
56        // Re-box after reconstructing inner
57        Self::new(T::from_view(view))
58    }
59}
60
61impl<T: AsView> AsView for Option<T> {
62    type ViewType = Option<T::ViewType>;
63
64    fn as_view(&self) -> Self::ViewType {
65        self.as_ref().map(AsView::as_view)
66    }
67
68    fn from_view(view: Self::ViewType) -> Self {
69        view.map(T::from_view)
70    }
71}
72
73impl<T: AsView> AsView for Vec<T> {
74    type ViewType = Vec<T::ViewType>;
75
76    fn as_view(&self) -> Self::ViewType {
77        self.iter().map(AsView::as_view).collect()
78    }
79
80    fn from_view(view: Self::ViewType) -> Self {
81        view.into_iter().map(T::from_view).collect()
82    }
83}
84
85impl<T, S> AsView for HashSet<T, S>
86where
87    T: AsView + Eq + Hash + Clone,
88    S: BuildHasher + Default,
89{
90    type ViewType = Vec<T::ViewType>;
91
92    fn as_view(&self) -> Self::ViewType {
93        self.iter().map(AsView::as_view).collect()
94    }
95
96    fn from_view(view: Self::ViewType) -> Self {
97        view.into_iter().map(T::from_view).collect()
98    }
99}
100
101impl<K, V, S> AsView for HashMap<K, V, S>
102where
103    K: AsView + Eq + Hash + Clone,
104    V: AsView,
105    S: BuildHasher + Default,
106{
107    type ViewType = Vec<(K::ViewType, V::ViewType)>;
108
109    fn as_view(&self) -> Self::ViewType {
110        self.iter()
111            .map(|(k, v)| (k.as_view(), v.as_view()))
112            .collect()
113    }
114
115    fn from_view(view: Self::ViewType) -> Self {
116        view.into_iter()
117            .map(|(k, v)| (K::from_view(k), V::from_view(v)))
118            .collect()
119    }
120}
121
122impl<T> AsView for BTreeSet<T>
123where
124    T: AsView + Ord + Clone,
125{
126    type ViewType = Vec<T::ViewType>;
127
128    fn as_view(&self) -> Self::ViewType {
129        self.iter().map(AsView::as_view).collect()
130    }
131
132    fn from_view(view: Self::ViewType) -> Self {
133        view.into_iter().map(T::from_view).collect()
134    }
135}
136
137impl<K, V> AsView for BTreeMap<K, V>
138where
139    K: AsView + Ord + Clone,
140    V: AsView,
141{
142    type ViewType = Vec<(K::ViewType, V::ViewType)>;
143
144    fn as_view(&self) -> Self::ViewType {
145        self.iter()
146            .map(|(k, v)| (k.as_view(), v.as_view()))
147            .collect()
148    }
149
150    fn from_view(view: Self::ViewType) -> Self {
151        view.into_iter()
152            .map(|(k, v)| (K::from_view(k), V::from_view(v)))
153            .collect()
154    }
155}
156
157#[macro_export]
158macro_rules! impl_view {
159    ($($type:ty),*) => {
160        $(
161            impl AsView for $type {
162                type ViewType = Self;
163
164                fn as_view(&self) -> Self::ViewType {
165                    *self
166                }
167
168                fn from_view(view: Self::ViewType) -> Self {
169                    view
170                }
171            }
172        )*
173    };
174}
175
176impl_view!(bool, i8, i16, i32, i64, u8, u16, u32, u64);
177
178impl AsView for f32 {
179    type ViewType = Self;
180
181    fn as_view(&self) -> Self::ViewType {
182        *self
183    }
184
185    fn from_view(view: Self::ViewType) -> Self {
186        if view.is_finite() {
187            if view == 0.0 { 0.0 } else { view }
188        } else {
189            0.0
190        }
191    }
192}
193
194impl AsView for f64 {
195    type ViewType = Self;
196
197    fn as_view(&self) -> Self::ViewType {
198        *self
199    }
200
201    fn from_view(view: Self::ViewType) -> Self {
202        if view.is_finite() {
203            if view == 0.0 { 0.0 } else { view }
204        } else {
205            0.0
206        }
207    }
208}
209
210///
211/// CreateView
212///
213
214pub trait CreateView: AsView {
215    /// Payload accepted when creating this value.
216    ///
217    /// This is often equal to ViewType, but may differ
218    /// (e.g. Option<T>, defaults, omissions).
219    type CreateViewType: CandidType + Default;
220
221    fn from_create_view(view: Self::CreateViewType) -> Self;
222}
223
224///
225/// UpdateView
226///
227
228pub trait UpdateView: AsView {
229    /// Payload accepted when updating this value.
230    type UpdateViewType: CandidType + Default;
231
232    /// Merge the update payload into self.
233    fn merge(&mut self, _update: Self::UpdateViewType) {}
234}
235
236impl<T> UpdateView for Option<T>
237where
238    T: UpdateView + Default,
239{
240    type UpdateViewType = Option<T::UpdateViewType>;
241
242    fn merge(&mut self, update: Self::UpdateViewType) {
243        match update {
244            None => {
245                // Field was provided (outer Some), inner None means explicit delete
246                *self = None;
247            }
248            Some(inner_update) => {
249                if let Some(inner_value) = self.as_mut() {
250                    inner_value.merge(inner_update);
251                } else {
252                    let mut new_value = T::default();
253                    new_value.merge(inner_update);
254                    *self = Some(new_value);
255                }
256            }
257        }
258    }
259}
260
261impl<T> UpdateView for Vec<T>
262where
263    T: UpdateView + Default,
264{
265    // Payload is T::UpdateViewType, which *is* CandidType
266    type UpdateViewType = Vec<ListPatch<T::UpdateViewType>>;
267
268    fn merge(&mut self, patches: Self::UpdateViewType) {
269        for patch in patches {
270            match patch {
271                ListPatch::Update { index, patch } => {
272                    if let Some(elem) = self.get_mut(index) {
273                        elem.merge(patch);
274                    }
275                }
276                ListPatch::Insert { index, value } => {
277                    let mut elem = T::default();
278                    elem.merge(value);
279                    let idx = index.min(self.len());
280                    self.insert(idx, elem);
281                }
282                ListPatch::Push { value } => {
283                    let mut elem = T::default();
284                    elem.merge(value);
285                    self.push(elem);
286                }
287                ListPatch::Overwrite { values } => {
288                    self.clear();
289                    self.reserve(values.len());
290
291                    for value in values {
292                        let mut elem = T::default();
293                        elem.merge(value);
294                        self.push(elem);
295                    }
296                }
297                ListPatch::Remove { index } => {
298                    if index < self.len() {
299                        self.remove(index);
300                    }
301                }
302                ListPatch::Clear => self.clear(),
303            }
304        }
305    }
306}
307
308impl<T, S> UpdateView for HashSet<T, S>
309where
310    T: UpdateView + Clone + Default + Eq + Hash,
311    S: BuildHasher + Default,
312{
313    type UpdateViewType = Vec<SetPatch<T::UpdateViewType>>;
314
315    fn merge(&mut self, patches: Self::UpdateViewType) {
316        for patch in patches {
317            match patch {
318                SetPatch::Insert(value) => {
319                    let mut elem = T::default();
320                    elem.merge(value);
321                    self.insert(elem);
322                }
323                SetPatch::Remove(value) => {
324                    let mut elem = T::default();
325                    elem.merge(value);
326                    self.remove(&elem);
327                }
328                SetPatch::Overwrite { values } => {
329                    self.clear();
330
331                    for value in values {
332                        let mut elem = T::default();
333                        elem.merge(value);
334                        self.insert(elem);
335                    }
336                }
337                SetPatch::Clear => self.clear(),
338            }
339        }
340    }
341}
342
343/// Internal representation used to normalize map patches before application.
344enum MapPatchOp<K, V> {
345    Insert { key: K, value: V },
346    Remove { key: K },
347    Replace { key: K, value: V },
348    Clear,
349}
350
351impl<K, V, S> UpdateView for HashMap<K, V, S>
352where
353    K: UpdateView + Clone + Default + Eq + Hash,
354    V: UpdateView + Default,
355    S: BuildHasher + Default,
356{
357    type UpdateViewType = Vec<MapPatch<K::UpdateViewType, V::UpdateViewType>>;
358
359    fn merge(&mut self, patches: Self::UpdateViewType) {
360        // Phase 1: decode patch payload into concrete keys.
361        let mut ops = Vec::with_capacity(patches.len());
362        for patch in patches {
363            match patch {
364                MapPatch::Insert { key, value } => {
365                    let mut key_value = K::default();
366                    key_value.merge(key);
367                    ops.push(MapPatchOp::Insert {
368                        key: key_value,
369                        value,
370                    });
371                }
372                MapPatch::Remove { key } => {
373                    let mut key_value = K::default();
374                    key_value.merge(key);
375                    ops.push(MapPatchOp::Remove { key: key_value });
376                }
377                MapPatch::Replace { key, value } => {
378                    let mut key_value = K::default();
379                    key_value.merge(key);
380                    ops.push(MapPatchOp::Replace {
381                        key: key_value,
382                        value,
383                    });
384                }
385                MapPatch::Clear => ops.push(MapPatchOp::Clear),
386            }
387        }
388
389        // Phase 2: reject ambiguous patch batches to keep semantics deterministic.
390        let mut saw_clear = false;
391        let mut touched = HashSet::with_capacity(ops.len());
392        for op in &ops {
393            match op {
394                MapPatchOp::Clear => {
395                    assert!(
396                        !saw_clear,
397                        "map patch batch contains duplicate Clear operations"
398                    );
399                    saw_clear = true;
400                    assert!(
401                        ops.len() == 1,
402                        "map patch batch cannot combine Clear with key operations"
403                    );
404                }
405                MapPatchOp::Insert { key, .. }
406                | MapPatchOp::Remove { key }
407                | MapPatchOp::Replace { key, .. } => {
408                    assert!(
409                        !saw_clear,
410                        "map patch batch cannot combine Clear with key operations"
411                    );
412                    assert!(
413                        touched.insert(key.clone()),
414                        "map patch batch contains duplicate key operations"
415                    );
416                }
417            }
418        }
419        if saw_clear {
420            self.clear();
421            return;
422        }
423
424        // Phase 3: apply deterministic map operations.
425        for op in ops {
426            match op {
427                MapPatchOp::Insert { key, value } => match self.entry(key) {
428                    HashMapEntry::Occupied(mut slot) => {
429                        slot.get_mut().merge(value);
430                    }
431                    HashMapEntry::Vacant(slot) => {
432                        let mut value_value = V::default();
433                        value_value.merge(value);
434                        slot.insert(value_value);
435                    }
436                },
437                MapPatchOp::Remove { key } => {
438                    assert!(
439                        self.remove(&key).is_some(),
440                        "map patch remove target missing"
441                    );
442                }
443                MapPatchOp::Replace { key, value } => match self.entry(key) {
444                    HashMapEntry::Occupied(mut slot) => {
445                        slot.get_mut().merge(value);
446                    }
447                    HashMapEntry::Vacant(_) => {
448                        panic!("map patch replace target missing");
449                    }
450                },
451                MapPatchOp::Clear => {
452                    unreachable!("map patch clear shape is handled before apply");
453                }
454            }
455        }
456    }
457}
458
459impl<T> UpdateView for BTreeSet<T>
460where
461    T: UpdateView + Clone + Default + Ord,
462{
463    type UpdateViewType = Vec<SetPatch<T::UpdateViewType>>;
464
465    fn merge(&mut self, patches: Self::UpdateViewType) {
466        for patch in patches {
467            match patch {
468                SetPatch::Insert(value) => {
469                    let mut elem = T::default();
470                    elem.merge(value);
471                    self.insert(elem);
472                }
473                SetPatch::Remove(value) => {
474                    let mut elem = T::default();
475                    elem.merge(value);
476                    self.remove(&elem);
477                }
478                SetPatch::Overwrite { values } => {
479                    self.clear();
480
481                    for value in values {
482                        let mut elem = T::default();
483                        elem.merge(value);
484                        self.insert(elem);
485                    }
486                }
487                SetPatch::Clear => self.clear(),
488            }
489        }
490    }
491}
492
493impl<K, V> UpdateView for BTreeMap<K, V>
494where
495    K: UpdateView + Clone + Default + Ord,
496    V: UpdateView + Default,
497{
498    type UpdateViewType = Vec<MapPatch<K::UpdateViewType, V::UpdateViewType>>;
499
500    fn merge(&mut self, patches: Self::UpdateViewType) {
501        // Phase 1: decode patch payload into concrete keys.
502        let mut ops = Vec::with_capacity(patches.len());
503        for patch in patches {
504            match patch {
505                MapPatch::Insert { key, value } => {
506                    let mut key_value = K::default();
507                    key_value.merge(key);
508                    ops.push(MapPatchOp::Insert {
509                        key: key_value,
510                        value,
511                    });
512                }
513                MapPatch::Remove { key } => {
514                    let mut key_value = K::default();
515                    key_value.merge(key);
516                    ops.push(MapPatchOp::Remove { key: key_value });
517                }
518                MapPatch::Replace { key, value } => {
519                    let mut key_value = K::default();
520                    key_value.merge(key);
521                    ops.push(MapPatchOp::Replace {
522                        key: key_value,
523                        value,
524                    });
525                }
526                MapPatch::Clear => ops.push(MapPatchOp::Clear),
527            }
528        }
529
530        // Phase 2: reject ambiguous patch batches to keep semantics deterministic.
531        let mut saw_clear = false;
532        let mut touched = BTreeSet::new();
533        for op in &ops {
534            match op {
535                MapPatchOp::Clear => {
536                    assert!(
537                        !saw_clear,
538                        "map patch batch contains duplicate Clear operations"
539                    );
540                    saw_clear = true;
541                    assert!(
542                        ops.len() == 1,
543                        "map patch batch cannot combine Clear with key operations"
544                    );
545                }
546                MapPatchOp::Insert { key, .. }
547                | MapPatchOp::Remove { key }
548                | MapPatchOp::Replace { key, .. } => {
549                    assert!(
550                        !saw_clear,
551                        "map patch batch cannot combine Clear with key operations"
552                    );
553                    assert!(
554                        touched.insert(key.clone()),
555                        "map patch batch contains duplicate key operations"
556                    );
557                }
558            }
559        }
560        if saw_clear {
561            self.clear();
562            return;
563        }
564
565        // Phase 3: apply deterministic map operations.
566        for op in ops {
567            match op {
568                MapPatchOp::Insert { key, value } => match self.entry(key) {
569                    BTreeMapEntry::Occupied(mut slot) => {
570                        slot.get_mut().merge(value);
571                    }
572                    BTreeMapEntry::Vacant(slot) => {
573                        let mut value_value = V::default();
574                        value_value.merge(value);
575                        slot.insert(value_value);
576                    }
577                },
578                MapPatchOp::Remove { key } => {
579                    assert!(
580                        self.remove(&key).is_some(),
581                        "map patch remove target missing"
582                    );
583                }
584                MapPatchOp::Replace { key, value } => match self.entry(key) {
585                    BTreeMapEntry::Occupied(mut slot) => {
586                        slot.get_mut().merge(value);
587                    }
588                    BTreeMapEntry::Vacant(_) => {
589                        panic!("map patch replace target missing");
590                    }
591                },
592                MapPatchOp::Clear => {
593                    unreachable!("map patch clear shape is handled before apply");
594                }
595            }
596        }
597    }
598}
599
600macro_rules! impl_update_view {
601    ($($type:ty),*) => {
602        $(
603            impl UpdateView for $type {
604                type UpdateViewType = Self;
605
606                fn merge(&mut self, update: Self::UpdateViewType) {
607                    *self = update;
608                }
609            }
610        )*
611    };
612}
613
614impl_update_view!(bool, i8, i16, i32, i64, u8, u16, u32, u64, String);