Skip to main content

cumulo_dipa/
option.rs

1use crate::{CreatedDelta, Diffable, Patchable};
2use serde::de::DeserializeOwned;
3use serde::Serialize;
4use std::fmt::{Debug, Formatter};
5
6impl<'s, 'e, T: Diffable<'s, 'e, T>> Diffable<'s, 'e, Option<T>> for Option<T>
7where
8    T: 'e,
9    <T as Diffable<'s, 'e, T>>::Delta: Serialize,
10    <T as Diffable<'s, 'e, T>>::DeltaOwned: DeserializeOwned,
11{
12    type Delta = OptionDelta<'s, 'e, T>;
13    type DeltaOwned = OptionDeltaOwned<'s, 'e, T>;
14
15    fn create_delta_towards(&'s self, end_state: &'e Option<T>) -> CreatedDelta<Self::Delta> {
16        let diff = match (self, end_state) {
17            (None, None) => OptionDelta::NoChange,
18            (None, Some(new)) => OptionDelta::OuterChange(Some(new)),
19            (Some(_), None) => OptionDelta::OuterChange(None),
20
21            (Some(old), Some(new)) => {
22                let diff = old.create_delta_towards(new);
23
24                if diff.did_change {
25                    OptionDelta::InnerChange(diff.delta)
26                } else {
27                    OptionDelta::NoChange
28                }
29            }
30        };
31
32        let did_change = match &diff {
33            OptionDelta::NoChange => false,
34            _ => true,
35        };
36
37        CreatedDelta {
38            delta: diff,
39            did_change,
40        }
41    }
42}
43
44impl<'s, 'e, T> Patchable<<Option<T> as Diffable<'s, 'e, Option<T>>>::DeltaOwned> for Option<T>
45where
46    T: 'e,
47    T: Diffable<'s, 'e, T>,
48    T: Patchable<<T as Diffable<'s, 'e, T>>::DeltaOwned>,
49    <T as Diffable<'s, 'e, T>>::Delta: Serialize,
50    <T as Diffable<'s, 'e, T>>::DeltaOwned: DeserializeOwned,
51{
52    fn apply_patch(&mut self, patch: <Option<T> as Diffable<'s, 'e, Option<T>>>::DeltaOwned) {
53        match patch {
54            OptionDeltaOwned::NoChange => {}
55            OptionDeltaOwned::InnerChange(delta) => match self {
56                Some(inner) => inner.apply_patch(delta),
57                _ => panic!("No inner field to change"),
58            },
59            OptionDeltaOwned::OuterChange(outer) => {
60                *self = outer;
61            }
62        }
63    }
64}
65
66#[derive(Serialize)]
67#[allow(missing_docs)]
68pub enum OptionDelta<'s, 'e, T: Diffable<'s, 'e, T>>
69where
70    <T as Diffable<'s, 'e, T>>::Delta: Serialize,
71{
72    NoChange,
73    InnerChange(<T as Diffable<'s, 'e, T>>::Delta),
74    OuterChange(Option<&'e T>),
75}
76
77#[derive(Deserialize)]
78#[allow(missing_docs)]
79pub enum OptionDeltaOwned<'s, 'e, T: Diffable<'s, 'e, T>>
80where
81    <T as Diffable<'s, 'e, T>>::DeltaOwned: DeserializeOwned,
82{
83    NoChange,
84    InnerChange(<T as Diffable<'s, 'e, T>>::DeltaOwned),
85    OuterChange(Option<T>),
86}
87
88// Used by DipaImplTester
89impl<'s, 'e, T: Diffable<'s, 'e, T>> Debug for OptionDelta<'s, 'e, T>
90where
91    T: Debug,
92    <T as Diffable<'s, 'e, T>>::Delta: Serialize,
93    <T as Diffable<'s, 'e, T>>::Delta: Debug,
94{
95    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
96        match self {
97            OptionDelta::NoChange => {
98                f.write_str("NoChange")?;
99            }
100            OptionDelta::InnerChange(delta) => {
101                f.debug_tuple("InnerChange").field(delta).finish()?;
102            }
103            OptionDelta::OuterChange(outer) => {
104                f.debug_tuple("Outer").field(outer).finish()?;
105            }
106        };
107
108        Ok(())
109    }
110}
111
112// Used by DipaImplTester
113impl<'s, 'e, T: Diffable<'s, 'e, T>> PartialEq for OptionDelta<'s, 'e, T>
114where
115    T: PartialEq,
116    <T as Diffable<'s, 'e, T>>::Delta: Serialize,
117    <T as Diffable<'s, 'e, T>>::Delta: PartialEq,
118{
119    fn eq(&self, other: &Self) -> bool {
120        match (self, other) {
121            (OptionDelta::NoChange, OptionDelta::NoChange) => true,
122            (OptionDelta::InnerChange(left), OptionDelta::InnerChange(right)) => left.eq(right),
123            (OptionDelta::OuterChange(left), OptionDelta::OuterChange(right)) => left.eq(right),
124            _ => false,
125        }
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132    use crate::DipaImplTester;
133
134    /// Verify that we can diff/patch an Option<T>
135    #[test]
136    fn dipa_option_impl() {
137        DipaImplTester {
138            label: Some("Option<T>::None no change"),
139            start: &mut Option::<()>::None,
140            end: &None,
141            expected_delta: OptionDelta::NoChange,
142            expected_serialized_patch_size: 1,
143            expected_did_change: false,
144        }
145        .test();
146
147        DipaImplTester {
148            label: Some("Option<T>::Some no change"),
149            start: &mut Some(1u32),
150            end: &Some(1u32),
151            expected_delta: OptionDelta::NoChange,
152            expected_serialized_patch_size: 1,
153            expected_did_change: false,
154        }
155        .test();
156
157        DipaImplTester {
158            label: Some("Option<T>::Some change"),
159            start: &mut Some(1u32),
160            end: &Some(5u32),
161            expected_delta: OptionDelta::InnerChange(Some(5)),
162            expected_serialized_patch_size: 3,
163            expected_did_change: true,
164        }
165        .test();
166
167        DipaImplTester {
168            label: Some("Option<T> Some -> None"),
169            start: &mut Some(1u32),
170            end: &None,
171            expected_delta: OptionDelta::OuterChange(None),
172            expected_serialized_patch_size: 2,
173            expected_did_change: true,
174        }
175        .test();
176
177        DipaImplTester {
178            label: Some("Option<T> None -> Some"),
179            start: &mut None,
180            end: &Some(1u32),
181            expected_delta: OptionDelta::OuterChange(Some(&1u32)),
182            expected_serialized_patch_size: 3,
183            expected_did_change: true,
184        }
185        .test();
186    }
187}