Skip to main content

icydb_core/traits/
view.rs

1use crate::{
2    error::{ErrorClass, ErrorDetail, ErrorOrigin, InternalError},
3    view::{ListPatch, MapPatch, SetPatch},
4};
5use candid::CandidType;
6use std::{
7    collections::{
8        BTreeMap, BTreeSet, HashMap, HashSet, btree_map::Entry as BTreeMapEntry,
9        hash_map::Entry as HashMapEntry,
10    },
11    hash::{BuildHasher, Hash},
12    iter::IntoIterator,
13};
14use thiserror::Error as ThisError;
15
16///
17/// AsView
18///
19/// Recursive for all field/value nodes
20/// `from_view` is infallible; view values are treated as canonical.
21///
22
23pub trait AsView: Sized {
24    type ViewType: Default;
25
26    fn as_view(&self) -> Self::ViewType;
27    fn from_view(view: Self::ViewType) -> Self;
28}
29
30impl AsView for () {
31    type ViewType = Self;
32
33    fn as_view(&self) -> Self::ViewType {}
34
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/// ViewPatchError
230///
231/// Structured failures for user-driven patch application.
232///
233#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
234pub enum ViewPatchError {
235    #[error("invalid patch shape: expected {expected}, found {actual}")]
236    InvalidPatchShape {
237        expected: &'static str,
238        actual: &'static str,
239    },
240
241    #[error("missing key for map operation: {operation}")]
242    MissingKey { operation: &'static str },
243
244    #[error("invalid patch cardinality: expected {expected}, found {actual}")]
245    CardinalityViolation { expected: usize, actual: usize },
246
247    #[error("patch merge failed at {path}: {source}")]
248    Context {
249        path: String,
250        #[source]
251        source: Box<Self>,
252    },
253}
254
255impl ViewPatchError {
256    /// Prepend a field segment to the merge error path.
257    #[must_use]
258    pub fn with_field(self, field: impl AsRef<str>) -> Self {
259        self.with_path_segment(field.as_ref())
260    }
261
262    /// Prepend an index segment to the merge error path.
263    #[must_use]
264    pub fn with_index(self, index: usize) -> Self {
265        self.with_path_segment(format!("[{index}]"))
266    }
267
268    /// Return the full contextual path, if available.
269    #[must_use]
270    pub const fn path(&self) -> Option<&str> {
271        match self {
272            Self::Context { path, .. } => Some(path.as_str()),
273            _ => None,
274        }
275    }
276
277    /// Return the innermost, non-context merge error variant.
278    #[must_use]
279    pub fn leaf(&self) -> &Self {
280        match self {
281            Self::Context { source, .. } => source.leaf(),
282            _ => self,
283        }
284    }
285
286    #[must_use]
287    fn with_path_segment(self, segment: impl Into<String>) -> Self {
288        let segment = segment.into();
289        match self {
290            Self::Context { path, source } => Self::Context {
291                path: Self::join_segments(segment.as_str(), path.as_str()),
292                source,
293            },
294            source => Self::Context {
295                path: segment,
296                source: Box::new(source),
297            },
298        }
299    }
300
301    #[must_use]
302    fn join_segments(prefix: &str, suffix: &str) -> String {
303        if suffix.starts_with('[') {
304            format!("{prefix}{suffix}")
305        } else {
306            format!("{prefix}.{suffix}")
307        }
308    }
309}
310
311///
312/// Error
313///
314/// Stable merge error returned by `UpdateView::merge`.
315/// Preserves structured patch detail through `ErrorDetail::ViewPatch`.
316///
317pub type Error = InternalError;
318
319impl From<ViewPatchError> for Error {
320    fn from(err: ViewPatchError) -> Self {
321        let class = match err.leaf() {
322            ViewPatchError::MissingKey { .. } => ErrorClass::NotFound,
323            _ => ErrorClass::Unsupported,
324        };
325
326        Self {
327            class,
328            origin: ErrorOrigin::Interface,
329            message: err.to_string(),
330            detail: Some(ErrorDetail::ViewPatch(err)),
331        }
332    }
333}
334
335impl InternalError {
336    /// Prepend a field segment when the error contains view-patch detail.
337    #[must_use]
338    pub fn with_field(self, field: impl AsRef<str>) -> Self {
339        let field = field.as_ref().to_string();
340        self.with_view_patch_context(|err| err.with_field(field))
341    }
342
343    /// Prepend an index segment when the error contains view-patch detail.
344    #[must_use]
345    pub fn with_index(self, index: usize) -> Self {
346        self.with_view_patch_context(|err| err.with_index(index))
347    }
348
349    /// Return the contextual patch path when available.
350    #[must_use]
351    pub const fn path(&self) -> Option<&str> {
352        match &self.detail {
353            Some(ErrorDetail::ViewPatch(err)) => err.path(),
354            _ => None,
355        }
356    }
357
358    /// Return the innermost patch leaf when available.
359    #[must_use]
360    pub fn leaf(&self) -> Option<&ViewPatchError> {
361        match &self.detail {
362            Some(ErrorDetail::ViewPatch(err)) => Some(err.leaf()),
363            _ => None,
364        }
365    }
366
367    #[must_use]
368    fn with_view_patch_context(self, map: impl FnOnce(ViewPatchError) -> ViewPatchError) -> Self {
369        // Preserve non-patch errors as-is; only rewrite structured patch detail.
370        let Self {
371            class,
372            origin,
373            message,
374            detail,
375        } = self;
376
377        match detail {
378            Some(ErrorDetail::ViewPatch(err)) => Self::from(map(err)),
379            detail => Self {
380                class,
381                origin,
382                message,
383                detail,
384            },
385        }
386    }
387}
388
389///
390/// UpdateView
391///
392
393pub trait UpdateView: AsView {
394    /// Payload accepted when updating this value.
395    type UpdateViewType: CandidType + Default;
396
397    /// Merge the update payload into self.
398    fn merge(&mut self, _update: Self::UpdateViewType) -> Result<(), Error> {
399        Ok(())
400    }
401}
402
403impl<T> UpdateView for Option<T>
404where
405    T: UpdateView + Default,
406{
407    type UpdateViewType = Option<T::UpdateViewType>;
408
409    fn merge(&mut self, update: Self::UpdateViewType) -> Result<(), Error> {
410        match update {
411            None => {
412                // Field was provided (outer Some), inner None means explicit delete
413                *self = None;
414            }
415            Some(inner_update) => {
416                if let Some(inner_value) = self.as_mut() {
417                    inner_value
418                        .merge(inner_update)
419                        .map_err(|err| err.with_field("value"))?;
420                } else {
421                    let mut new_value = T::default();
422                    new_value
423                        .merge(inner_update)
424                        .map_err(|err| err.with_field("value"))?;
425                    *self = Some(new_value);
426                }
427            }
428        }
429
430        Ok(())
431    }
432}
433
434impl<T> UpdateView for Vec<T>
435where
436    T: UpdateView + Default,
437{
438    // Payload is T::UpdateViewType, which *is* CandidType
439    type UpdateViewType = Vec<ListPatch<T::UpdateViewType>>;
440
441    fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), Error> {
442        for patch in patches {
443            match patch {
444                ListPatch::Update { index, patch } => {
445                    if let Some(elem) = self.get_mut(index) {
446                        elem.merge(patch).map_err(|err| err.with_index(index))?;
447                    }
448                }
449                ListPatch::Insert { index, value } => {
450                    let idx = index.min(self.len());
451                    let mut elem = T::default();
452                    elem.merge(value).map_err(|err| err.with_index(idx))?;
453                    self.insert(idx, elem);
454                }
455                ListPatch::Push { value } => {
456                    let idx = self.len();
457                    let mut elem = T::default();
458                    elem.merge(value).map_err(|err| err.with_index(idx))?;
459                    self.push(elem);
460                }
461                ListPatch::Overwrite { values } => {
462                    self.clear();
463                    self.reserve(values.len());
464
465                    for (index, value) in values.into_iter().enumerate() {
466                        let mut elem = T::default();
467                        elem.merge(value).map_err(|err| err.with_index(index))?;
468                        self.push(elem);
469                    }
470                }
471                ListPatch::Remove { index } => {
472                    if index < self.len() {
473                        self.remove(index);
474                    }
475                }
476                ListPatch::Clear => self.clear(),
477            }
478        }
479
480        Ok(())
481    }
482}
483
484impl<T, S> UpdateView for HashSet<T, S>
485where
486    T: UpdateView + Clone + Default + Eq + Hash,
487    S: BuildHasher + Default,
488{
489    type UpdateViewType = Vec<SetPatch<T::UpdateViewType>>;
490
491    fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), Error> {
492        for patch in patches {
493            match patch {
494                SetPatch::Insert(value) => {
495                    let mut elem = T::default();
496                    elem.merge(value).map_err(|err| err.with_field("insert"))?;
497                    self.insert(elem);
498                }
499                SetPatch::Remove(value) => {
500                    let mut elem = T::default();
501                    elem.merge(value).map_err(|err| err.with_field("remove"))?;
502                    self.remove(&elem);
503                }
504                SetPatch::Overwrite { values } => {
505                    self.clear();
506
507                    for (index, value) in values.into_iter().enumerate() {
508                        let mut elem = T::default();
509                        elem.merge(value)
510                            .map_err(|err| err.with_field("overwrite").with_index(index))?;
511                        self.insert(elem);
512                    }
513                }
514                SetPatch::Clear => self.clear(),
515            }
516        }
517
518        Ok(())
519    }
520}
521
522/// Internal representation used to normalize map patches before application.
523enum MapPatchOp<K, V> {
524    Insert { key: K, value: V },
525    Remove { key: K },
526    Replace { key: K, value: V },
527    Clear,
528}
529
530impl<K, V, S> UpdateView for HashMap<K, V, S>
531where
532    K: UpdateView + Clone + Default + Eq + Hash,
533    V: UpdateView + Default,
534    S: BuildHasher + Default,
535{
536    type UpdateViewType = Vec<MapPatch<K::UpdateViewType, V::UpdateViewType>>;
537
538    #[expect(clippy::too_many_lines)]
539    fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), Error> {
540        // Phase 1: decode patch payload into concrete keys.
541        let mut ops = Vec::with_capacity(patches.len());
542        for patch in patches {
543            match patch {
544                MapPatch::Insert { key, value } => {
545                    let mut key_value = K::default();
546                    key_value
547                        .merge(key)
548                        .map_err(|err| err.with_field("insert").with_field("key"))?;
549                    ops.push(MapPatchOp::Insert {
550                        key: key_value,
551                        value,
552                    });
553                }
554                MapPatch::Remove { key } => {
555                    let mut key_value = K::default();
556                    key_value
557                        .merge(key)
558                        .map_err(|err| err.with_field("remove").with_field("key"))?;
559                    ops.push(MapPatchOp::Remove { key: key_value });
560                }
561                MapPatch::Replace { key, value } => {
562                    let mut key_value = K::default();
563                    key_value
564                        .merge(key)
565                        .map_err(|err| err.with_field("replace").with_field("key"))?;
566                    ops.push(MapPatchOp::Replace {
567                        key: key_value,
568                        value,
569                    });
570                }
571                MapPatch::Clear => ops.push(MapPatchOp::Clear),
572            }
573        }
574
575        // Phase 2: reject ambiguous patch batches to keep semantics deterministic.
576        let mut saw_clear = false;
577        let mut touched = HashSet::with_capacity(ops.len());
578        for op in &ops {
579            match op {
580                MapPatchOp::Clear => {
581                    if saw_clear {
582                        return Err(ViewPatchError::InvalidPatchShape {
583                            expected: "at most one Clear operation per map patch batch",
584                            actual: "duplicate Clear operations",
585                        }
586                        .into());
587                    }
588                    saw_clear = true;
589                    if ops.len() != 1 {
590                        return Err(ViewPatchError::CardinalityViolation {
591                            expected: 1,
592                            actual: ops.len(),
593                        }
594                        .into());
595                    }
596                }
597                MapPatchOp::Insert { key, .. }
598                | MapPatchOp::Remove { key }
599                | MapPatchOp::Replace { key, .. } => {
600                    if saw_clear {
601                        return Err(ViewPatchError::InvalidPatchShape {
602                            expected: "Clear must be the only operation in a map patch batch",
603                            actual: "Clear combined with key operation",
604                        }
605                        .into());
606                    }
607                    if !touched.insert(key.clone()) {
608                        return Err(ViewPatchError::InvalidPatchShape {
609                            expected: "unique key operations per map patch batch",
610                            actual: "duplicate key operation",
611                        }
612                        .into());
613                    }
614                }
615            }
616        }
617        if saw_clear {
618            self.clear();
619            return Ok(());
620        }
621
622        // Phase 3: apply deterministic map operations.
623        for op in ops {
624            match op {
625                MapPatchOp::Insert { key, value } => match self.entry(key) {
626                    HashMapEntry::Occupied(mut slot) => {
627                        slot.get_mut()
628                            .merge(value)
629                            .map_err(|err| err.with_field("insert").with_field("value"))?;
630                    }
631                    HashMapEntry::Vacant(slot) => {
632                        let mut value_value = V::default();
633                        value_value
634                            .merge(value)
635                            .map_err(|err| err.with_field("insert").with_field("value"))?;
636                        slot.insert(value_value);
637                    }
638                },
639                MapPatchOp::Remove { key } => {
640                    if self.remove(&key).is_none() {
641                        return Err(ViewPatchError::MissingKey {
642                            operation: "remove",
643                        }
644                        .into());
645                    }
646                }
647                MapPatchOp::Replace { key, value } => match self.entry(key) {
648                    HashMapEntry::Occupied(mut slot) => {
649                        slot.get_mut()
650                            .merge(value)
651                            .map_err(|err| err.with_field("replace").with_field("value"))?;
652                    }
653                    HashMapEntry::Vacant(_) => {
654                        return Err(ViewPatchError::MissingKey {
655                            operation: "replace",
656                        }
657                        .into());
658                    }
659                },
660                MapPatchOp::Clear => {
661                    return Err(ViewPatchError::InvalidPatchShape {
662                        expected: "Clear to be handled before apply phase",
663                        actual: "Clear reached apply phase",
664                    }
665                    .into());
666                }
667            }
668        }
669
670        Ok(())
671    }
672}
673
674impl<T> UpdateView for BTreeSet<T>
675where
676    T: UpdateView + Clone + Default + Ord,
677{
678    type UpdateViewType = Vec<SetPatch<T::UpdateViewType>>;
679
680    fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), Error> {
681        for patch in patches {
682            match patch {
683                SetPatch::Insert(value) => {
684                    let mut elem = T::default();
685                    elem.merge(value).map_err(|err| err.with_field("insert"))?;
686                    self.insert(elem);
687                }
688                SetPatch::Remove(value) => {
689                    let mut elem = T::default();
690                    elem.merge(value).map_err(|err| err.with_field("remove"))?;
691                    self.remove(&elem);
692                }
693                SetPatch::Overwrite { values } => {
694                    self.clear();
695
696                    for (index, value) in values.into_iter().enumerate() {
697                        let mut elem = T::default();
698                        elem.merge(value)
699                            .map_err(|err| err.with_field("overwrite").with_index(index))?;
700                        self.insert(elem);
701                    }
702                }
703                SetPatch::Clear => self.clear(),
704            }
705        }
706
707        Ok(())
708    }
709}
710
711impl<K, V> UpdateView for BTreeMap<K, V>
712where
713    K: UpdateView + Clone + Default + Ord,
714    V: UpdateView + Default,
715{
716    type UpdateViewType = Vec<MapPatch<K::UpdateViewType, V::UpdateViewType>>;
717
718    #[expect(clippy::too_many_lines)]
719    fn merge(&mut self, patches: Self::UpdateViewType) -> Result<(), Error> {
720        // Phase 1: decode patch payload into concrete keys.
721        let mut ops = Vec::with_capacity(patches.len());
722        for patch in patches {
723            match patch {
724                MapPatch::Insert { key, value } => {
725                    let mut key_value = K::default();
726                    key_value
727                        .merge(key)
728                        .map_err(|err| err.with_field("insert").with_field("key"))?;
729                    ops.push(MapPatchOp::Insert {
730                        key: key_value,
731                        value,
732                    });
733                }
734                MapPatch::Remove { key } => {
735                    let mut key_value = K::default();
736                    key_value
737                        .merge(key)
738                        .map_err(|err| err.with_field("remove").with_field("key"))?;
739                    ops.push(MapPatchOp::Remove { key: key_value });
740                }
741                MapPatch::Replace { key, value } => {
742                    let mut key_value = K::default();
743                    key_value
744                        .merge(key)
745                        .map_err(|err| err.with_field("replace").with_field("key"))?;
746                    ops.push(MapPatchOp::Replace {
747                        key: key_value,
748                        value,
749                    });
750                }
751                MapPatch::Clear => ops.push(MapPatchOp::Clear),
752            }
753        }
754
755        // Phase 2: reject ambiguous patch batches to keep semantics deterministic.
756        let mut saw_clear = false;
757        let mut touched = BTreeSet::new();
758        for op in &ops {
759            match op {
760                MapPatchOp::Clear => {
761                    if saw_clear {
762                        return Err(ViewPatchError::InvalidPatchShape {
763                            expected: "at most one Clear operation per map patch batch",
764                            actual: "duplicate Clear operations",
765                        }
766                        .into());
767                    }
768                    saw_clear = true;
769                    if ops.len() != 1 {
770                        return Err(ViewPatchError::CardinalityViolation {
771                            expected: 1,
772                            actual: ops.len(),
773                        }
774                        .into());
775                    }
776                }
777                MapPatchOp::Insert { key, .. }
778                | MapPatchOp::Remove { key }
779                | MapPatchOp::Replace { key, .. } => {
780                    if saw_clear {
781                        return Err(ViewPatchError::InvalidPatchShape {
782                            expected: "Clear must be the only operation in a map patch batch",
783                            actual: "Clear combined with key operation",
784                        }
785                        .into());
786                    }
787                    if !touched.insert(key.clone()) {
788                        return Err(ViewPatchError::InvalidPatchShape {
789                            expected: "unique key operations per map patch batch",
790                            actual: "duplicate key operation",
791                        }
792                        .into());
793                    }
794                }
795            }
796        }
797        if saw_clear {
798            self.clear();
799            return Ok(());
800        }
801
802        // Phase 3: apply deterministic map operations.
803        for op in ops {
804            match op {
805                MapPatchOp::Insert { key, value } => match self.entry(key) {
806                    BTreeMapEntry::Occupied(mut slot) => {
807                        slot.get_mut()
808                            .merge(value)
809                            .map_err(|err| err.with_field("insert").with_field("value"))?;
810                    }
811                    BTreeMapEntry::Vacant(slot) => {
812                        let mut value_value = V::default();
813                        value_value
814                            .merge(value)
815                            .map_err(|err| err.with_field("insert").with_field("value"))?;
816                        slot.insert(value_value);
817                    }
818                },
819                MapPatchOp::Remove { key } => {
820                    if self.remove(&key).is_none() {
821                        return Err(ViewPatchError::MissingKey {
822                            operation: "remove",
823                        }
824                        .into());
825                    }
826                }
827                MapPatchOp::Replace { key, value } => match self.entry(key) {
828                    BTreeMapEntry::Occupied(mut slot) => {
829                        slot.get_mut()
830                            .merge(value)
831                            .map_err(|err| err.with_field("replace").with_field("value"))?;
832                    }
833                    BTreeMapEntry::Vacant(_) => {
834                        return Err(ViewPatchError::MissingKey {
835                            operation: "replace",
836                        }
837                        .into());
838                    }
839                },
840                MapPatchOp::Clear => {
841                    return Err(ViewPatchError::InvalidPatchShape {
842                        expected: "Clear to be handled before apply phase",
843                        actual: "Clear reached apply phase",
844                    }
845                    .into());
846                }
847            }
848        }
849
850        Ok(())
851    }
852}
853
854macro_rules! impl_update_view {
855    ($($type:ty),*) => {
856        $(
857            impl UpdateView for $type {
858                type UpdateViewType = Self;
859
860                fn merge(
861                    &mut self,
862                    update: Self::UpdateViewType,
863                ) -> Result<(), Error> {
864                    *self = update;
865
866                    Ok(())
867                }
868            }
869        )*
870    };
871}
872
873impl_update_view!(bool, i8, i16, i32, i64, u8, u16, u32, u64, String);