dd_sds/
event.rs

1use crate::PathSegment;
2use crate::encoding::{Encoding, Utf8Encoding};
3use crate::path::Path;
4use crate::scanner::error::ScannerError;
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    fn get_id(&self) -> Option<&str> {
25        None
26    }
27}
28
29pub trait EventVisitor<'path> {
30    fn push_segment(&mut self, segment: PathSegment<'path>);
31    fn pop_segment(&mut self);
32    fn visit_string<'s>(
33        &'s mut self,
34        value: &str,
35    ) -> Result<VisitStringResult<'s, 'path>, ScannerError>;
36}
37
38pub struct VisitStringResult<'s, 'path> {
39    /// This will be true if `visit_string_mut` may be called in the future for the string that was just visited.
40    /// This is intended as a flag for performance optimization.
41    pub might_mutate: bool,
42    pub path: &'s Path<'path>,
43}
44
45impl Event for String {
46    type Encoding = Utf8Encoding;
47
48    fn visit_event<'path>(
49        &'path mut self,
50        visitor: &mut impl EventVisitor<'path>,
51    ) -> Result<(), ScannerError> {
52        visitor.visit_string(self).map(|_| {})
53    }
54
55    fn visit_string_mut(&mut self, _path: &Path, mut visit: impl FnMut(&mut String) -> bool) {
56        (visit)(self);
57    }
58}
59
60#[cfg(test)]
61pub(crate) mod test {
62    use crate::simple_event::SimpleEvent;
63
64    use super::*;
65
66    #[derive(Debug, PartialEq, Eq)]
67    enum VisitOp {
68        Push(PathSegment<'static>),
69        Pop,
70        Visit(String),
71    }
72
73    struct Visitor {
74        path: Path<'static>,
75        ops: Vec<VisitOp>,
76    }
77
78    impl<'path> EventVisitor<'path> for Visitor {
79        fn push_segment(&mut self, segment: PathSegment<'path>) {
80            self.ops.push(VisitOp::Push(segment.into_static()));
81        }
82
83        fn pop_segment(&mut self) {
84            self.ops.push(VisitOp::Pop);
85        }
86
87        fn visit_string<'s>(
88            &'s mut self,
89            value: &str,
90        ) -> Result<VisitStringResult<'s, 'path>, ScannerError> {
91            self.ops.push(VisitOp::Visit(value.to_string()));
92            Ok(VisitStringResult {
93                might_mutate: true,
94                path: &self.path,
95            })
96        }
97    }
98
99    #[test]
100    pub fn test_string_event() {
101        let value = "sdsisthebest";
102        let mut visitor = Visitor {
103            ops: vec![],
104            path: Path::root(),
105        };
106        value.to_string().visit_event(&mut visitor).unwrap();
107        assert_eq!(visitor.ops, vec![VisitOp::Visit(value.into()),]);
108    }
109
110    #[test]
111    pub fn test_simple_event() {
112        let mut event = SimpleEvent::Map(
113            [
114                (
115                    "key-a".to_string(),
116                    SimpleEvent::String("value-a".to_string()),
117                ),
118                (
119                    "key-b".to_string(),
120                    SimpleEvent::Map(
121                        [(
122                            "key-b-1".to_string(),
123                            SimpleEvent::String("value-b-1".to_string()),
124                        )]
125                        .into(),
126                    ),
127                ),
128            ]
129            .into(),
130        );
131
132        let mut visitor = Visitor {
133            ops: vec![],
134            path: Path::root(),
135        };
136        event.visit_event(&mut visitor).unwrap();
137
138        assert_eq!(
139            visitor.ops,
140            vec![
141                VisitOp::Push(PathSegment::Field("key-a".into())),
142                VisitOp::Visit("value-a".into()),
143                VisitOp::Pop,
144                VisitOp::Push(PathSegment::Field("key-b".into())),
145                VisitOp::Push(PathSegment::Field("key-b-1".into())),
146                VisitOp::Visit("value-b-1".into()),
147                VisitOp::Pop,
148                VisitOp::Pop,
149            ]
150        );
151    }
152}