dd_sds/
event.rs

1use crate::encoding::{Encoding, Utf8Encoding};
2use crate::path::Path;
3use crate::scanner::error::ScannerError;
4use crate::PathSegment;
5
6/// Any object that can be scanned by SDS needs to implement `Event`.
7/// You can think of an Event as a "JSON-like" object that has a nested map of values with String
8/// keys.
9pub trait Event: Sized {
10    /// The encoding used to calculate match indices. The actual data itself must always be UTF-8.
11    type Encoding: Encoding;
12
13    /// Recursively visit all strings contained in the object.
14    fn visit_event<'a>(
15        &'a mut self,
16        visitor: &mut impl EventVisitor<'a>,
17    ) -> Result<(), ScannerError>;
18
19    /// Visit the string at the specified path. The path is guaranteed to be valid, it will be a path
20    /// that was previously used in `visit_event'. This is used to replace redacted content.
21    /// `visit` returns a bool indicating if the string was mutated.
22    fn visit_string_mut(&mut self, path: &Path, visit: impl FnMut(&mut String) -> bool);
23}
24
25pub trait EventVisitor<'path> {
26    fn push_segment(&mut self, segment: PathSegment<'path>);
27    fn pop_segment(&mut self);
28    fn visit_string<'s>(
29        &'s mut self,
30        value: &str,
31    ) -> Result<VisitStringResult<'s, 'path>, ScannerError>;
32}
33
34pub struct VisitStringResult<'s, 'path> {
35    /// This will be true if `visit_string_mut` may be called in the future for the string that was just visited.
36    /// This is intended as a flag for performance optimization.
37    pub might_mutate: bool,
38    pub path: &'s Path<'path>,
39}
40
41impl Event for String {
42    type Encoding = Utf8Encoding;
43
44    fn visit_event<'path>(
45        &'path mut self,
46        visitor: &mut impl EventVisitor<'path>,
47    ) -> Result<(), ScannerError> {
48        visitor.visit_string(self).map(|_| {})
49    }
50
51    fn visit_string_mut(&mut self, _path: &Path, mut visit: impl FnMut(&mut String) -> bool) {
52        (visit)(self);
53    }
54}
55
56#[cfg(test)]
57pub(crate) mod test {
58    use crate::simple_event::SimpleEvent;
59
60    use super::*;
61
62    #[derive(Debug, PartialEq, Eq)]
63    enum VisitOp {
64        Push(PathSegment<'static>),
65        Pop,
66        Visit(String),
67    }
68
69    struct Visitor {
70        path: Path<'static>,
71        ops: Vec<VisitOp>,
72    }
73
74    impl<'path> EventVisitor<'path> for Visitor {
75        fn push_segment(&mut self, segment: PathSegment<'path>) {
76            self.ops.push(VisitOp::Push(segment.into_static()));
77        }
78
79        fn pop_segment(&mut self) {
80            self.ops.push(VisitOp::Pop);
81        }
82
83        fn visit_string<'s>(
84            &'s mut self,
85            value: &str,
86        ) -> Result<VisitStringResult<'s, 'path>, ScannerError> {
87            self.ops.push(VisitOp::Visit(value.to_string()));
88            Ok(VisitStringResult {
89                might_mutate: true,
90                path: &self.path,
91            })
92        }
93    }
94
95    #[test]
96    pub fn test_string_event() {
97        let value = "sdsisthebest";
98        let mut visitor = Visitor {
99            ops: vec![],
100            path: Path::root(),
101        };
102        value.to_string().visit_event(&mut visitor).unwrap();
103        assert_eq!(visitor.ops, vec![VisitOp::Visit(value.into()),]);
104    }
105
106    #[test]
107    pub fn test_simple_event() {
108        let mut event = SimpleEvent::Map(
109            [
110                (
111                    "key-a".to_string(),
112                    SimpleEvent::String("value-a".to_string()),
113                ),
114                (
115                    "key-b".to_string(),
116                    SimpleEvent::Map(
117                        [(
118                            "key-b-1".to_string(),
119                            SimpleEvent::String("value-b-1".to_string()),
120                        )]
121                        .into(),
122                    ),
123                ),
124            ]
125            .into(),
126        );
127
128        let mut visitor = Visitor {
129            ops: vec![],
130            path: Path::root(),
131        };
132        event.visit_event(&mut visitor).unwrap();
133
134        assert_eq!(
135            visitor.ops,
136            vec![
137                VisitOp::Push(PathSegment::Field("key-a".into())),
138                VisitOp::Visit("value-a".into()),
139                VisitOp::Pop,
140                VisitOp::Push(PathSegment::Field("key-b".into())),
141                VisitOp::Push(PathSegment::Field("key-b-1".into())),
142                VisitOp::Visit("value-b-1".into()),
143                VisitOp::Pop,
144                VisitOp::Pop,
145            ]
146        );
147    }
148}