k8_diff/json/
diff.rs

1use serde_json::Value;
2
3use super::JsonDiff;
4use super::PatchObject;
5use crate::Changes;
6use crate::Diff;
7use crate::DiffError;
8
9impl Changes for Value {
10    type Replace = Value;
11    type Patch = PatchObject;
12
13    fn diff(&self, new: &Self) -> Result<JsonDiff, DiffError> {
14        if *self == *new {
15            return Ok(Diff::None);
16        }
17        match self {
18            Value::Null => Ok(Diff::Replace(new.clone())),
19            _ => {
20                match new {
21                    Value::Null => Ok(Diff::Replace(Value::Null)),
22                    Value::Bool(ref _val) => Ok(Diff::Replace(new.clone())), // for now, we only support replace
23                    Value::Number(ref _val) => Ok(Diff::Replace(new.clone())),
24                    Value::String(ref _val) => Ok(Diff::Replace(new.clone())),
25                    Value::Array(ref _val) => Ok(Diff::Replace(new.clone())),
26                    Value::Object(ref new_val) => match self {
27                        Value::Object(ref old_val) => {
28                            let patch = PatchObject::diff(old_val, new_val)?;
29                            Ok(Diff::Patch(patch))
30                        }
31                        _ => Err(DiffError::DiffValue),
32                    },
33                }
34            }
35        }
36    }
37}
38
39#[cfg(test)]
40mod test {
41
42    use serde_json::json;
43    use serde_json::Value;
44
45    use super::Changes;
46
47    #[test]
48    fn test_null_comparision() {
49        let n1 = Value::Null;
50        let str1 = Value::String("test".to_owned());
51        let str2 = Value::String("test".to_owned());
52
53        assert!(n1.diff(&str1).expect("diff").is_replace());
54        assert!(str1.diff(&str2).expect("diff").is_none());
55    }
56
57    #[test]
58    fn test_object_comparision() {
59        let old_spec = json!({
60            "replicas": 2,
61            "apple": 5
62        });
63        let new_spec = json!({
64            "replicas": 3,
65            "apple": 5
66        });
67
68        let diff = old_spec.diff(&new_spec).expect("diff");
69        assert!(diff.is_patch());
70        let patch = diff.as_patch_ref().get_inner_ref();
71        assert_eq!(patch.len(), 1);
72        let diff_replicas = patch.get("replicas").unwrap();
73        assert!(diff_replicas.is_replace());
74        assert_eq!(*diff_replicas.as_replace_ref(), 3);
75    }
76
77    #[test]
78    #[allow(clippy::clippy::assertions_on_constants)]
79    fn test_replace_some_with_none() {
80        use serde::Serialize;
81        use serde_json::to_value;
82
83        use crate::Diff;
84
85        #[derive(Serialize)]
86        struct Test {
87            choice: Option<bool>,
88            value: u16,
89        }
90
91        let old_spec = to_value(Test {
92            choice: Some(true),
93            value: 5,
94        })
95        .expect("json");
96        let new_spec = to_value(Test {
97            choice: None,
98            value: 5,
99        })
100        .expect("json");
101
102        let diff = old_spec.diff(&new_spec).expect("diff");
103
104        assert!(diff.is_patch());
105
106        match diff {
107            Diff::Patch(p) => {
108                let json_diff = serde_json::to_value(p).expect("json");
109                println!("json diff: {:#?}", json_diff);
110                assert_eq!(json_diff, json!({ "choice": null }));
111            }
112            _ => assert!(false),
113        }
114    }
115}