1use 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
28fn 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
36fn 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 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
106fn tree_selector<'a, E>(
108 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 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
168fn 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 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
230fn 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)?; return Ok((rest, comment));
239 }
240 Ok((rest, comment))
241}
242
243fn 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
260fn 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
273pub struct FastError;
276
277pub struct VerboseError;
280
281mod private {
282 pub trait Sealed {}
283
284 impl Sealed for super::FastError {}
285 impl Sealed for super::VerboseError {}
286}
287
288pub 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
311pub 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 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
330pub 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
353pub 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
377pub 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 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 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 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 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 " ",
541 r#"a/b\*/c"#,
544 r#"a/\*/c"#,
545 "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 "a/b:",
712 "a/b:**",
714 r#"a/b**:c"#,
716 r#"a/b:c**"#,
718 "a/b:**",
719 "a/**:c",
721 ":c",
723 "a b:c",
725 "a*b:\tc",
726 ];
727 for string in test_vector {
728 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 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 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 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 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}