dd_sds/
event.rs

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