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