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