dd_sds/
event_json.rs

1use core::panic;
2use std::collections::HashMap;
3use std::hash::RandomState;
4
5use crate::{Event, EventVisitor, Path, PathSegment, ScannerError, Utf8Encoding};
6
7impl Event for serde_json::Value {
8    type Encoding = Utf8Encoding;
9
10    fn visit_event<'a>(
11        &'a mut self,
12        visitor: &mut impl EventVisitor<'a>,
13    ) -> Result<(), ScannerError> {
14        match self {
15            serde_json::Value::Null => Ok(()),
16            serde_json::Value::Bool(value) => {
17                visitor.visit_string(value.to_string().as_str()).map(|_| {})
18            }
19            serde_json::Value::Number(number) => visitor
20                .visit_string(number.to_string().as_str())
21                .map(|_| {}),
22            serde_json::Value::String(s) => visitor.visit_string(s).map(|_| {}),
23            serde_json::Value::Object(map) => {
24                for (k, child) in map.iter_mut() {
25                    visitor.push_segment(k.as_str().into());
26                    child.visit_event(visitor)?;
27                    visitor.pop_segment();
28                }
29                Ok(())
30            }
31            serde_json::Value::Array(values) => {
32                for (i, value) in values.iter_mut().enumerate() {
33                    visitor.push_segment(PathSegment::Index(i));
34                    value.visit_event(visitor)?;
35                    visitor.pop_segment();
36                }
37                Ok(())
38            }
39        }
40    }
41
42    fn visit_string_mut(&mut self, path: &Path, mut visit: impl FnMut(&mut String) -> bool) {
43        let mut value = self;
44        for segment in &path.segments {
45            match segment {
46                PathSegment::Field(key) => {
47                    value = value
48                        .as_object_mut()
49                        .unwrap()
50                        .get_mut(key.as_ref())
51                        .unwrap();
52                }
53                PathSegment::Index(i) => {
54                    value = value.as_array_mut().unwrap().get_mut(*i).unwrap();
55                }
56            }
57        }
58        match value {
59            serde_json::Value::String(s) => {
60                (visit)(s);
61            }
62            _ => panic!("unknown value"),
63        };
64    }
65}
66
67impl Event for HashMap<String, serde_json::Value, RandomState> {
68    type Encoding = Utf8Encoding;
69
70    fn visit_event<'a>(
71        &'a mut self,
72        visitor: &mut impl EventVisitor<'a>,
73    ) -> Result<(), ScannerError> {
74        for (k, v) in self.iter_mut() {
75            visitor.push_segment(PathSegment::Field(k.as_str().into()));
76            v.visit_event(visitor)?;
77            visitor.pop_segment();
78        }
79        Ok(())
80    }
81
82    fn visit_string_mut(&mut self, path: &Path, mut visit: impl FnMut(&mut String) -> bool) {
83        let first_segment = path.segments.first().unwrap();
84        let mut remaining_segments = path.segments.clone();
85        remaining_segments.remove(0);
86        if let PathSegment::Field(field) = first_segment {
87            let value = self.get_mut(&field.to_string()).unwrap();
88            value.visit_string_mut(&Path::from(remaining_segments), &mut visit);
89        }
90    }
91}
92
93#[cfg(test)]
94mod test {
95    use std::collections::HashMap;
96
97    use serde_json::{Map, Value, json};
98
99    use crate::VisitStringResult;
100
101    use super::*;
102
103    #[derive(Debug, PartialEq, Eq)]
104    enum VisitOp {
105        Push(PathSegment<'static>),
106        Pop,
107        Visit(String),
108    }
109
110    struct Visitor {
111        path: Path<'static>,
112        ops: Vec<VisitOp>,
113    }
114
115    impl<'path> EventVisitor<'path> for Visitor {
116        fn push_segment(&mut self, segment: PathSegment<'path>) {
117            self.ops.push(VisitOp::Push(segment.into_static()));
118        }
119
120        fn pop_segment(&mut self) {
121            self.ops.push(VisitOp::Pop);
122        }
123
124        fn visit_string<'s>(
125            &'s mut self,
126            value: &str,
127        ) -> Result<VisitStringResult<'s, 'path>, ScannerError> {
128            self.ops.push(VisitOp::Visit(value.to_string()));
129            Ok(VisitStringResult {
130                might_mutate: true,
131                path: &self.path,
132            })
133        }
134    }
135
136    #[test]
137    pub fn test_hashmap_event() {
138        let mut map = Map::new();
139        map.insert(
140            "key-a-1".to_string(),
141            Value::String("value-a-1".to_string()),
142        );
143        map.insert(
144            "key-a-2".to_string(),
145            Value::String("value-b-1".to_string()),
146        );
147        map.insert("key-a-3".to_string(), json!(["an", "array"]));
148        let mut event = HashMap::from([("key-a".to_string(), Value::Object(map))]);
149
150        /*
151           event = {
152               "key-a": {
153                   "key-a-1": "value-a-1",
154                   "key-a-2": "value-a-2",
155                   "key-a-3": ["an", "array"]
156               }
157           }
158        */
159
160        let mut visitor = Visitor {
161            ops: vec![],
162            path: Path::root(),
163        };
164        event.visit_event(&mut visitor).unwrap();
165
166        assert_eq!(
167            visitor.ops,
168            vec![
169                VisitOp::Push(PathSegment::Field("key-a".into())),
170                VisitOp::Push(PathSegment::Field("key-a-1".into())),
171                VisitOp::Visit("value-a-1".into()),
172                VisitOp::Pop,
173                VisitOp::Push(PathSegment::Field("key-a-2".into())),
174                VisitOp::Visit("value-b-1".into()),
175                VisitOp::Pop,
176                VisitOp::Push(PathSegment::Field("key-a-3".into())),
177                VisitOp::Push(PathSegment::Index(0)),
178                VisitOp::Visit("an".into()),
179                VisitOp::Pop,
180                VisitOp::Push(PathSegment::Index(1)),
181                VisitOp::Visit("array".into()),
182                VisitOp::Pop,
183                VisitOp::Pop,
184                VisitOp::Pop,
185            ]
186        );
187
188        // I'm testing here that when I visit a string, giving the path will actually visit the right leaf of the tree.
189        let mut leaf = String::new();
190        event.visit_string_mut(
191            &Path::from(vec![
192                PathSegment::Field("key-a".into()),
193                PathSegment::Field("key-a-3".into()),
194                PathSegment::Index(1),
195            ]),
196            |s| {
197                leaf = s.clone();
198                true
199            },
200        );
201        // We're going to the "key-a.key-a-3.1" path
202        // This corresponds to the leaf "array" in the test event I created.
203        // Because "array" is the second element of the array, located at "key-a.key-a-3"
204        assert_eq!(leaf, "array".to_string())
205    }
206}