diff --git a/sds/src/event.rs b/sds/src/event.rs index 6104c7d9..6f21ab5d 100644 --- a/sds/src/event.rs +++ b/sds/src/event.rs @@ -1,3 +1,7 @@ +use core::panic; +use std::collections::HashMap; +use std::hash::RandomState; + use crate::encoding::{Encoding, Utf8Encoding}; use crate::path::Path; use crate::PathSegment; @@ -43,9 +47,93 @@ impl Event for String { } } +impl Event for serde_json::Value { + type Encoding = Utf8Encoding; + + fn visit_event<'a>(&'a mut self, visitor: &mut impl EventVisitor<'a>) { + match self { + serde_json::Value::Null => {} + serde_json::Value::Bool(value) => { + let _result = visitor.visit_string(value.to_string().as_str()); + } + serde_json::Value::Number(number) => { + let _result = visitor.visit_string(number.to_string().as_str()); + } + serde_json::Value::String(s) => { + let _result = visitor.visit_string(&s); + } + serde_json::Value::Object(map) => { + for (k, child) in map.iter_mut() { + visitor.push_segment(k.as_str().into()); + child.visit_event(visitor); + visitor.pop_segment(); + } + } + serde_json::Value::Array(values) => { + for (i, value) in values.into_iter().enumerate() { + visitor.push_segment(PathSegment::Index(i)); + value.visit_event(visitor); + visitor.pop_segment(); + } + } + } + } + + fn visit_string_mut(&mut self, path: &Path, mut visit: impl FnMut(&mut String) -> bool) { + let mut value = self; + for segment in &path.segments { + match segment { + PathSegment::Field(key) => { + value = value + .as_object_mut() + .unwrap() + .get_mut(key.as_ref()) + .unwrap(); + } + PathSegment::Index(i) => { + value = value.as_array_mut().unwrap().get_mut(*i).unwrap(); + } + } + } + match value { + serde_json::Value::String(s) => { + (visit)(s); + } + _ => panic!("unknown value"), + }; + } +} + +impl Event for HashMap { + type Encoding = Utf8Encoding; + + fn visit_event<'a>(&'a mut self, visitor: &mut impl EventVisitor<'a>) { + for (k, v) in self.iter_mut() { + visitor.push_segment(PathSegment::Field(k.as_str().into())); + v.visit_event(visitor); + visitor.pop_segment(); + } + } + + fn visit_string_mut(&mut self, path: &Path, mut visit: impl FnMut(&mut String) -> bool) { + let first_segment = path.segments.first().unwrap(); + let mut remaining_segments = path.segments.clone(); + remaining_segments.remove(0); + match first_segment { + PathSegment::Field(field) => { + let value = self.get_mut(&field.to_string()).unwrap(); + value.visit_string_mut(&Path::from(remaining_segments), &mut visit); + } + _ => {} + } + } +} + #[cfg(test)] pub(crate) mod test { + use serde_json::{json, Map, Value}; + use crate::simple_event::SimpleEvent; use super::*; @@ -133,4 +221,60 @@ pub(crate) mod test { ] ); } + #[test] + pub fn test_hashmap_event() { + let mut map = Map::new(); + map.insert( + "key-a-1".to_string(), + Value::String("value-a-1".to_string()), + ); + map.insert( + "key-a-2".to_string(), + Value::String("value-b-1".to_string()), + ); + map.insert("key-a-3".to_string(), json!(["an", "array"])); + let mut event = HashMap::from([("key-a".to_string(), Value::Object(map))]); + + let mut visitor = Visitor { + ops: vec![], + path: Path::root(), + }; + event.visit_event(&mut visitor); + + assert_eq!( + visitor.ops, + vec![ + VisitOp::Push(PathSegment::Field("key-a".into())), + VisitOp::Push(PathSegment::Field("key-a-1".into())), + VisitOp::Visit("value-a-1".into()), + VisitOp::Pop, + VisitOp::Push(PathSegment::Field("key-a-2".into())), + VisitOp::Visit("value-b-1".into()), + VisitOp::Pop, + VisitOp::Push(PathSegment::Field("key-a-3".into())), + VisitOp::Push(PathSegment::Index(0)), + VisitOp::Visit("an".into()), + VisitOp::Pop, + VisitOp::Push(PathSegment::Index(1)), + VisitOp::Visit("array".into()), + VisitOp::Pop, + VisitOp::Pop, + VisitOp::Pop, + ] + ); + + let mut leaf = String::new(); + event.visit_string_mut( + &Path::from(vec![ + PathSegment::Field("key-a".into()), + PathSegment::Field("key-a-3".into()), + PathSegment::Index(1), + ]), + |s| { + leaf = s.clone(); + true + }, + ); + assert_eq!(leaf, "array".to_string()) + } }