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, is_not, 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) = spaced(preceded(tag("//"), is_not("\n\r"))).parse(input)?;
228    if !rest.is_empty() {
229        let (rest, _) = one_of("\n\r").parse(rest)?; // consume the newline character
230        return Ok((rest, comment));
231    }
232    Ok((rest, comment))
233}
234
235/// Parses a core selector (component + tree + property). It accepts both raw selectors or
236/// selectors wrapped in double quotes. Selectors wrapped in quotes accept spaces in the tree and
237/// property names and require internal quotes to be escaped.
238fn core_selector<'a, E>(
239    input: &'a str,
240) -> IResult<&'a str, (ComponentSelector<'a>, TreeSelector<'a>), E>
241where
242    E: NomParseError<&'a str>,
243{
244    let required_tree_escape =
245        if input.starts_with('"') { RequireEscaped::empty() } else { RequireEscaped::WHITESPACE };
246    let (rest, (component, _, tree)) =
247        (component_selector(RequireEscaped::COLONS), tag(":"), tree_selector(required_tree_escape))
248            .parse(extract_from_quotes(input))?;
249    Ok((rest, (component, tree)))
250}
251
252/// Recognizes selectors, with comments allowed or disallowed.
253fn do_parse_selector<'a, E>(
254    allow_inline_comment: bool,
255) -> impl Parser<&'a str, Output = Selector<'a>, Error = E>
256where
257    E: NomParseError<&'a str>,
258{
259    map(
260        (spaced(core_selector), cond(allow_inline_comment, opt(comment)), multispace0),
261        move |((component, tree), _, _)| Selector { component, tree },
262    )
263}
264
265/// A fast efficient error that won't provide much information besides the name kind of nom parsers
266/// that failed and the position at which it failed.
267pub struct FastError;
268
269/// A slower but more user friendly error that will provide information about the chain of parsers
270/// that found the error and some context.
271pub struct VerboseError;
272
273mod private {
274    pub trait Sealed {}
275
276    impl Sealed for super::FastError {}
277    impl Sealed for super::VerboseError {}
278}
279
280/// Implemented by types which can be used to specify the error strategy the parsers should use.
281pub trait ParsingError<'a>: private::Sealed {
282    type Internal: NomParseError<&'a str>;
283
284    fn to_error(input: &str, err: Self::Internal) -> ParseError;
285}
286
287impl<'a> ParsingError<'a> for FastError {
288    type Internal = (&'a str, ErrorKind);
289
290    fn to_error(_: &str, (part, error_kind): Self::Internal) -> ParseError {
291        ParseError::Fast(part.to_owned(), error_kind)
292    }
293}
294
295impl<'a> ParsingError<'a> for VerboseError {
296    type Internal = nom_language::error::VerboseError<&'a str>;
297
298    fn to_error(input: &str, err: Self::Internal) -> ParseError {
299        ParseError::Verbose(nom_language::error::convert_error(input, err))
300    }
301}
302
303/// Parses the input into a `Selector`.
304pub fn selector<'a, E>(input: &'a str) -> Result<Selector<'a>, ParseError>
305where
306    E: ParsingError<'a>,
307{
308    let result = complete(all_consuming(do_parse_selector::<<E as ParsingError<'_>>::Internal>(
309        /*allow_inline_comment=*/ false,
310    )))
311    .parse(input);
312    match result {
313        Ok((_, selector)) => {
314            selector.validate()?;
315            Ok(selector)
316        }
317        Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(E::to_error(input, e)),
318        _ => unreachable!("through the complete combinator we get rid of Incomplete"),
319    }
320}
321
322/// Parses the input into a `TreeSelector` ignoring any whitespace around the component
323/// selector.
324pub fn standalone_tree_selector<'a, E>(input: &'a str) -> Result<TreeSelector<'a>, ParseError>
325where
326    E: ParsingError<'a>,
327{
328    let required_tree_escape =
329        if input.starts_with('"') { RequireEscaped::empty() } else { RequireEscaped::WHITESPACE };
330    let result = nom::combinator::all_consuming(pair(
331        spaced(tree_selector(required_tree_escape)),
332        multispace0,
333    ))
334    .parse(extract_from_quotes(input));
335    match result {
336        Ok((_, (tree_selector, _))) => {
337            tree_selector.validate()?;
338            Ok(tree_selector)
339        }
340        Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(E::to_error(input, e)),
341        _ => unreachable!("through the complete combinator we get rid of Incomplete"),
342    }
343}
344
345/// Parses the input into a `ComponentSelector` ignoring any whitespace around the component
346/// selector.
347pub fn consuming_component_selector<'a, E>(
348    input: &'a str,
349    required_escapes: RequireEscaped,
350) -> Result<ComponentSelector<'a>, ParseError>
351where
352    E: ParsingError<'a>,
353{
354    let result = nom::combinator::all_consuming(pair(
355        spaced(component_selector(required_escapes)),
356        multispace0,
357    ))
358    .parse(input);
359    match result {
360        Ok((_, (component_selector, _))) => {
361            component_selector.validate()?;
362            Ok(component_selector)
363        }
364        Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(E::to_error(input, e)),
365        _ => unreachable!("through the complete combinator we get rid of Incomplete"),
366    }
367}
368
369/// Parses the given input line into a Selector or None.
370pub fn selector_or_comment<'a, E>(input: &'a str) -> Result<Option<Selector<'a>>, ParseError>
371where
372    E: ParsingError<'a>,
373{
374    let result = complete(all_consuming(alt((
375        map(comment, |_| None),
376        map(
377            do_parse_selector::<<E as ParsingError<'_>>::Internal>(
378                /*allow_inline_comment=*/ true,
379            ),
380            Some,
381        ),
382    ))))
383    .parse(input);
384    match result {
385        Ok((_, maybe_selector)) => match maybe_selector {
386            None => Ok(None),
387            Some(selector) => {
388                selector.validate()?;
389                Ok(Some(selector))
390            }
391        },
392        Err(nom::Err::Error(e) | nom::Err::Failure(e)) => Err(E::to_error(input, e)),
393        _ => unreachable!("through the complete combinator we get rid of Incomplete"),
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400
401    #[fuchsia::test]
402    fn canonical_component_selector_test() {
403        let test_vector = vec![
404            (
405                "a/b/c",
406                vec![
407                    Segment::ExactMatch("a".into()),
408                    Segment::ExactMatch("b".into()),
409                    Segment::ExactMatch("c".into()),
410                ],
411            ),
412            (
413                "a/*/c",
414                vec![
415                    Segment::ExactMatch("a".into()),
416                    Segment::Pattern("*".into()),
417                    Segment::ExactMatch("c".into()),
418                ],
419            ),
420            (
421                "a/b*/c",
422                vec![
423                    Segment::ExactMatch("a".into()),
424                    Segment::Pattern("b*".into()),
425                    Segment::ExactMatch("c".into()),
426                ],
427            ),
428            (
429                "a/b/**",
430                vec![
431                    Segment::ExactMatch("a".into()),
432                    Segment::ExactMatch("b".into()),
433                    Segment::Pattern("**".into()),
434                ],
435            ),
436            (
437                "core/session\\:id/foo",
438                vec![
439                    Segment::ExactMatch("core".into()),
440                    Segment::ExactMatch("session:id".into()),
441                    Segment::ExactMatch("foo".into()),
442                ],
443            ),
444            ("c", vec![Segment::ExactMatch("c".into())]),
445            ("<component_manager>", vec![Segment::ExactMatch("<component_manager>".into())]),
446            (
447                r#"a/*/b/**"#,
448                vec![
449                    Segment::ExactMatch("a".into()),
450                    Segment::Pattern("*".into()),
451                    Segment::ExactMatch("b".into()),
452                    Segment::Pattern("**".into()),
453                ],
454            ),
455        ];
456
457        for (test_string, expected_segments) in test_vector {
458            let (_, selector) = component_selector::<nom_language::error::VerboseError<&str>>(
459                RequireEscaped::COLONS,
460            )
461            .parse(test_string)
462            .unwrap();
463            assert_eq!(
464                expected_segments, selector.segments,
465                "For '{test_string}', got: {selector:?}",
466            );
467
468            // Component selectors can start with `/`
469            let test_moniker_string = format!("/{test_string}");
470            let (_, selector) = component_selector::<nom_language::error::VerboseError<&str>>(
471                RequireEscaped::COLONS,
472            )
473            .parse(&test_moniker_string)
474            .unwrap();
475            assert_eq!(
476                expected_segments, selector.segments,
477                "For '{test_moniker_string}', got: {selector:?}",
478            );
479
480            // Component selectors can start with `./`
481            let test_moniker_string = format!("./{test_string}");
482            let (_, selector) = component_selector::<nom_language::error::VerboseError<&str>>(
483                RequireEscaped::COLONS,
484            )
485            .parse(&test_moniker_string)
486            .unwrap();
487            assert_eq!(
488                expected_segments, selector.segments,
489                "For '{test_moniker_string}', got: {selector:?}",
490            );
491
492            // We can also accept component selectors without escaping
493            let test_moniker_string = test_string.replace("\\:", ":");
494            let (_, selector) = component_selector::<nom_language::error::VerboseError<&str>>(
495                RequireEscaped::empty(),
496            )
497            .parse(&test_moniker_string)
498            .unwrap();
499            assert_eq!(
500                expected_segments, selector.segments,
501                "For '{test_moniker_string}', got: {selector:?}",
502            );
503        }
504    }
505
506    #[fuchsia::test]
507    fn missing_path_component_selector_test() {
508        let component_selector_string = "c";
509        let (_, component_selector) =
510            component_selector::<nom_language::error::VerboseError<&str>>(RequireEscaped::COLONS)
511                .parse(component_selector_string)
512                .unwrap();
513        let mut path_vec = component_selector.segments;
514        assert_eq!(path_vec.pop(), Some(Segment::ExactMatch("c".into())));
515        assert!(path_vec.is_empty());
516    }
517
518    #[fuchsia::test]
519    fn errorful_component_selector_test() {
520        let test_vector: Vec<&str> = vec![
521            "",
522            "a\\",
523            r#"a/b***/c"#,
524            r#"a/***/c"#,
525            r#"a/**/c"#,
526            // NOTE: This used to be accepted but not anymore. Spaces shouldn't be a valid component
527            // selector character since it's not a valid moniker character.
528            " ",
529            // NOTE: The previous parser was accepting quotes in component selectors. However, by
530            // definition, a component moniker (both in v1 and v2) doesn't allow a `*` in its name.
531            r#"a/b\*/c"#,
532            r#"a/\*/c"#,
533            // Invalid characters
534            "a$c/d",
535        ];
536        for test_string in test_vector {
537            let component_selector_result =
538                consuming_component_selector::<VerboseError>(test_string, RequireEscaped::COLONS);
539            assert!(component_selector_result.is_err(), "expected '{test_string}' to fail");
540        }
541    }
542
543    #[fuchsia::test]
544    fn canonical_tree_selector_test() {
545        let test_vector = vec![
546            (
547                r#"[name="with internal ,"]b/c:d"#,
548                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
549                Some(Segment::ExactMatch("d".into())),
550                Some(vec![r#"with internal ,"#].into()),
551            ),
552            (
553                r#"[name="with internal \" escaped quote"]b/c:d"#,
554                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
555                Some(Segment::ExactMatch("d".into())),
556                Some(vec![r#"with internal " escaped quote"#].into()),
557            ),
558            (
559                r#"[name="with internal ] closing bracket"]b/c:d"#,
560                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
561                Some(Segment::ExactMatch("d".into())),
562                Some(vec!["with internal ] closing bracket"].into()),
563            ),
564            (
565                "[name=a]b/c:d",
566                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
567                Some(Segment::ExactMatch("d".into())),
568                Some(vec!["a"].into()),
569            ),
570            (
571                "[name=a:b:c:d]b/c:d",
572                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
573                Some(Segment::ExactMatch("d".into())),
574                Some(vec!["a:b:c:d"].into()),
575            ),
576            (
577                "[name=a,name=bb]b/c:d",
578                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
579                Some(Segment::ExactMatch("d".into())),
580                Some(vec!["a", "bb"].into()),
581            ),
582            (
583                "[...]b/c:d",
584                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
585                Some(Segment::ExactMatch("d".into())),
586                Some(TreeNames::All),
587            ),
588            (
589                "[name=a, name=bb]b/c:d",
590                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
591                Some(Segment::ExactMatch("d".into())),
592                Some(vec!["a", "bb"].into()),
593            ),
594            (
595                "[name=a, name=\"bb\"]b/c:d",
596                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
597                Some(Segment::ExactMatch("d".into())),
598                Some(vec!["a", "bb"].into()),
599            ),
600            (
601                r#"[name=a, name="a/\*:a"]b/c:d"#,
602                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
603                Some(Segment::ExactMatch("d".into())),
604                Some(vec!["a", "a/*:a"].into()),
605            ),
606            (
607                r#""a 1/b:d""#,
608                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b".into())],
609                Some(Segment::ExactMatch("d".into())),
610                None,
611            ),
612            (
613                r#""a 1/b 2:d""#,
614                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
615                Some(Segment::ExactMatch("d".into())),
616                None,
617            ),
618            (
619                r#""a 1/b 2:d 3""#,
620                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
621                Some(Segment::ExactMatch("d 3".into())),
622                None,
623            ),
624            (
625                r#"a\ 1/b:d"#,
626                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b".into())],
627                Some(Segment::ExactMatch("d".into())),
628                None,
629            ),
630            (
631                r#"a\ 1/b\ 2:d"#,
632                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
633                Some(Segment::ExactMatch("d".into())),
634                None,
635            ),
636            (
637                r#"a\ 1/b\ 2:d\ 3"#,
638                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
639                Some(Segment::ExactMatch("d 3".into())),
640                None,
641            ),
642            (
643                r#""a\ 1/b\ 2:d\ 3""#,
644                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
645                Some(Segment::ExactMatch("d 3".into())),
646                None,
647            ),
648            (
649                "a/b:c",
650                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b".into())],
651                Some(Segment::ExactMatch("c".into())),
652                None,
653            ),
654            (
655                "a/*:c",
656                vec![Segment::ExactMatch("a".into()), Segment::Pattern("*".into())],
657                Some(Segment::ExactMatch("c".into())),
658                None,
659            ),
660            (
661                "a/b:*",
662                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b".into())],
663                Some(Segment::Pattern("*".into())),
664                None,
665            ),
666            (
667                "a/b",
668                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b".into())],
669                None,
670                None,
671            ),
672            (
673                r#"a/b\:\*c"#,
674                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b:*c".into())],
675                None,
676                None,
677            ),
678        ];
679
680        for (string, expected_path, expected_property, expected_tree_name) in test_vector {
681            let tree_selector = standalone_tree_selector::<VerboseError>(string)
682                .unwrap_or_else(|e| panic!("input: |{string}| error: {e}"));
683            assert_eq!(
684                tree_selector,
685                TreeSelector {
686                    node: expected_path,
687                    property: expected_property,
688                    tree_names: expected_tree_name,
689                },
690                "input: |{string}|",
691            );
692        }
693    }
694
695    #[fuchsia::test]
696    fn errorful_tree_selector_test() {
697        let test_vector = vec![
698            // Not allowed due to empty property selector.
699            "a/b:",
700            // Not allowed due to glob property selector.
701            "a/b:**",
702            // String literals can't have globs.
703            r#"a/b**:c"#,
704            // Property selector string literals cant have globs.
705            r#"a/b:c**"#,
706            "a/b:**",
707            // Node path cant have globs.
708            "a/**:c",
709            // Node path can't be empty
710            ":c",
711            // Spaces aren't accepted when parsing with allow_spaces=false.
712            "a b:c",
713            "a*b:\tc",
714        ];
715        for string in test_vector {
716            // prepend a placeholder component selector so that we exercise the validation code.
717            let test_selector = format!("a:{string}");
718            assert!(
719                selector::<VerboseError>(&test_selector).is_err(),
720                "{test_selector} should fail"
721            );
722        }
723    }
724
725    #[fuchsia::test]
726    fn tree_selector_with_spaces() {
727        let with_spaces = vec![
728            (
729                r#"a\ b:c"#,
730                vec![Segment::ExactMatch("a b".into())],
731                Some(Segment::ExactMatch("c".into())),
732            ),
733            (
734                r#"ab/\ d:c\ "#,
735                vec![Segment::ExactMatch("ab".into()), Segment::ExactMatch(" d".into())],
736                Some(Segment::ExactMatch("c ".into())),
737            ),
738            (
739                "a\\\t*b:c",
740                vec![Segment::Pattern("a\t*b".into())],
741                Some(Segment::ExactMatch("c".into())),
742            ),
743            (
744                r#"a\ "x":c"#,
745                vec![Segment::ExactMatch(r#"a "x""#.into())],
746                Some(Segment::ExactMatch("c".into())),
747            ),
748        ];
749        for (string, node, property) in with_spaces {
750            assert_eq!(
751                all_consuming(tree_selector(RequireEscaped::WHITESPACE))
752                    .parse(string)
753                    .unwrap_or_else(|e: nom::Err<nom_language::error::VerboseError<_>>| panic!(
754                        "all_consuming |{string}| failed: {e}"
755                    ))
756                    .1,
757                TreeSelector { node, property, tree_names: None },
758                "input: |{string}|",
759            );
760        }
761
762        // Un-escaped quotes aren't accepted when parsing with spaces.
763        assert!(standalone_tree_selector::<VerboseError>(r#"a/b:"xc"/d"#).is_err());
764    }
765
766    #[fuchsia::test]
767    fn parse_full_selector() {
768        assert_eq!(
769            selector::<VerboseError>("core/**:some-node/he*re:prop").unwrap(),
770            Selector {
771                component: ComponentSelector {
772                    segments: vec![
773                        Segment::ExactMatch("core".into()),
774                        Segment::Pattern("**".into()),
775                    ],
776                },
777                tree: TreeSelector {
778                    node: vec![
779                        Segment::ExactMatch("some-node".into()),
780                        Segment::Pattern("he*re".into()),
781                    ],
782                    property: Some(Segment::ExactMatch("prop".into())),
783                    tree_names: None,
784                },
785            }
786        );
787
788        // Ignores whitespace.
789        assert_eq!(
790            selector::<VerboseError>("   foo:bar  ").unwrap(),
791            Selector {
792                component: ComponentSelector { segments: vec![Segment::ExactMatch("foo".into())] },
793                tree: TreeSelector {
794                    node: vec![Segment::ExactMatch("bar".into())],
795                    property: None,
796                    tree_names: None
797                },
798            }
799        );
800
801        // parses tree names
802        assert_eq!(
803            selector::<VerboseError>(r#"core/**:[name=foo, name="bar\*"]some-node/he*re:prop"#)
804                .unwrap(),
805            Selector {
806                component: ComponentSelector {
807                    segments: vec![
808                        Segment::ExactMatch("core".into()),
809                        Segment::Pattern("**".into()),
810                    ],
811                },
812                tree: TreeSelector {
813                    node: vec![
814                        Segment::ExactMatch("some-node".into()),
815                        Segment::Pattern("he*re".into()),
816                    ],
817                    property: Some(Segment::ExactMatch("prop".into())),
818                    tree_names: Some(vec!["foo", r"bar*"].into()),
819                },
820            }
821        );
822
823        assert_eq!(
824            selector::<VerboseError>(r#"core/**:[name="foo:bar"]some-node/he*re:prop"#).unwrap(),
825            Selector {
826                component: ComponentSelector {
827                    segments: vec![
828                        Segment::ExactMatch("core".into()),
829                        Segment::Pattern("**".into()),
830                    ],
831                },
832                tree: TreeSelector {
833                    node: vec![
834                        Segment::ExactMatch("some-node".into()),
835                        Segment::Pattern("he*re".into()),
836                    ],
837                    property: Some(Segment::ExactMatch("prop".into())),
838                    tree_names: Some(vec!["foo:bar"].into()),
839                },
840            }
841        );
842
843        assert_eq!(
844            selector::<VerboseError>(r#"core/**:[name="name=bar"]some-node/he*re:prop"#).unwrap(),
845            Selector {
846                component: ComponentSelector {
847                    segments: vec![
848                        Segment::ExactMatch("core".into()),
849                        Segment::Pattern("**".into()),
850                    ],
851                },
852                tree: TreeSelector {
853                    node: vec![
854                        Segment::ExactMatch("some-node".into()),
855                        Segment::Pattern("he*re".into()),
856                    ],
857                    property: Some(Segment::ExactMatch("prop".into())),
858                    tree_names: Some(vec!["name=bar"].into()),
859                },
860            }
861        );
862
863        assert_eq!(
864            selector::<VerboseError>(r#"core/**:[name=foo-bar_baz]some-node/he*re:prop"#).unwrap(),
865            Selector {
866                component: ComponentSelector {
867                    segments: vec![
868                        Segment::ExactMatch("core".into()),
869                        Segment::Pattern("**".into()),
870                    ],
871                },
872                tree: TreeSelector {
873                    node: vec![
874                        Segment::ExactMatch("some-node".into()),
875                        Segment::Pattern("he*re".into()),
876                    ],
877                    property: Some(Segment::ExactMatch("prop".into())),
878                    tree_names: Some(vec!["foo-bar_baz"].into()),
879                },
880            }
881        );
882
883        // At least one filter is required when `where` is provided.
884        assert!(selector::<VerboseError>("foo:bar where").is_err());
885    }
886
887    #[fuchsia::test]
888    fn assert_no_trailing_backward_slash() {
889        assert!(selector::<VerboseError>(r#"foo:bar:baz\"#).is_err());
890    }
891
892    #[fuchsia::test]
893    fn parse_full_selector_with_spaces() {
894        let expected_regardless_of_escape_or_quote = Selector {
895            component: ComponentSelector {
896                segments: vec![
897                    Segment::ExactMatch("core".into()),
898                    Segment::ExactMatch("foo".into()),
899                ],
900            },
901            tree: TreeSelector {
902                node: vec![Segment::ExactMatch("some node".into()), Segment::Pattern("*".into())],
903                property: Some(Segment::ExactMatch("prop".into())),
904                tree_names: None,
905            },
906        };
907        assert_eq!(
908            selector::<VerboseError>(r#"core/foo:some\ node/*:prop"#).unwrap(),
909            expected_regardless_of_escape_or_quote,
910        );
911
912        assert_eq!(
913            selector::<VerboseError>(r#""core/foo:some node/*:prop""#).unwrap(),
914            expected_regardless_of_escape_or_quote,
915        );
916    }
917
918    #[fuchsia::test]
919    fn test_extract_from_quotes() {
920        let test_cases = [
921            ("foo", "foo"),
922            (r#""foo""#, "foo"),
923            (r#""foo\"bar""#, r#"foo\"bar"#),
924            (r#""bar\*""#, r#"bar\*"#),
925        ];
926
927        for (case_number, (input, expected_extracted)) in test_cases.into_iter().enumerate() {
928            let actual_extracted = extract_from_quotes(input);
929            assert_eq!(
930                expected_extracted, actual_extracted,
931                "failed test case {case_number} on name_list: |{input}|",
932            );
933        }
934    }
935
936    #[fuchsia::test]
937    fn extract_name_list() {
938        let test_cases = [
939            ("root:prop", ("root:prop", None)),
940            ("[name=foo]root:prop", ("root:prop", Some("name=foo"))),
941            (r#"[name="with internal ,"]root"#, ("root", Some(r#"name="with internal ,""#))),
942            (r#"[name="f[o]o"]root:prop"#, ("root:prop", Some(r#"name="f[o]o""#))),
943            (
944                r#"[name="fo]o", name="[bar,baz"]root:prop"#,
945                ("root:prop", Some(r#"name="fo]o", name="[bar,baz""#)),
946            ),
947            (r#"ab/\ d:c\ "#, (r#"ab/\ d:c\ "#, None)),
948        ];
949
950        for (case_number, (input, (expected_residue, expected_name_list))) in
951            test_cases.into_iter().enumerate()
952        {
953            let (actual_residue, actual_name_list) =
954                extract_conjoined_names::<nom_language::error::VerboseError<&str>>(input).unwrap();
955            assert_eq!(
956                expected_residue, actual_residue,
957                "failed test case {case_number} on residue: |{input}|",
958            );
959            assert_eq!(
960                expected_name_list, actual_name_list,
961                "failed test case {case_number} on name_list: |{input}|",
962            );
963        }
964    }
965
966    #[fuchsia::test]
967    fn comma_separated_name_lists() {
968        let test_cases = [
969            (r#"name=foo, name=bar"#, vec!["name=foo", "name=bar"]),
970            (r#"name="with internal ,""#, vec![r#"name="with internal ,""#]),
971            (r#"name="foo", name=bar"#, vec![r#"name="foo""#, "name=bar"]),
972            (r#"name="foo,bar", name=baz"#, vec![r#"name="foo,bar""#, "name=baz"]),
973            (r#"name="foo,bar", name="baz""#, vec![r#"name="foo,bar""#, r#"name="baz""#]),
974            (r#"name="foo ,bar", name=baz"#, vec![r#"name="foo ,bar""#, "name=baz"]),
975            (r#"name="foo\",bar", name="baz""#, vec![r#"name="foo\",bar""#, r#"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            (
979                r#"name="foo\", bar,", name=",,baz,,,""#,
980                vec![r#"name="foo\", bar,""#, r#"name=",,baz,,,""#],
981            ),
982        ];
983
984        for (case_number, (input, expected)) in test_cases.into_iter().enumerate() {
985            let actual = parse_quote_sensitive_separator(input, ',');
986            let actual_len = actual.len();
987            let expected_len = expected.len();
988            assert_eq!(
989                expected, actual,
990                "failed test case {case_number}: |{input}|\nexpected length: {expected_len}\nactual length: {actual_len}",
991            );
992        }
993    }
994}