Skip to main content

wp_primitives/scope/
escaped.rs

1/// Evaluator for scopes with escape sequences (e.g., quoted strings)
2#[derive(Default)]
3pub struct EscapedScopeEval {
4    beg: char,
5    end: char,
6    esc_beg: char,
7    esc_end: char,
8}
9
10/// State machine for parsing scoped content with escape sequences
11enum ScopeParseState {
12    /// Waiting for the opening delimiter
13    Initial,
14    /// Parsing content inside the scope
15    Parsing,
16    /// Inside an escaped section (e.g., within quotes)
17    Escaped,
18}
19impl EscapedScopeEval {
20    #[inline(always)]
21    pub fn new(beg: char, end: char, esc_beg: char, esc_end: char) -> Self {
22        Self {
23            beg,
24            end,
25            esc_beg,
26            esc_end,
27        }
28    }
29    #[inline(always)]
30    pub fn len(&self, data: &str) -> usize {
31        let mut take_len = 0;
32        let mut mode = ScopeParseState::Initial;
33        let mut work_level = 0;
34        for c in data.chars() {
35            match mode {
36                ScopeParseState::Initial => {
37                    if c == self.beg {
38                        mode = ScopeParseState::Parsing;
39                        work_level += 1;
40                        take_len += 1;
41                        continue;
42                    } else {
43                        break;
44                    }
45                }
46                ScopeParseState::Parsing => {
47                    if c == self.end {
48                        take_len += 1;
49                        work_level -= 1;
50                        if work_level == 0 {
51                            break;
52                        }
53                        continue;
54                    }
55                    if c == self.beg {
56                        take_len += 1;
57                        work_level += 1;
58                        continue;
59                    }
60                    if c == self.esc_beg {
61                        mode = ScopeParseState::Escaped;
62                        take_len += 1;
63                        continue;
64                    }
65                    take_len += 1;
66                }
67                ScopeParseState::Escaped => {
68                    if c == self.esc_end {
69                        mode = ScopeParseState::Parsing;
70                        take_len += 1;
71                        continue;
72                    }
73                    take_len += 1;
74                }
75            }
76        }
77        take_len
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use crate::scope::EscapedScopeEval;
84
85    mod escaped_scope {
86        use super::*;
87
88        #[test]
89        fn valid_escaped_content() {
90            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
91            let data = r#"{ "a" : "} hello {" }"#;
92            let size = scope_rule.len(data);
93            assert_eq!(size, 21);
94        }
95
96        #[test]
97        fn valid_simple_scope() {
98            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
99            let data = r#"{ "a" : 123 }"#;
100            let size = scope_rule.len(data);
101            assert_eq!(size, 13);
102        }
103
104        #[test]
105        fn valid_first_scope_only() {
106            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
107            let data = r#"{ "a" : 123 } {"b" : 234 }"#;
108            let size = scope_rule.len(data);
109            assert_eq!(size, 13);
110        }
111
112        #[test]
113        fn invalid_leading_space() {
114            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
115            let data = r#" { "a" : 123 } {"b" : 234 }"#;
116            let size = scope_rule.len(data);
117            assert_eq!(size, 0);
118        }
119
120        #[test]
121        fn valid_deeply_nested() {
122            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
123            let data = r#"{ "a" : 123 , "b": { "x" : { "y" :1 }} }"#;
124            let size = scope_rule.len(data);
125            assert_eq!(size, 40);
126        }
127    }
128
129    mod edge_cases {
130        use super::*;
131
132        #[test]
133        fn empty_scope() {
134            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
135            let data = "{}";
136            let size = scope_rule.len(data);
137            assert_eq!(size, 2);
138        }
139
140        #[test]
141        fn empty_input() {
142            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
143            let data = "";
144            let size = scope_rule.len(data);
145            assert_eq!(size, 0);
146        }
147
148        #[test]
149        fn escaped_opening_delimiter() {
150            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
151            let data = r#"{ "key": "{value}" }"#;
152            let size = scope_rule.len(data);
153            assert_eq!(size, 20);
154        }
155
156        #[test]
157        fn escaped_closing_delimiter() {
158            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
159            let data = r#"{ "key": "val}ue" }"#;
160            let size = scope_rule.len(data);
161            assert_eq!(size, 19);
162        }
163
164        #[test]
165        fn multiple_escaped_sections() {
166            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
167            let data = r#"{ "a": "}" , "b": "{" , "c": "}" }"#;
168            let size = scope_rule.len(data);
169            assert_eq!(size, 34);
170        }
171
172        #[test]
173        fn unmatched_opening() {
174            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
175            let data = r#"{ "key": "value" "#;
176            let size = scope_rule.len(data);
177            // Current implementation continues until end of input
178            assert_eq!(
179                size, 17,
180                "Continues to end without finding closing delimiter"
181            );
182        }
183
184        #[test]
185        fn nested_with_escape() {
186            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
187            let data = r#"{ "outer": { "inner": "}" } }"#;
188            let size = scope_rule.len(data);
189            assert_eq!(size, 29);
190        }
191    }
192
193    mod different_escape_delimiters {
194        use super::*;
195
196        #[test]
197        fn single_quote_escape() {
198            let scope_rule = EscapedScopeEval::new('{', '}', '\'', '\'');
199            let data = r#"{ 'key': 'val}ue' }"#;
200            let size = scope_rule.len(data);
201            assert_eq!(size, 19);
202        }
203
204        #[test]
205        fn parentheses_with_string_escape() {
206            let scope_rule = EscapedScopeEval::new('(', ')', '"', '"');
207            let data = r#"(a ")" b)"#;
208            let size = scope_rule.len(data);
209            assert_eq!(size, 9);
210        }
211
212        #[test]
213        fn brackets_with_escape() {
214            let scope_rule = EscapedScopeEval::new('[', ']', '"', '"');
215            let data = r#"["item]", "other"]"#;
216            let size = scope_rule.len(data);
217            assert_eq!(size, 18);
218        }
219    }
220
221    mod boundary_conditions {
222        use super::*;
223
224        #[test]
225        fn only_escape_chars() {
226            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
227            let data = r#"{ "" }"#;
228            let size = scope_rule.len(data);
229            assert_eq!(size, 6);
230        }
231
232        #[test]
233        fn nested_escape_sections() {
234            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
235            let data = r#"{ "a": "\"nested\"" }"#;
236            let size = scope_rule.len(data);
237            // Note: This tests current behavior, might need different escape handling
238            assert_eq!(size, 21);
239        }
240
241        #[test]
242        fn whitespace_in_escaped() {
243            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
244            let data = r#"{ "  spaces  " }"#;
245            let size = scope_rule.len(data);
246            assert_eq!(size, 16);
247        }
248
249        #[test]
250        fn newlines_in_escaped() {
251            let scope_rule = EscapedScopeEval::new('{', '}', '"', '"');
252            let data = "{ \"line1\nline2\" }";
253            let size = scope_rule.len(data);
254            assert_eq!(size, 17);
255        }
256    }
257}