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 '{test_string}', got: {selector:?}",
474 );
475
476 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 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 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 " ",
537 r#"a/b\*/c"#,
540 r#"a/\*/c"#,
541 "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 "a/b:",
708 "a/b:**",
710 r#"a/b**:c"#,
712 r#"a/b:c**"#,
714 "a/b:**",
715 "a/**:c",
717 ":c",
719 "a b:c",
721 "a*b:\tc",
722 ];
723 for string in test_vector {
724 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 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 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 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 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}