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 '{test_string}', got: {selector:?}",
474            );
475
476            // Component selectors can start with `/`
477            let test_moniker_string = format!("/{test_string}");
478            let (_, selector) = component_selector::<nom_language::error::VerboseError<&str>>(
479                RequireEscaped::COLONS,
480            )
481            .parse(&test_moniker_string)
482            .unwrap();
483            assert_eq!(
484                expected_segments, selector.segments,
485                "For '{test_moniker_string}', got: {selector:?}",
486            );
487
488            // Component selectors can start with `./`
489            let test_moniker_string = format!("./{test_string}");
490            let (_, selector) = component_selector::<nom_language::error::VerboseError<&str>>(
491                RequireEscaped::COLONS,
492            )
493            .parse(&test_moniker_string)
494            .unwrap();
495            assert_eq!(
496                expected_segments, selector.segments,
497                "For '{test_moniker_string}', got: {selector:?}",
498            );
499
500            // We can also accept component selectors without escaping
501            let test_moniker_string = test_string.replace("\\:", ":");
502            let (_, selector) = component_selector::<nom_language::error::VerboseError<&str>>(
503                RequireEscaped::empty(),
504            )
505            .parse(&test_moniker_string)
506            .unwrap();
507            assert_eq!(
508                expected_segments, selector.segments,
509                "For '{test_moniker_string}', got: {selector:?}",
510            );
511        }
512    }
513
514    #[fuchsia::test]
515    fn missing_path_component_selector_test() {
516        let component_selector_string = "c";
517        let (_, component_selector) =
518            component_selector::<nom_language::error::VerboseError<&str>>(RequireEscaped::COLONS)
519                .parse(component_selector_string)
520                .unwrap();
521        let mut path_vec = component_selector.segments;
522        assert_eq!(path_vec.pop(), Some(Segment::ExactMatch("c".into())));
523        assert!(path_vec.is_empty());
524    }
525
526    #[fuchsia::test]
527    fn errorful_component_selector_test() {
528        let test_vector: Vec<&str> = vec![
529            "",
530            "a\\",
531            r#"a/b***/c"#,
532            r#"a/***/c"#,
533            r#"a/**/c"#,
534            // NOTE: This used to be accepted but not anymore. Spaces shouldn't be a valid component
535            // selector character since it's not a valid moniker character.
536            " ",
537            // NOTE: The previous parser was accepting quotes in component selectors. However, by
538            // definition, a component moniker (both in v1 and v2) doesn't allow a `*` in its name.
539            r#"a/b\*/c"#,
540            r#"a/\*/c"#,
541            // Invalid characters
542            "a$c/d",
543        ];
544        for test_string in test_vector {
545            let component_selector_result =
546                consuming_component_selector::<VerboseError>(test_string, RequireEscaped::COLONS);
547            assert!(component_selector_result.is_err(), "expected '{test_string}' to fail");
548        }
549    }
550
551    #[fuchsia::test]
552    fn canonical_tree_selector_test() {
553        let test_vector = vec![
554            (
555                r#"[name="with internal ,"]b/c:d"#,
556                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
557                Some(Segment::ExactMatch("d".into())),
558                Some(vec![r#"with internal ,"#].into()),
559            ),
560            (
561                r#"[name="with internal \" escaped quote"]b/c:d"#,
562                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
563                Some(Segment::ExactMatch("d".into())),
564                Some(vec![r#"with internal " escaped quote"#].into()),
565            ),
566            (
567                r#"[name="with internal ] closing bracket"]b/c:d"#,
568                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
569                Some(Segment::ExactMatch("d".into())),
570                Some(vec!["with internal ] closing bracket"].into()),
571            ),
572            (
573                "[name=a]b/c:d",
574                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
575                Some(Segment::ExactMatch("d".into())),
576                Some(vec!["a"].into()),
577            ),
578            (
579                "[name=a:b:c:d]b/c:d",
580                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
581                Some(Segment::ExactMatch("d".into())),
582                Some(vec!["a:b:c:d"].into()),
583            ),
584            (
585                "[name=a,name=bb]b/c:d",
586                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
587                Some(Segment::ExactMatch("d".into())),
588                Some(vec!["a", "bb"].into()),
589            ),
590            (
591                "[...]b/c:d",
592                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
593                Some(Segment::ExactMatch("d".into())),
594                Some(TreeNames::All),
595            ),
596            (
597                "[name=a, name=bb]b/c:d",
598                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
599                Some(Segment::ExactMatch("d".into())),
600                Some(vec!["a", "bb"].into()),
601            ),
602            (
603                "[name=a, name=\"bb\"]b/c:d",
604                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
605                Some(Segment::ExactMatch("d".into())),
606                Some(vec!["a", "bb"].into()),
607            ),
608            (
609                r#"[name=a, name="a/\*:a"]b/c:d"#,
610                vec![Segment::ExactMatch("b".into()), Segment::ExactMatch("c".into())],
611                Some(Segment::ExactMatch("d".into())),
612                Some(vec!["a", "a/*:a"].into()),
613            ),
614            (
615                r#""a 1/b:d""#,
616                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b".into())],
617                Some(Segment::ExactMatch("d".into())),
618                None,
619            ),
620            (
621                r#""a 1/b 2:d""#,
622                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
623                Some(Segment::ExactMatch("d".into())),
624                None,
625            ),
626            (
627                r#""a 1/b 2:d 3""#,
628                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
629                Some(Segment::ExactMatch("d 3".into())),
630                None,
631            ),
632            (
633                r#"a\ 1/b:d"#,
634                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b".into())],
635                Some(Segment::ExactMatch("d".into())),
636                None,
637            ),
638            (
639                r#"a\ 1/b\ 2:d"#,
640                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
641                Some(Segment::ExactMatch("d".into())),
642                None,
643            ),
644            (
645                r#"a\ 1/b\ 2:d\ 3"#,
646                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
647                Some(Segment::ExactMatch("d 3".into())),
648                None,
649            ),
650            (
651                r#""a\ 1/b\ 2:d\ 3""#,
652                vec![Segment::ExactMatch("a 1".into()), Segment::ExactMatch("b 2".into())],
653                Some(Segment::ExactMatch("d 3".into())),
654                None,
655            ),
656            (
657                "a/b:c",
658                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b".into())],
659                Some(Segment::ExactMatch("c".into())),
660                None,
661            ),
662            (
663                "a/*:c",
664                vec![Segment::ExactMatch("a".into()), Segment::Pattern("*".into())],
665                Some(Segment::ExactMatch("c".into())),
666                None,
667            ),
668            (
669                "a/b:*",
670                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b".into())],
671                Some(Segment::Pattern("*".into())),
672                None,
673            ),
674            (
675                "a/b",
676                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b".into())],
677                None,
678                None,
679            ),
680            (
681                r#"a/b\:\*c"#,
682                vec![Segment::ExactMatch("a".into()), Segment::ExactMatch("b:*c".into())],
683                None,
684                None,
685            ),
686        ];
687
688        for (string, expected_path, expected_property, expected_tree_name) in test_vector {
689            let tree_selector = standalone_tree_selector::<VerboseError>(string)
690                .unwrap_or_else(|e| panic!("input: |{string}| error: {e}"));
691            assert_eq!(
692                tree_selector,
693                TreeSelector {
694                    node: expected_path,
695                    property: expected_property,
696                    tree_names: expected_tree_name,
697                },
698                "input: |{string}|",
699            );
700        }
701    }
702
703    #[fuchsia::test]
704    fn errorful_tree_selector_test() {
705        let test_vector = vec![
706            // Not allowed due to empty property selector.
707            "a/b:",
708            // Not allowed due to glob property selector.
709            "a/b:**",
710            // String literals can't have globs.
711            r#"a/b**:c"#,
712            // Property selector string literals cant have globs.
713            r#"a/b:c**"#,
714            "a/b:**",
715            // Node path cant have globs.
716            "a/**:c",
717            // Node path can't be empty
718            ":c",
719            // Spaces aren't accepted when parsing with allow_spaces=false.
720            "a b:c",
721            "a*b:\tc",
722        ];
723        for string in test_vector {
724            // prepend a placeholder component selector so that we exercise the validation code.
725            let test_selector = format!("a:{string}");
726            assert!(
727                selector::<VerboseError>(&test_selector).is_err(),
728                "{test_selector} should fail"
729            );
730        }
731    }
732
733    #[fuchsia::test]
734    fn tree_selector_with_spaces() {
735        let with_spaces = vec![
736            (
737                r#"a\ b:c"#,
738                vec![Segment::ExactMatch("a b".into())],
739                Some(Segment::ExactMatch("c".into())),
740            ),
741            (
742                r#"ab/\ d:c\ "#,
743                vec![Segment::ExactMatch("ab".into()), Segment::ExactMatch(" d".into())],
744                Some(Segment::ExactMatch("c ".into())),
745            ),
746            (
747                "a\\\t*b:c",
748                vec![Segment::Pattern("a\t*b".into())],
749                Some(Segment::ExactMatch("c".into())),
750            ),
751            (
752                r#"a\ "x":c"#,
753                vec![Segment::ExactMatch(r#"a "x""#.into())],
754                Some(Segment::ExactMatch("c".into())),
755            ),
756        ];
757        for (string, node, property) in with_spaces {
758            assert_eq!(
759                all_consuming(tree_selector(RequireEscaped::WHITESPACE))
760                    .parse(string)
761                    .unwrap_or_else(|e: nom::Err<nom_language::error::VerboseError<_>>| panic!(
762                        "all_consuming |{string}| failed: {e}"
763                    ))
764                    .1,
765                TreeSelector { node, property, tree_names: None },
766                "input: |{string}|",
767            );
768        }
769
770        // Un-escaped quotes aren't accepted when parsing with spaces.
771        assert!(standalone_tree_selector::<VerboseError>(r#"a/b:"xc"/d"#).is_err());
772    }
773
774    #[fuchsia::test]
775    fn parse_full_selector() {
776        assert_eq!(
777            selector::<VerboseError>("core/**:some-node/he*re:prop").unwrap(),
778            Selector {
779                component: ComponentSelector {
780                    segments: vec![
781                        Segment::ExactMatch("core".into()),
782                        Segment::Pattern("**".into()),
783                    ],
784                },
785                tree: TreeSelector {
786                    node: vec![
787                        Segment::ExactMatch("some-node".into()),
788                        Segment::Pattern("he*re".into()),
789                    ],
790                    property: Some(Segment::ExactMatch("prop".into())),
791                    tree_names: None,
792                },
793            }
794        );
795
796        // Ignores whitespace.
797        assert_eq!(
798            selector::<VerboseError>("   foo:bar  ").unwrap(),
799            Selector {
800                component: ComponentSelector { segments: vec![Segment::ExactMatch("foo".into())] },
801                tree: TreeSelector {
802                    node: vec![Segment::ExactMatch("bar".into())],
803                    property: None,
804                    tree_names: None
805                },
806            }
807        );
808
809        // parses tree names
810        assert_eq!(
811            selector::<VerboseError>(r#"core/**:[name=foo, name="bar\*"]some-node/he*re:prop"#)
812                .unwrap(),
813            Selector {
814                component: ComponentSelector {
815                    segments: vec![
816                        Segment::ExactMatch("core".into()),
817                        Segment::Pattern("**".into()),
818                    ],
819                },
820                tree: TreeSelector {
821                    node: vec![
822                        Segment::ExactMatch("some-node".into()),
823                        Segment::Pattern("he*re".into()),
824                    ],
825                    property: Some(Segment::ExactMatch("prop".into())),
826                    tree_names: Some(vec!["foo", r"bar*"].into()),
827                },
828            }
829        );
830
831        assert_eq!(
832            selector::<VerboseError>(r#"core/**:[name="foo:bar"]some-node/he*re:prop"#).unwrap(),
833            Selector {
834                component: ComponentSelector {
835                    segments: vec![
836                        Segment::ExactMatch("core".into()),
837                        Segment::Pattern("**".into()),
838                    ],
839                },
840                tree: TreeSelector {
841                    node: vec![
842                        Segment::ExactMatch("some-node".into()),
843                        Segment::Pattern("he*re".into()),
844                    ],
845                    property: Some(Segment::ExactMatch("prop".into())),
846                    tree_names: Some(vec!["foo:bar"].into()),
847                },
848            }
849        );
850
851        assert_eq!(
852            selector::<VerboseError>(r#"core/**:[name="name=bar"]some-node/he*re:prop"#).unwrap(),
853            Selector {
854                component: ComponentSelector {
855                    segments: vec![
856                        Segment::ExactMatch("core".into()),
857                        Segment::Pattern("**".into()),
858                    ],
859                },
860                tree: TreeSelector {
861                    node: vec![
862                        Segment::ExactMatch("some-node".into()),
863                        Segment::Pattern("he*re".into()),
864                    ],
865                    property: Some(Segment::ExactMatch("prop".into())),
866                    tree_names: Some(vec!["name=bar"].into()),
867                },
868            }
869        );
870
871        assert_eq!(
872            selector::<VerboseError>(r#"core/**:[name=foo-bar_baz]some-node/he*re:prop"#).unwrap(),
873            Selector {
874                component: ComponentSelector {
875                    segments: vec![
876                        Segment::ExactMatch("core".into()),
877                        Segment::Pattern("**".into()),
878                    ],
879                },
880                tree: TreeSelector {
881                    node: vec![
882                        Segment::ExactMatch("some-node".into()),
883                        Segment::Pattern("he*re".into()),
884                    ],
885                    property: Some(Segment::ExactMatch("prop".into())),
886                    tree_names: Some(vec!["foo-bar_baz"].into()),
887                },
888            }
889        );
890
891        // At least one filter is required when `where` is provided.
892        assert!(selector::<VerboseError>("foo:bar where").is_err());
893    }
894
895    #[fuchsia::test]
896    fn assert_no_trailing_backward_slash() {
897        assert!(selector::<VerboseError>(r#"foo:bar:baz\"#).is_err());
898    }
899
900    #[fuchsia::test]
901    fn parse_full_selector_with_spaces() {
902        let expected_regardless_of_escape_or_quote = Selector {
903            component: ComponentSelector {
904                segments: vec![
905                    Segment::ExactMatch("core".into()),
906                    Segment::ExactMatch("foo".into()),
907                ],
908            },
909            tree: TreeSelector {
910                node: vec![Segment::ExactMatch("some node".into()), Segment::Pattern("*".into())],
911                property: Some(Segment::ExactMatch("prop".into())),
912                tree_names: None,
913            },
914        };
915        assert_eq!(
916            selector::<VerboseError>(r#"core/foo:some\ node/*:prop"#).unwrap(),
917            expected_regardless_of_escape_or_quote,
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
926    #[fuchsia::test]
927    fn test_extract_from_quotes() {
928        let test_cases = [
929            ("foo", "foo"),
930            (r#""foo""#, "foo"),
931            (r#""foo\"bar""#, r#"foo\"bar"#),
932            (r#""bar\*""#, r#"bar\*"#),
933        ];
934
935        for (case_number, (input, expected_extracted)) in test_cases.into_iter().enumerate() {
936            let actual_extracted = extract_from_quotes(input);
937            assert_eq!(
938                expected_extracted, actual_extracted,
939                "failed test case {case_number} on name_list: |{input}|",
940            );
941        }
942    }
943
944    #[fuchsia::test]
945    fn extract_name_list() {
946        let test_cases = [
947            ("root:prop", ("root:prop", None)),
948            ("[name=foo]root:prop", ("root:prop", Some("name=foo"))),
949            (r#"[name="with internal ,"]root"#, ("root", Some(r#"name="with internal ,""#))),
950            (r#"[name="f[o]o"]root:prop"#, ("root:prop", Some(r#"name="f[o]o""#))),
951            (
952                r#"[name="fo]o", name="[bar,baz"]root:prop"#,
953                ("root:prop", Some(r#"name="fo]o", name="[bar,baz""#)),
954            ),
955            (r#"ab/\ d:c\ "#, (r#"ab/\ d:c\ "#, None)),
956        ];
957
958        for (case_number, (input, (expected_residue, expected_name_list))) in
959            test_cases.into_iter().enumerate()
960        {
961            let (actual_residue, actual_name_list) =
962                extract_conjoined_names::<nom_language::error::VerboseError<&str>>(input).unwrap();
963            assert_eq!(
964                expected_residue, actual_residue,
965                "failed test case {case_number} on residue: |{input}|",
966            );
967            assert_eq!(
968                expected_name_list, actual_name_list,
969                "failed test case {case_number} on name_list: |{input}|",
970            );
971        }
972    }
973
974    #[fuchsia::test]
975    fn comma_separated_name_lists() {
976        let test_cases = [
977            (r#"name=foo, name=bar"#, vec!["name=foo", "name=bar"]),
978            (r#"name="with internal ,""#, vec![r#"name="with internal ,""#]),
979            (r#"name="foo", name=bar"#, vec![r#"name="foo""#, "name=bar"]),
980            (r#"name="foo,bar", name=baz"#, vec![r#"name="foo,bar""#, "name=baz"]),
981            (r#"name="foo,bar", name="baz""#, vec![r#"name="foo,bar""#, r#"name="baz""#]),
982            (r#"name="foo ,bar", name=baz"#, vec![r#"name="foo ,bar""#, "name=baz"]),
983            (r#"name="foo\",bar", name="baz""#, vec![r#"name="foo\",bar""#, r#"name="baz""#]),
984            (r#"name="foo\" ,bar", name="baz""#, vec![r#"name="foo\" ,bar""#, r#"name="baz""#]),
985            (r#"name="foo,bar", name=" baz  ""#, vec![r#"name="foo,bar""#, r#"name=" baz  ""#]),
986            (
987                r#"name="foo\", bar,", name=",,baz,,,""#,
988                vec![r#"name="foo\", bar,""#, r#"name=",,baz,,,""#],
989            ),
990        ];
991
992        for (case_number, (input, expected)) in test_cases.into_iter().enumerate() {
993            let actual = parse_quote_sensitive_separator(input, ',');
994            let actual_len = actual.len();
995            let expected_len = expected.len();
996            assert_eq!(
997                expected, actual,
998                "failed test case {case_number}: |{input}|\nexpected length: {expected_len}\nactual length: {actual_len}",
999            );
1000        }
1001    }
1002}