selectors/
parser.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::error::ParseError;
6use crate::ir::*;
7use crate::validate::{ValidateComponentSelectorExt, ValidateExt, ValidateTreeSelectorExt};
8use bitflags::bitflags;
9use nom::branch::alt;
10use nom::bytes::complete::{escaped, tag, take_while};
11use nom::character::complete::{alphanumeric1, multispace0, none_of, one_of};
12use nom::combinator::{all_consuming, complete, cond, map, opt, peek, recognize, verify};
13use nom::error::{ErrorKind, ParseError as NomParseError};
14use nom::multi::{many1_count, separated_list1};
15use nom::sequence::{pair, preceded, separated_pair};
16use nom::{IResult, Parser};
17
18const ALL_TREE_NAMES_SELECTED_SYMBOL: &str = "...";
19
20bitflags! {
21    pub struct RequireEscaped: u8 {
22        const NONE = 0;
23        const COLONS = 1;
24        const WHITESPACE = 2;
25    }
26}
27
28/// Recognizes 0 or more spaces or tabs.
29fn whitespace0<'a, E>(input: &'a str) -> IResult<&'a str, &'a str, E>
30where
31    E: NomParseError<&'a str>,
32{
33    take_while(move |c| c == ' ' || c == '\t').parse(input)
34}
35
36/// Parses an input containing any number and type of whitespace at the front.
37fn spaced<'a, E, F, O>(parser: F) -> impl Parser<&'a str, Output = O, Error = E>
38where
39    F: Parser<&'a str, Output = O, Error = E>,
40    E: NomParseError<&'a str>,
41{
42    preceded(whitespace0, parser)
43}
44
45fn extract_conjoined_names<'a, E>(input: &'a str) -> IResult<&'a str, Option<&'a str>, E>
46where
47    E: NomParseError<&'a str>,
48{
49    let split = parse_quote_sensitive_separator(input, ']');
50    if split.len() == 1 { Ok((split[0], None)) } else { Ok((split[1], Some(&split[0][1..]))) }
51}
52
53fn extract_from_quotes(input: &str) -> &str {
54    if input.starts_with('"') && input.len() > 1 { &input[1..input.len() - 1] } else { input }
55}
56
57fn parse_quote_sensitive_separator(input: &str, sep: char) -> Vec<&str> {
58    let mut inside_quotes = false;
59    let mut last_split_index = 0;
60    let mut chars = input.chars().enumerate().peekable();
61    let mut result = vec![];
62
63    let mut push_substr = |curr_index| {
64        result.push(input[last_split_index..curr_index].trim_start());
65        last_split_index = curr_index + 1;
66    };
67
68    while let (Some((_, c1)), Some((idx2, c2))) = (chars.next(), chars.peek()) {
69        match (c1, c2) {
70            ('\\', '"') => {
71                chars.next();
72            }
73            ('"', c) if *c == sep && inside_quotes => {
74                inside_quotes = !inside_quotes;
75                push_substr(*idx2);
76            }
77            ('"', _) => {
78                inside_quotes = !inside_quotes;
79            }
80            (_, c) if *c == sep && !inside_quotes => {
81                push_substr(*idx2);
82            }
83            _ => {}
84        }
85    }
86
87    // if a split is produced by a trailing comma then last_split_index is input.len() + 1
88    if last_split_index < input.len() {
89        let maybe_last_segment = input[last_split_index..].trim_start();
90        if !maybe_last_segment.is_empty() {
91            result.push(maybe_last_segment);
92        }
93    }
94
95    result
96}
97
98/// Returns the parser for a tree selector, which is a node selector and an optional property selector.
99fn tree_selector<'a, E>(
100    // this can't be determined from the input to the resulting parser, because this function is
101    // used on both whole selector strings and strings that are only tree selectors
102    required_escapes: RequireEscaped,
103) -> impl Parser<&'a str, Output = TreeSelector<'a>, Error = E>
104where
105    E: NomParseError<&'a str>,
106{
107    move |input| {
108        let mut esc = if required_escapes.intersects(RequireEscaped::WHITESPACE) {
109            escaped(none_of(":/\\ \t\n"), '\\', one_of("* \t/:\\"))
110        } else {
111            escaped(none_of(":/\\\t\n"), '\\', one_of("* \t/:\\"))
112        };
113
114        let (rest, unparsed_name_list) =
115            extract_conjoined_names::<E>(input).unwrap_or((input, None));
116
117        let tree_names = if unparsed_name_list == Some(ALL_TREE_NAMES_SELECTED_SYMBOL) {
118            Some(TreeNames::All)
119        } else {
120            // because of strict requirements around using quotation marks and the context of
121            // list brackets, not that much stuff needs to be escaped. Note that `"` is both
122            // an allowed normal character and an escaped character because at the time this
123            // parser is applied, wrapper quotes have not been stripped
124            let mut name_escapes = escaped(none_of(r#"*\"#), '\\', one_of(r#""*"#));
125            unparsed_name_list
126                .map(|names: &str| {
127                    parse_quote_sensitive_separator(names, ',')
128                        .into_iter()
129                        .map(|name| {
130                            let (_, (_, value)) =
131                                spaced(separated_pair(tag("name"), tag("="), &mut name_escapes))
132                                    .parse(name)?;
133                            Ok(extract_from_quotes(value))
134                        })
135                        .collect::<Result<Vec<_>, _>>()
136                })
137                .transpose()?
138                .map(|value| value.into())
139        };
140
141        let (rest, node_segments) = separated_list1(tag("/"), &mut esc).parse(rest)?;
142        let (rest, property_segment) = if peek(tag::<_, _, E>(":")).parse(rest).is_ok() {
143            let (rest, _) = tag(":").parse(rest)?;
144            let (rest, property) = verify(esc, |value: &str| !value.is_empty()).parse(rest)?;
145            (rest, Some(property))
146        } else {
147            (rest, None)
148        };
149        Ok((
150            rest,
151            TreeSelector {
152                node: node_segments.into_iter().map(|value| value.into()).collect(),
153                property: property_segment.map(|value| value.into()),
154                tree_names,
155            },
156        ))
157    }
158}
159
160/// Returns the parser for a component selector. The parser accepts unescaped depending on the
161/// the argument `escape_colons`.
162fn component_selector<'a, E>(
163    required_escapes: RequireEscaped,
164) -> impl Parser<&'a str, Output = ComponentSelector<'a>, Error = E>
165where
166    E: NomParseError<&'a str>,
167{
168    fn inner_component_selector<'a, F, E>(
169        segment: F,
170        input: &'a str,
171    ) -> IResult<&'a str, ComponentSelector<'a>, E>
172    where
173        F: Parser<&'a str, Output = &'a str, Error = E>,
174        E: NomParseError<&'a str>,
175    {
176        // Monikers (the first part of a selector) can optionally be preceded by "/" or "./".
177        let (rest, segments) =
178            preceded(opt(alt((tag("./"), tag("/")))), separated_list1(tag("/"), segment))
179                .parse(input)?;
180        Ok((
181            rest,
182            ComponentSelector { segments: segments.into_iter().map(Segment::from).collect() },
183        ))
184    }
185
186    move |input| {
187        if required_escapes.intersects(RequireEscaped::COLONS) {
188            inner_component_selector(
189                recognize(escaped(
190                    alt((
191                        alphanumeric1,
192                        tag("*"),
193                        tag("."),
194                        tag("-"),
195                        tag("_"),
196                        tag(">"),
197                        tag("<"),
198                    )),
199                    '\\',
200                    tag(":"),
201                )),
202                input,
203            )
204        } else {
205            inner_component_selector(
206                recognize(many1_count(alt((
207                    alphanumeric1,
208                    tag("*"),
209                    tag("."),
210                    tag("-"),
211                    tag("_"),
212                    tag(">"),
213                    tag("<"),
214                    tag(":"),
215                )))),
216                input,
217            )
218        }
219    }
220}
221
222/// A comment allowed in selector files.
223fn comment<'a, E>(input: &'a str) -> IResult<&'a str, &'a str, E>
224where
225    E: NomParseError<&'a str>,
226{
227    let (rest, comment) =
228        spaced(preceded(tag("//"), take_while(|c| c != '\n' && c != '\r'))).parse(input)?;
229    if !rest.is_empty() {
230        let (rest, _) = one_of("\n\r").parse(rest)?; // consume the newline character
231        return Ok((rest, comment));
232    }
233    Ok((rest, comment))
234}
235
236/// Parses a core selector (component + tree + property). It accepts both raw selectors or
237/// selectors wrapped in double quotes. Selectors wrapped in quotes accept spaces in the tree and
238/// property names and require internal quotes to be escaped.
239fn core_selector<'a, E>(
240    input: &'a str,
241) -> IResult<&'a str, (ComponentSelector<'a>, TreeSelector<'a>), E>
242where
243    E: NomParseError<&'a str>,
244{
245    let required_tree_escape =
246        if input.starts_with('"') { RequireEscaped::empty() } else { RequireEscaped::WHITESPACE };
247    let (rest, (component, _, tree)) =
248        (component_selector(RequireEscaped::COLONS), tag(":"), tree_selector(required_tree_escape))
249            .parse(extract_from_quotes(input))?;
250    Ok((rest, (component, tree)))
251}
252
253/// Recognizes selectors, with comments allowed or disallowed.
254fn do_parse_selector<'a, E>(
255    allow_inline_comment: bool,
256) -> impl Parser<&'a str, Output = Selector<'a>, Error = E>
257where
258    E: NomParseError<&'a str>,
259{
260    map(
261        (spaced(core_selector), cond(allow_inline_comment, opt(comment)), multispace0),
262        move |((component, tree), _, _)| Selector { component, tree },
263    )
264}
265
266/// A fast efficient error that won't provide much information besides the name kind of nom parsers
267/// that failed and the position at which it failed.
268pub struct FastError;
269
270/// A slower but more user friendly error that will provide information about the chain of parsers
271/// that found the error and some context.
272pub struct VerboseError;
273
274mod private {
275    pub trait Sealed {}
276
277    impl Sealed for super::FastError {}
278    impl Sealed for super::VerboseError {}
279}
280
281/// Implemented by types which can be used to specify the error strategy the parsers should use.
282pub trait ParsingError<'a>: private::Sealed {
283    type Internal: NomParseError<&'a str>;
284
285    fn to_error(input: &str, err: Self::Internal) -> ParseError;
286}
287
288impl<'a> ParsingError<'a> for FastError {
289    type Internal = (&'a str, ErrorKind);
290
291    fn to_error(_: &str, (part, error_kind): Self::Internal) -> ParseError {
292        ParseError::Fast(part.to_owned(), error_kind)
293    }
294}
295
296impl<'a> ParsingError<'a> for VerboseError {
297    type Internal = nom_language::error::VerboseError<&'a str>;
298
299    fn to_error(input: &str, err: Self::Internal) -> ParseError {
300        ParseError::Verbose(nom_language::error::convert_error(input, err))
301    }
302}
303
304/// Parses the input into a `Selector`.
305pub fn selector<'a, E>(input: &'a str) -> Result<Selector<'a>, ParseError>
306where
307    E: ParsingError<'a>,
308{
309    let result = complete(all_consuming(do_parse_selector::<<E as ParsingError<'_>>::Internal>(
310        /*allow_inline_comment=*/ false,
311    )))
312    .parse(input);
313    match result {
314        Ok((_, selector)) => {
315            selector.validate()?;
316            Ok(selector)
317        }
318        Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(E::to_error(input, e)),
319        _ => unreachable!("through the complete combinator we get rid of Incomplete"),
320    }
321}
322
323/// Parses the input into a `TreeSelector` ignoring any whitespace around the component
324/// selector.
325pub fn standalone_tree_selector<'a, E>(input: &'a str) -> Result<TreeSelector<'a>, ParseError>
326where
327    E: ParsingError<'a>,
328{
329    let required_tree_escape =
330        if input.starts_with('"') { RequireEscaped::empty() } else { RequireEscaped::WHITESPACE };
331    let result = nom::combinator::all_consuming(pair(
332        spaced(tree_selector(required_tree_escape)),
333        multispace0,
334    ))
335    .parse(extract_from_quotes(input));
336    match result {
337        Ok((_, (tree_selector, _))) => {
338            tree_selector.validate()?;
339            Ok(tree_selector)
340        }
341        Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(E::to_error(input, e)),
342        _ => unreachable!("through the complete combinator we get rid of Incomplete"),
343    }
344}
345
346/// Parses the input into a `ComponentSelector` ignoring any whitespace around the component
347/// selector.
348pub fn consuming_component_selector<'a, E>(
349    input: &'a str,
350    required_escapes: RequireEscaped,
351) -> Result<ComponentSelector<'a>, ParseError>
352where
353    E: ParsingError<'a>,
354{
355    let result = nom::combinator::all_consuming(pair(
356        spaced(component_selector(required_escapes)),
357        multispace0,
358    ))
359    .parse(input);
360    match result {
361        Ok((_, (component_selector, _))) => {
362            component_selector.validate()?;
363            Ok(component_selector)
364        }
365        Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(E::to_error(input, e)),
366        _ => unreachable!("through the complete combinator we get rid of Incomplete"),
367    }
368}
369
370/// Parses the given input line into a Selector or None.
371pub fn selector_or_comment<'a, E>(input: &'a str) -> Result<Option<Selector<'a>>, ParseError>
372where
373    E: ParsingError<'a>,
374{
375    let result = complete(all_consuming(alt((
376        map(comment, |_| None),
377        map(
378            do_parse_selector::<<E as ParsingError<'_>>::Internal>(
379                /*allow_inline_comment=*/ true,
380            ),
381            Some,
382        ),
383    ))))
384    .parse(input);
385    match result {
386        Ok((_, maybe_selector)) => match maybe_selector {
387            None => Ok(None),
388            Some(selector) => {
389                selector.validate()?;
390                Ok(Some(selector))
391            }
392        },
393        Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(E::to_error(input, e)),
394        _ => unreachable!("through the complete combinator we get rid of Incomplete"),
395    }
396}
397
398#[cfg(test)]
399mod tests {
400    use super::*;
401
402    #[fuchsia::test]
403    fn canonical_component_selector_test() {
404        let test_vector = vec![
405            (
406                "a/b/c",
407                vec![
408                    Segment::ExactMatch("a".into()),
409                    Segment::ExactMatch("b".into()),
410                    Segment::ExactMatch("c".into()),
411                ],
412            ),
413            (
414                "a/*/c",
415                vec![
416                    Segment::ExactMatch("a".into()),
417                    Segment::Pattern("*".into()),
418                    Segment::ExactMatch("c".into()),
419                ],
420            ),
421            (
422                "a/b*/c",
423                vec![
424                    Segment::ExactMatch("a".into()),
425                    Segment::Pattern("b*".into()),
426                    Segment::ExactMatch("c".into()),
427                ],
428            ),
429            (
430                "a/b/**",
431                vec![
432                    Segment::ExactMatch("a".into()),
433                    Segment::ExactMatch("b".into()),
434                    Segment::Pattern("**".into()),
435                ],
436            ),
437            (
438                "core/session\\:id/foo",
439                vec![
440                    Segment::ExactMatch("core".into()),
441                    Segment::ExactMatch("session:id".into()),
442                    Segment::ExactMatch("foo".into()),
443                ],
444            ),
445            ("c", vec![Segment::ExactMatch("c".into())]),
446            ("<component_manager>", vec![Segment::ExactMatch("<component_manager>".into())]),
447            (
448                r#"a/*/b/**"#,
449                vec![
450                    Segment::ExactMatch("a".into()),
451                    Segment::Pattern("*".into()),
452                    Segment::ExactMatch("b".into()),
453                    Segment::Pattern("**".into()),
454                ],
455            ),
456        ];
457
458        for (test_string, expected_segments) in test_vector {
459            let (_, selector) = component_selector::<nom_language::error::VerboseError<&str>>(
460                RequireEscaped::COLONS,
461            )
462            .parse(test_string)
463            .unwrap();
464            assert_eq!(
465                expected_segments, selector.segments,
466                "For '{test_string}', got: {selector:?}",
467            );
468
469            // Component selectors can start with `/`
470            let test_moniker_string = format!("/{test_string}");
471            let (_, selector) = component_selector::<nom_language::error::VerboseError<&str>>(
472                RequireEscaped::COLONS,
473            )
474            .parse(&test_moniker_string)
475            .unwrap();
476            assert_eq!(
477                expected_segments, selector.segments,
478                "For '{test_moniker_string}', got: {selector:?}",
479            );
480
481            // Component selectors can start with `./`
482            let test_moniker_string = format!("./{test_string}");
483            let (_, selector) = component_selector::<nom_language::error::VerboseError<&str>>(
484                RequireEscaped::COLONS,
485            )
486            .parse(&test_moniker_string)
487            .unwrap();
488            assert_eq!(
489                expected_segments, selector.segments,
490                "For '{test_moniker_string}', got: {selector:?}",
491            );
492
493            // We can also accept component selectors without escaping
494            let test_moniker_string = test_string.replace("\\:", ":");
495            let (_, selector) = component_selector::<nom_language::error::VerboseError<&str>>(
496                RequireEscaped::empty(),
497            )
498            .parse(&test_moniker_string)
499            .unwrap();
500            assert_eq!(
501                expected_segments, selector.segments,
502                "For '{test_moniker_string}', got: {selector:?}",
503            );
504        }
505    }
506
507    #[fuchsia::test]
508    fn missing_path_component_selector_test() {
509        let component_selector_string = "c";
510        let (_, component_selector) =
511            component_selector::<nom_language::error::VerboseError<&str>>(RequireEscaped::COLONS)
512                .parse(component_selector_string)
513                .unwrap();
514        let mut path_vec = component_selector.segments;
515        assert_eq!(path_vec.pop(), Some(Segment::ExactMatch("c".into())));
516        assert!(path_vec.is_empty());
517    }
518
519    #[fuchsia::test]
520    fn errorful_component_selector_test() {
521        let test_vector: Vec<&str> = vec![
522            "",
523            "a\\",
524            r#"a/b***/c"#,
525            r#"a/***/c"#,
526            r#"a/**/c"#,
527            // NOTE: This used to be accepted but not anymore. Spaces shouldn't be a valid component
528            // selector character since it's not a valid moniker character.
529            " ",
530            // NOTE: The previous parser was accepting quotes in component selectors. However, by
531            // definition, a component moniker (both in v1 and v2) doesn't allow a `*` in its name.
532            r#"a/b\*/c"#,
533            r#"a/\*/c"#,
534            // Invalid characters
535            "a$c/d",
536        ];
537        for test_string in test_vector {
538            let component_selector_result =
539                consuming_component_selector::<VerboseError>(test_string, RequireEscaped::COLONS);
540            assert!(component_selector_result.is_err(), "expected '{test_string}' to fail");
541        }
542    }
543
544    #[fuchsia::test]
545    fn canonical_tree_selector_test() {
546        let test_vector = vec![
547            (
548                r#"[name="with internal ,"]b/c:d"#,
549                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
550                Some(Segment::ExactMatch("d".into())),
551                Some(vec![r#"with internal ,"#].into()),
552            ),
553            (
554                r#"[name="with internal \" escaped quote"]b/c:d"#,
555                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
556                Some(Segment::ExactMatch("d".into())),
557                Some(vec![r#"with internal " escaped quote"#].into()),
558            ),
559            (
560                r#"[name="with internal ] closing bracket"]b/c:d"#,
561                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
562                Some(Segment::ExactMatch("d".into())),
563                Some(vec!["with internal ] closing bracket"].into()),
564            ),
565            (
566                "[name=a]b/c:d",
567                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
568                Some(Segment::ExactMatch("d".into())),
569                Some(vec!["a"].into()),
570            ),
571            (
572                "[name=a:b:c:d]b/c:d",
573                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
574                Some(Segment::ExactMatch("d".into())),
575                Some(vec!["a:b:c:d"].into()),
576            ),
577            (
578                "[name=a,name=bb]b/c:d",
579                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
580                Some(Segment::ExactMatch("d".into())),
581                Some(vec!["a", "bb"].into()),
582            ),
583            (
584                "[...]b/c:d",
585                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
586                Some(Segment::ExactMatch("d".into())),
587                Some(TreeNames::All),
588            ),
589            (
590                "[name=a, name=bb]b/c:d",
591                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
592                Some(Segment::ExactMatch("d".into())),
593                Some(vec!["a", "bb"].into()),
594            ),
595            (
596                "[name=a, name=\"bb\"]b/c:d",
597                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
598                Some(Segment::ExactMatch("d".into())),
599                Some(vec!["a", "bb"].into()),
600            ),
601            (
602                r#"[name=a, name="a/\*:a"]b/c:d"#,
603                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
604                Some(Segment::ExactMatch("d".into())),
605                Some(vec!["a", "a/*:a"].into()),
606            ),
607            (
608                r#""a 1/b:d""#,
609                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b".into())],
610                Some(Segment::ExactMatch("d".into())),
611                None,
612            ),
613            (
614                r#""a 1/b 2:d""#,
615                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
616                Some(Segment::ExactMatch("d".into())),
617                None,
618            ),
619            (
620                r#""a 1/b 2:d 3""#,
621                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
622                Some(Segment::ExactMatch("d 3".into())),
623                None,
624            ),
625            (
626                r#"a\ 1/b:d"#,
627                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b".into())],
628                Some(Segment::ExactMatch("d".into())),
629                None,
630            ),
631            (
632                r#"a\ 1/b\ 2:d"#,
633                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
634                Some(Segment::ExactMatch("d".into())),
635                None,
636            ),
637            (
638                r#"a\ 1/b\ 2:d\ 3"#,
639                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
640                Some(Segment::ExactMatch("d 3".into())),
641                None,
642            ),
643            (
644                r#""a\ 1/b\ 2:d\ 3""#,
645                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
646                Some(Segment::ExactMatch("d 3".into())),
647                None,
648            ),
649            (
650                "a/b:c",
651                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b".into())],
652                Some(Segment::ExactMatch("c".into())),
653                None,
654            ),
655            (
656                "a/*:c",
657                vec![Segment::ExactMatch("a".into()), Segment::Pattern("*".into())],
658                Some(Segment::ExactMatch("c".into())),
659                None,
660            ),
661            (
662                "a/b:*",
663                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b".into())],
664                Some(Segment::Pattern("*".into())),
665                None,
666            ),
667            (
668                "a/b",
669                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b".into())],
670                None,
671                None,
672            ),
673            (
674                r#"a/b\:\*c"#,
675                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b:*c".into())],
676                None,
677                None,
678            ),
679        ];
680
681        for (string, expected_path, expected_property, expected_tree_name) in test_vector {
682            let tree_selector = standalone_tree_selector::<VerboseError>(string)
683                .unwrap_or_else(|e| panic!("input: |{string}| error: {e}"));
684            assert_eq!(
685                tree_selector,
686                TreeSelector {
687                    node: expected_path,
688                    property: expected_property,
689                    tree_names: expected_tree_name,
690                },
691                "input: |{string}|",
692            );
693        }
694    }
695
696    #[fuchsia::test]
697    fn errorful_tree_selector_test() {
698        let test_vector = vec![
699            // Not allowed due to empty property selector.
700            "a/b:",
701            // Not allowed due to glob property selector.
702            "a/b:**",
703            // String literals can't have globs.
704            r#"a/b**:c"#,
705            // Property selector string literals cant have globs.
706            r#"a/b:c**"#,
707            "a/b:**",
708            // Node path cant have globs.
709            "a/**:c",
710            // Node path can't be empty
711            ":c",
712            // Spaces aren't accepted when parsing with allow_spaces=false.
713            "a b:c",
714            "a*b:\tc",
715        ];
716        for string in test_vector {
717            // prepend a placeholder component selector so that we exercise the validation code.
718            let test_selector = format!("a:{string}");
719            assert!(
720                selector::<VerboseError>(&test_selector).is_err(),
721                "{test_selector} should fail"
722            );
723        }
724    }
725
726    #[fuchsia::test]
727    fn tree_selector_with_spaces() {
728        let with_spaces = vec![
729            (
730                r#"a\ b:c"#,
731                vec![Segment::ExactMatch("a b".into())],
732                Some(Segment::ExactMatch("c".into())),
733            ),
734            (
735                r#"ab/\ d:c\ "#,
736                vec![Segment::ExactMatch("ab".into()), Segment::ExactMatch(" d".into())],
737                Some(Segment::ExactMatch("c ".into())),
738            ),
739            (
740                "a\\\t*b:c",
741                vec![Segment::Pattern("a\t*b".into())],
742                Some(Segment::ExactMatch("c".into())),
743            ),
744            (
745                r#"a\ "x":c"#,
746                vec![Segment::ExactMatch(r#"a "x""#.into())],
747                Some(Segment::ExactMatch("c".into())),
748            ),
749        ];
750        for (string, node, property) in with_spaces {
751            assert_eq!(
752                all_consuming(tree_selector(RequireEscaped::WHITESPACE))
753                    .parse(string)
754                    .unwrap_or_else(|e: nom::Err<nom_language::error::VerboseError<_>>| panic!(
755                        "all_consuming |{string}| failed: {e}"
756                    ))
757                    .1,
758                TreeSelector { node, property, tree_names: None },
759                "input: |{string}|",
760            );
761        }
762
763        // Un-escaped quotes aren't accepted when parsing with spaces.
764        assert!(standalone_tree_selector::<VerboseError>(r#"a/b:"xc"/d"#).is_err());
765    }
766
767    #[fuchsia::test]
768    fn parse_full_selector() {
769        assert_eq!(
770            selector::<VerboseError>("core/**:some-node/he*re:prop").unwrap(),
771            Selector {
772                component: ComponentSelector {
773                    segments: vec![
774                        Segment::ExactMatch("core".into()),
775                        Segment::Pattern("**".into()),
776                    ],
777                },
778                tree: TreeSelector {
779                    node: vec![
780                        Segment::ExactMatch("some-node".into()),
781                        Segment::Pattern("he*re".into()),
782                    ],
783                    property: Some(Segment::ExactMatch("prop".into())),
784                    tree_names: None,
785                },
786            }
787        );
788
789        // Ignores whitespace.
790        assert_eq!(
791            selector::<VerboseError>("   foo:bar  ").unwrap(),
792            Selector {
793                component: ComponentSelector { segments: vec![Segment::ExactMatch("foo".into())] },
794                tree: TreeSelector {
795                    node: vec![Segment::ExactMatch("bar".into())],
796                    property: None,
797                    tree_names: None
798                },
799            }
800        );
801
802        // parses tree names
803        assert_eq!(
804            selector::<VerboseError>(r#"core/**:[name=foo, name="bar\*"]some-node/he*re:prop"#)
805                .unwrap(),
806            Selector {
807                component: ComponentSelector {
808                    segments: vec![
809                        Segment::ExactMatch("core".into()),
810                        Segment::Pattern("**".into()),
811                    ],
812                },
813                tree: TreeSelector {
814                    node: vec![
815                        Segment::ExactMatch("some-node".into()),
816                        Segment::Pattern("he*re".into()),
817                    ],
818                    property: Some(Segment::ExactMatch("prop".into())),
819                    tree_names: Some(vec!["foo", r"bar*"].into()),
820                },
821            }
822        );
823
824        assert_eq!(
825            selector::<VerboseError>(r#"core/**:[name="foo:bar"]some-node/he*re:prop"#).unwrap(),
826            Selector {
827                component: ComponentSelector {
828                    segments: vec![
829                        Segment::ExactMatch("core".into()),
830                        Segment::Pattern("**".into()),
831                    ],
832                },
833                tree: TreeSelector {
834                    node: vec![
835                        Segment::ExactMatch("some-node".into()),
836                        Segment::Pattern("he*re".into()),
837                    ],
838                    property: Some(Segment::ExactMatch("prop".into())),
839                    tree_names: Some(vec!["foo:bar"].into()),
840                },
841            }
842        );
843
844        assert_eq!(
845            selector::<VerboseError>(r#"core/**:[name="name=bar"]some-node/he*re:prop"#).unwrap(),
846            Selector {
847                component: ComponentSelector {
848                    segments: vec![
849                        Segment::ExactMatch("core".into()),
850                        Segment::Pattern("**".into()),
851                    ],
852                },
853                tree: TreeSelector {
854                    node: vec![
855                        Segment::ExactMatch("some-node".into()),
856                        Segment::Pattern("he*re".into()),
857                    ],
858                    property: Some(Segment::ExactMatch("prop".into())),
859                    tree_names: Some(vec!["name=bar"].into()),
860                },
861            }
862        );
863
864        assert_eq!(
865            selector::<VerboseError>(r#"core/**:[name=foo-bar_baz]some-node/he*re:prop"#).unwrap(),
866            Selector {
867                component: ComponentSelector {
868                    segments: vec![
869                        Segment::ExactMatch("core".into()),
870                        Segment::Pattern("**".into()),
871                    ],
872                },
873                tree: TreeSelector {
874                    node: vec![
875                        Segment::ExactMatch("some-node".into()),
876                        Segment::Pattern("he*re".into()),
877                    ],
878                    property: Some(Segment::ExactMatch("prop".into())),
879                    tree_names: Some(vec!["foo-bar_baz"].into()),
880                },
881            }
882        );
883
884        // At least one filter is required when `where` is provided.
885        assert!(selector::<VerboseError>("foo:bar where").is_err());
886    }
887
888    #[fuchsia::test]
889    fn assert_no_trailing_backward_slash() {
890        assert!(selector::<VerboseError>(r#"foo:bar:baz\"#).is_err());
891    }
892
893    #[fuchsia::test]
894    fn parse_full_selector_with_spaces() {
895        let expected_regardless_of_escape_or_quote = Selector {
896            component: ComponentSelector {
897                segments: vec![
898                    Segment::ExactMatch("core".into()),
899                    Segment::ExactMatch("foo".into()),
900                ],
901            },
902            tree: TreeSelector {
903                node: vec![Segment::ExactMatch("some node".into()), Segment::Pattern("*".into())],
904                property: Some(Segment::ExactMatch("prop".into())),
905                tree_names: None,
906            },
907        };
908        assert_eq!(
909            selector::<VerboseError>(r#"core/foo:some\ node/*:prop"#).unwrap(),
910            expected_regardless_of_escape_or_quote,
911        );
912
913        assert_eq!(
914            selector::<VerboseError>(r#""core/foo:some node/*:prop""#).unwrap(),
915            expected_regardless_of_escape_or_quote,
916        );
917    }
918
919    #[fuchsia::test]
920    fn test_extract_from_quotes() {
921        let test_cases = [
922            ("foo", "foo"),
923            (r#""foo""#, "foo"),
924            (r#""foo\"bar""#, r#"foo\"bar"#),
925            (r#""bar\*""#, r#"bar\*"#),
926        ];
927
928        for (case_number, (input, expected_extracted)) in test_cases.into_iter().enumerate() {
929            let actual_extracted = extract_from_quotes(input);
930            assert_eq!(
931                expected_extracted, actual_extracted,
932                "failed test case {case_number} on name_list: |{input}|",
933            );
934        }
935    }
936
937    #[fuchsia::test]
938    fn extract_name_list() {
939        let test_cases = [
940            ("root:prop", ("root:prop", None)),
941            ("[name=foo]root:prop", ("root:prop", Some("name=foo"))),
942            (r#"[name="with internal ,"]root"#, ("root", Some(r#"name="with internal ,""#))),
943            (r#"[name="f[o]o"]root:prop"#, ("root:prop", Some(r#"name="f[o]o""#))),
944            (
945                r#"[name="fo]o", name="[bar,baz"]root:prop"#,
946                ("root:prop", Some(r#"name="fo]o", name="[bar,baz""#)),
947            ),
948            (r#"ab/\ d:c\ "#, (r#"ab/\ d:c\ "#, None)),
949        ];
950
951        for (case_number, (input, (expected_residue, expected_name_list))) in
952            test_cases.into_iter().enumerate()
953        {
954            let (actual_residue, actual_name_list) =
955                extract_conjoined_names::<nom_language::error::VerboseError<&str>>(input).unwrap();
956            assert_eq!(
957                expected_residue, actual_residue,
958                "failed test case {case_number} on residue: |{input}|",
959            );
960            assert_eq!(
961                expected_name_list, actual_name_list,
962                "failed test case {case_number} on name_list: |{input}|",
963            );
964        }
965    }
966
967    #[fuchsia::test]
968    fn comma_separated_name_lists() {
969        let test_cases = [
970            (r#"name=foo, name=bar"#, vec!["name=foo", "name=bar"]),
971            (r#"name="with internal ,""#, vec![r#"name="with internal ,""#]),
972            (r#"name="foo", name=bar"#, vec![r#"name="foo""#, "name=bar"]),
973            (r#"name="foo,bar", name=baz"#, vec![r#"name="foo,bar""#, "name=baz"]),
974            (r#"name="foo,bar", name="baz""#, vec![r#"name="foo,bar""#, r#"name="baz""#]),
975            (r#"name="foo ,bar", name=baz"#, vec![r#"name="foo ,bar""#, "name=baz"]),
976            (r#"name="foo\",bar", name="baz""#, vec![r#"name="foo\",bar""#, r#"name="baz""#]),
977            (r#"name="foo\" ,bar", name="baz""#, vec![r#"name="foo\" ,bar""#, r#"name="baz""#]),
978            (r#"name="foo,bar", name=" baz  ""#, vec![r#"name="foo,bar""#, r#"name=" baz  ""#]),
979            (
980                r#"name="foo\", bar,", name=",,baz,,,""#,
981                vec![r#"name="foo\", bar,""#, r#"name=",,baz,,,""#],
982            ),
983        ];
984
985        for (case_number, (input, expected)) in test_cases.into_iter().enumerate() {
986            let actual = parse_quote_sensitive_separator(input, ',');
987            let actual_len = actual.len();
988            let expected_len = expected.len();
989            assert_eq!(
990                expected, actual,
991                "failed test case {case_number}: |{input}|\nexpected length: {expected_len}\nactual length: {actual_len}",
992            );
993        }
994    }
995}