1use crate::error::{StringPatternError, ValidationError};
6use crate::ir;
7use fidl_fuchsia_diagnostics as fdiagnostics;
8
9pub trait ValidateExt {
12 fn validate(&self) -> Result<(), ValidationError>;
13}
14
15pub trait ValidateComponentSelectorExt {
16 fn validate(&self) -> Result<(), ValidationError>;
17}
18
19pub trait ValidateTreeSelectorExt {
20 fn validate(&self) -> Result<(), ValidationError>;
21}
22
23pub trait Selector {
24 type Component: ComponentSelector;
25 type Tree: TreeSelector;
26
27 fn component(&self) -> Option<&Self::Component>;
28 fn tree(&self) -> Option<&Self::Tree>;
29}
30
31pub trait ComponentSelector {
32 type Segment: StringSelector;
33 fn segments(&self) -> Option<&[Self::Segment]>;
34}
35
36pub trait TreeSelector {
37 type Segment: StringSelector;
38
39 fn node_path(&self) -> Option<&[Self::Segment]>;
40 fn property(&self) -> Option<&Self::Segment>;
41}
42
43pub trait StringSelector {
44 fn exact_match(&self) -> Option<&str>;
45 fn pattern(&self) -> Option<&str>;
46}
47
48impl<T: Selector> ValidateExt for T {
49 fn validate(&self) -> Result<(), ValidationError> {
50 match (self.component(), self.tree()) {
51 (Some(component), Some(tree)) => {
52 component.validate()?;
53 tree.validate()?;
54 }
55 (None, _) => return Err(ValidationError::MissingComponentSelector),
56 (_, None) => return Err(ValidationError::MissingTreeSelector),
57 }
58 Ok(())
59 }
60}
61
62impl<T: ComponentSelector> ValidateComponentSelectorExt for T {
63 fn validate(&self) -> Result<(), ValidationError> {
64 let segments = self.segments().unwrap_or(&[]);
65 if segments.is_empty() {
66 return Err(ValidationError::EmptyComponentSelector);
67 }
68 let last_idx = segments.len() - 1;
69 for segment in &segments[..last_idx] {
70 segment.validate(StringSelectorValidationOpts {
71 allow_empty: false,
72 ..Default::default()
73 })?;
74 }
75 segments[last_idx].validate(StringSelectorValidationOpts {
76 allow_recursive_glob: true,
77 ..Default::default()
78 })?;
79 Ok(())
80 }
81}
82
83impl<T: TreeSelector> ValidateTreeSelectorExt for T {
84 fn validate(&self) -> Result<(), ValidationError> {
85 let node_path = self.node_path().unwrap_or(&[]);
86 if node_path.is_empty() {
87 return Err(ValidationError::EmptySubtreeSelector);
88 }
89 for segment in node_path {
90 segment.validate(StringSelectorValidationOpts::default())?;
91 }
92 if let Some(segment) = self.property() {
93 segment.validate(StringSelectorValidationOpts::default())?;
94 }
95 Ok(())
96 }
97}
98
99#[derive(Default)]
100struct StringSelectorValidationOpts {
101 allow_recursive_glob: bool,
102 allow_empty: bool,
103}
104
105trait ValidateStringSelectorExt {
106 fn validate(&self, opts: StringSelectorValidationOpts) -> Result<(), ValidationError>;
107}
108
109impl<T: StringSelector> ValidateStringSelectorExt for T {
110 fn validate(&self, opts: StringSelectorValidationOpts) -> Result<(), ValidationError> {
111 match (self.exact_match(), self.pattern()) {
112 (None, None) | (Some(_), Some(_)) => Err(ValidationError::InvalidStringSelector),
113 (Some(exact_match), None) => {
114 if !opts.allow_empty && exact_match.is_empty() {
115 return Err(ValidationError::InvalidStringSelector);
116 }
117 Ok(())
118 }
119 (None, Some(pattern)) => {
120 if opts.allow_recursive_glob && pattern == "**" {
121 Ok(())
122 } else {
123 validate_pattern(pattern)
124 }
125 }
126 }
127 }
128}
129
130fn contains_unescaped(target: &str, forbidden: &str) -> bool {
133 if target.len() < forbidden.len() {
134 return false;
135 }
136 if target.starts_with(forbidden) {
137 return true;
138 }
139 let flen = forbidden.len();
140 for i in 1..(target.len() - flen + 1) {
141 if &target[i..i + flen] == forbidden && !target[i - 1..i + flen].starts_with("\\") {
142 return true;
143 }
144 }
145 false
146}
147
148fn validate_pattern(pattern: &str) -> Result<(), ValidationError> {
152 if pattern.is_empty() {
153 return Err(ValidationError::EmptyStringPattern);
154 }
155
156 let mut errors = vec![];
157 if contains_unescaped(pattern, "**") {
158 errors.push(StringPatternError::UnescapedGlob);
159 }
160 if !errors.is_empty() {
161 return Err(ValidationError::InvalidStringPattern(pattern.to_string(), errors));
162 }
163 Ok(())
164}
165
166impl Selector for fdiagnostics::Selector {
167 type Component = fdiagnostics::ComponentSelector;
168 type Tree = fdiagnostics::TreeSelector;
169
170 fn component(&self) -> Option<&Self::Component> {
171 self.component_selector.as_ref()
172 }
173
174 fn tree(&self) -> Option<&Self::Tree> {
175 self.tree_selector.as_ref()
176 }
177}
178
179impl ComponentSelector for fdiagnostics::ComponentSelector {
180 type Segment = fdiagnostics::StringSelector;
181
182 fn segments(&self) -> Option<&[Self::Segment]> {
183 self.moniker_segments.as_deref()
184 }
185}
186
187impl TreeSelector for fdiagnostics::TreeSelector {
188 type Segment = fdiagnostics::StringSelector;
189
190 fn node_path(&self) -> Option<&[Self::Segment]> {
191 match self {
192 Self::SubtreeSelector(t) => Some(&t.node_path[..]),
193 Self::PropertySelector(p) => Some(&p.node_path[..]),
194 fdiagnostics::TreeSelectorUnknown!() => None,
195 }
196 }
197
198 fn property(&self) -> Option<&Self::Segment> {
199 match self {
200 Self::SubtreeSelector(_) => None,
201 Self::PropertySelector(p) => Some(&p.target_properties),
202 fdiagnostics::TreeSelectorUnknown!() => None,
203 }
204 }
205}
206
207impl StringSelector for fdiagnostics::StringSelector {
208 fn exact_match(&self) -> Option<&str> {
209 match self {
210 Self::ExactMatch(s) => Some(s),
211 _ => None,
212 }
213 }
214
215 fn pattern(&self) -> Option<&str> {
216 match self {
217 Self::StringPattern(s) => Some(s),
218 _ => None,
219 }
220 }
221}
222
223impl<'a> Selector for ir::Selector<'a> {
224 type Component = ir::ComponentSelector<'a>;
225 type Tree = ir::TreeSelector<'a>;
226
227 fn component(&self) -> Option<&Self::Component> {
228 Some(&self.component)
229 }
230
231 fn tree(&self) -> Option<&Self::Tree> {
232 Some(&self.tree)
233 }
234}
235
236impl<'a> ComponentSelector for ir::ComponentSelector<'a> {
237 type Segment = ir::Segment<'a>;
238
239 fn segments(&self) -> Option<&[Self::Segment]> {
240 Some(&self.segments[..])
241 }
242}
243
244impl<'a> TreeSelector for ir::TreeSelector<'a> {
245 type Segment = ir::Segment<'a>;
246
247 fn node_path(&self) -> Option<&[Self::Segment]> {
248 Some(&self.node)
249 }
250
251 fn property(&self) -> Option<&Self::Segment> {
252 self.property.as_ref()
253 }
254}
255
256impl StringSelector for ir::Segment<'_> {
257 fn exact_match(&self) -> Option<&str> {
258 match self {
259 Self::ExactMatch(s) => Some(s),
260 _ => None,
261 }
262 }
263
264 fn pattern(&self) -> Option<&str> {
265 match self {
266 Self::Pattern(s) => Some(s),
267 _ => None,
268 }
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275 use std::sync::LazyLock;
276
277 static SHARED_PASSING_TEST_CASES: LazyLock<Vec<(Vec<&'static str>, &'static str)>> =
278 LazyLock::new(|| {
279 vec![
280 (vec![r#"abc"#, r#"def"#, r#"g"#], r#"bob"#),
281 (vec![r#"\**"#], r#"\**"#),
282 (vec![r#"\/"#], r#"\/"#),
283 (vec![r#"\:"#], r#"\:"#),
284 (vec![r#"asda\\\:"#], r#"a"#),
285 (vec![r#"asda*"#], r#"a"#),
286 ]
287 });
288 static SHARED_FAILING_TEST_CASES: LazyLock<Vec<(Vec<&'static str>, &'static str)>> =
289 LazyLock::new(|| {
290 vec![
291 (vec![r#"a**"#], r#"a"#),
294 (vec![r#"**a"#], r#"a"#),
297 (vec![], r#"bob"#),
300 ]
301 });
302
303 #[fuchsia::test]
304 fn tree_selector_validator_test() {
305 let unique_failing_test_cases = [
306 (vec![r#"a"#], r#"**"#),
309 ];
310
311 fn create_tree_selector(node_path: &[&str], property: &str) -> fdiagnostics::TreeSelector {
312 let node_path = node_path
313 .iter()
314 .map(|path_node_str| {
315 fdiagnostics::StringSelector::StringPattern(path_node_str.to_string())
316 })
317 .collect::<Vec<fdiagnostics::StringSelector>>();
318 let target_properties =
319 fdiagnostics::StringSelector::StringPattern(property.to_string());
320 fdiagnostics::TreeSelector::PropertySelector(fdiagnostics::PropertySelector {
321 node_path,
322 target_properties,
323 })
324 }
325
326 for (node_path, property) in SHARED_PASSING_TEST_CASES.iter() {
327 let tree_selector = create_tree_selector(node_path, property);
328 assert!(tree_selector.validate().is_ok());
329 }
330
331 for (node_path, property) in SHARED_FAILING_TEST_CASES.iter() {
332 let tree_selector = create_tree_selector(node_path, property);
333 assert!(
334 ValidateTreeSelectorExt::validate(&tree_selector).is_err(),
335 "Failed to validate tree selector: {:?}",
336 tree_selector
337 );
338 }
339
340 for (node_path, property) in unique_failing_test_cases.iter() {
341 let tree_selector = create_tree_selector(node_path, property);
342 assert!(
343 ValidateTreeSelectorExt::validate(&tree_selector).is_err(),
344 "Failed to validate tree selector: {:?}",
345 tree_selector
346 );
347 }
348 }
349
350 #[fuchsia::test]
351 fn component_selector_validator_test() {
352 fn create_component_selector(
353 component_moniker: &[&str],
354 ) -> fdiagnostics::ComponentSelector {
355 fdiagnostics::ComponentSelector {
356 moniker_segments: Some(
357 component_moniker
358 .iter()
359 .map(|path_node_str| {
360 fdiagnostics::StringSelector::StringPattern(path_node_str.to_string())
361 })
362 .collect::<Vec<fdiagnostics::StringSelector>>(),
363 ),
364 ..fdiagnostics::ComponentSelector::default()
365 }
366 }
367
368 for (component_moniker, _) in SHARED_PASSING_TEST_CASES.iter() {
369 let component_selector = create_component_selector(component_moniker);
370
371 assert!(component_selector.validate().is_ok());
372 }
373
374 for (component_moniker, _) in SHARED_FAILING_TEST_CASES.iter() {
375 let component_selector = create_component_selector(component_moniker);
376
377 assert!(
378 component_selector.validate().is_err(),
379 "Failed to validate component selector: {:?}",
380 component_selector
381 );
382 }
383 }
384}