1use std::fmt::Write;
2use std::path::{is_separator, Path};
3
4use regex_automata::meta::Regex;
5
6use crate::{new_regex, Candidate, Error, ErrorKind};
7
8#[derive(Clone, Debug, Eq, PartialEq)]
16pub(crate) enum MatchStrategy {
17 Literal(String),
20 BasenameLiteral(String),
23 Extension(String),
26 Prefix(String),
29 Suffix {
35 suffix: String,
37 component: bool,
39 },
40 RequiredExtension(String),
45 Regex,
47}
48
49impl MatchStrategy {
50 pub(crate) fn new(pat: &Glob) -> MatchStrategy {
52 if let Some(lit) = pat.basename_literal() {
53 MatchStrategy::BasenameLiteral(lit)
54 } else if let Some(lit) = pat.literal() {
55 MatchStrategy::Literal(lit)
56 } else if let Some(ext) = pat.ext() {
57 MatchStrategy::Extension(ext)
58 } else if let Some(prefix) = pat.prefix() {
59 MatchStrategy::Prefix(prefix)
60 } else if let Some((suffix, component)) = pat.suffix() {
61 MatchStrategy::Suffix { suffix, component }
62 } else if let Some(ext) = pat.required_ext() {
63 MatchStrategy::RequiredExtension(ext)
64 } else {
65 MatchStrategy::Regex
66 }
67 }
68}
69
70#[derive(Clone, Debug, Eq)]
75pub struct Glob {
76 glob: String,
77 re: String,
78 opts: GlobOptions,
79 tokens: Tokens,
80}
81
82impl PartialEq for Glob {
83 fn eq(&self, other: &Glob) -> bool {
84 self.glob == other.glob && self.opts == other.opts
85 }
86}
87
88impl std::hash::Hash for Glob {
89 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
90 self.glob.hash(state);
91 self.opts.hash(state);
92 }
93}
94
95impl std::fmt::Display for Glob {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 self.glob.fmt(f)
98 }
99}
100
101impl std::str::FromStr for Glob {
102 type Err = Error;
103
104 fn from_str(glob: &str) -> Result<Self, Self::Err> {
105 Self::new(glob)
106 }
107}
108
109#[derive(Clone, Debug)]
111pub struct GlobMatcher {
112 pat: Glob,
114 re: Regex,
116}
117
118impl GlobMatcher {
119 pub fn is_match<P: AsRef<Path>>(&self, path: P) -> bool {
121 self.is_match_candidate(&Candidate::new(path.as_ref()))
122 }
123
124 pub fn is_match_candidate(&self, path: &Candidate<'_>) -> bool {
126 self.re.is_match(&path.path)
127 }
128
129 pub fn glob(&self) -> &Glob {
131 &self.pat
132 }
133}
134
135#[cfg(test)]
137#[derive(Clone, Debug)]
138struct GlobStrategic {
139 strategy: MatchStrategy,
141 re: Regex,
143}
144
145#[cfg(test)]
146impl GlobStrategic {
147 fn is_match<P: AsRef<Path>>(&self, path: P) -> bool {
149 self.is_match_candidate(&Candidate::new(path.as_ref()))
150 }
151
152 fn is_match_candidate(&self, candidate: &Candidate<'_>) -> bool {
154 let byte_path = &*candidate.path;
155
156 match self.strategy {
157 MatchStrategy::Literal(ref lit) => lit.as_bytes() == byte_path,
158 MatchStrategy::BasenameLiteral(ref lit) => {
159 lit.as_bytes() == &*candidate.basename
160 }
161 MatchStrategy::Extension(ref ext) => {
162 ext.as_bytes() == &*candidate.ext
163 }
164 MatchStrategy::Prefix(ref pre) => {
165 starts_with(pre.as_bytes(), byte_path)
166 }
167 MatchStrategy::Suffix { ref suffix, component } => {
168 if component && byte_path == &suffix.as_bytes()[1..] {
169 return true;
170 }
171 ends_with(suffix.as_bytes(), byte_path)
172 }
173 MatchStrategy::RequiredExtension(ref ext) => {
174 let ext = ext.as_bytes();
175 &*candidate.ext == ext && self.re.is_match(byte_path)
176 }
177 MatchStrategy::Regex => self.re.is_match(byte_path),
178 }
179 }
180}
181
182#[derive(Clone, Debug)]
189pub struct GlobBuilder<'a> {
190 glob: &'a str,
192 opts: GlobOptions,
194}
195
196#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
197struct GlobOptions {
198 case_insensitive: bool,
200 literal_separator: bool,
203 backslash_escape: bool,
206 empty_alternates: bool,
209}
210
211impl GlobOptions {
212 fn default() -> GlobOptions {
213 GlobOptions {
214 case_insensitive: false,
215 literal_separator: false,
216 backslash_escape: !is_separator('\\'),
217 empty_alternates: false,
218 }
219 }
220}
221
222#[derive(Clone, Debug, Default, Eq, PartialEq)]
223struct Tokens(Vec<Token>);
224
225impl std::ops::Deref for Tokens {
226 type Target = Vec<Token>;
227 fn deref(&self) -> &Vec<Token> {
228 &self.0
229 }
230}
231
232impl std::ops::DerefMut for Tokens {
233 fn deref_mut(&mut self) -> &mut Vec<Token> {
234 &mut self.0
235 }
236}
237
238#[derive(Clone, Debug, Eq, PartialEq)]
239enum Token {
240 Literal(char),
241 Any,
242 ZeroOrMore,
243 RecursivePrefix,
244 RecursiveSuffix,
245 RecursiveZeroOrMore,
246 Class { negated: bool, ranges: Vec<(char, char)> },
247 Alternates(Vec<Tokens>),
248}
249
250impl Glob {
251 pub fn new(glob: &str) -> Result<Glob, Error> {
253 GlobBuilder::new(glob).build()
254 }
255
256 pub fn compile_matcher(&self) -> GlobMatcher {
258 let re =
259 new_regex(&self.re).expect("regex compilation shouldn't fail");
260 GlobMatcher { pat: self.clone(), re }
261 }
262
263 #[cfg(test)]
269 fn compile_strategic_matcher(&self) -> GlobStrategic {
270 let strategy = MatchStrategy::new(self);
271 let re =
272 new_regex(&self.re).expect("regex compilation shouldn't fail");
273 GlobStrategic { strategy, re }
274 }
275
276 pub fn glob(&self) -> &str {
278 &self.glob
279 }
280
281 pub fn regex(&self) -> &str {
296 &self.re
297 }
298
299 fn literal(&self) -> Option<String> {
304 if self.opts.case_insensitive {
305 return None;
306 }
307 let mut lit = String::new();
308 for t in &*self.tokens {
309 let Token::Literal(c) = *t else { return None };
310 lit.push(c);
311 }
312 if lit.is_empty() {
313 None
314 } else {
315 Some(lit)
316 }
317 }
318
319 fn ext(&self) -> Option<String> {
327 if self.opts.case_insensitive {
328 return None;
329 }
330 let start = match *self.tokens.get(0)? {
331 Token::RecursivePrefix => 1,
332 _ => 0,
333 };
334 match *self.tokens.get(start)? {
335 Token::ZeroOrMore => {
336 if start == 0 && self.opts.literal_separator {
340 return None;
341 }
342 }
343 _ => return None,
344 }
345 match *self.tokens.get(start + 1)? {
346 Token::Literal('.') => {}
347 _ => return None,
348 }
349 let mut lit = ".".to_string();
350 for t in self.tokens[start + 2..].iter() {
351 match *t {
352 Token::Literal('.') | Token::Literal('/') => return None,
353 Token::Literal(c) => lit.push(c),
354 _ => return None,
355 }
356 }
357 if lit.is_empty() {
358 None
359 } else {
360 Some(lit)
361 }
362 }
363
364 fn required_ext(&self) -> Option<String> {
368 if self.opts.case_insensitive {
369 return None;
370 }
371 let mut ext: Vec<char> = vec![]; for t in self.tokens.iter().rev() {
375 match *t {
376 Token::Literal('/') => return None,
377 Token::Literal(c) => {
378 ext.push(c);
379 if c == '.' {
380 break;
381 }
382 }
383 _ => return None,
384 }
385 }
386 if ext.last() != Some(&'.') {
387 None
388 } else {
389 ext.reverse();
390 Some(ext.into_iter().collect())
391 }
392 }
393
394 fn prefix(&self) -> Option<String> {
397 if self.opts.case_insensitive {
398 return None;
399 }
400 let (end, need_sep) = match *self.tokens.last()? {
401 Token::ZeroOrMore => {
402 if self.opts.literal_separator {
403 return None;
410 }
411 (self.tokens.len() - 1, false)
412 }
413 Token::RecursiveSuffix => (self.tokens.len() - 1, true),
414 _ => (self.tokens.len(), false),
415 };
416 let mut lit = String::new();
417 for t in &self.tokens[0..end] {
418 let Token::Literal(c) = *t else { return None };
419 lit.push(c);
420 }
421 if need_sep {
422 lit.push('/');
423 }
424 if lit.is_empty() {
425 None
426 } else {
427 Some(lit)
428 }
429 }
430
431 fn suffix(&self) -> Option<(String, bool)> {
444 if self.opts.case_insensitive {
445 return None;
446 }
447 let mut lit = String::new();
448 let (start, entire) = match *self.tokens.get(0)? {
449 Token::RecursivePrefix => {
450 if let Some(&Token::Literal(_)) = self.tokens.get(1) {
453 lit.push('/');
454 (1, true)
455 } else {
456 (1, false)
457 }
458 }
459 _ => (0, false),
460 };
461 let start = match *self.tokens.get(start)? {
462 Token::ZeroOrMore => {
463 if self.opts.literal_separator {
467 return None;
468 }
469 start + 1
470 }
471 _ => start,
472 };
473 for t in &self.tokens[start..] {
474 let Token::Literal(c) = *t else { return None };
475 lit.push(c);
476 }
477 if lit.is_empty() || lit == "/" {
478 None
479 } else {
480 Some((lit, entire))
481 }
482 }
483
484 fn basename_tokens(&self) -> Option<&[Token]> {
496 if self.opts.case_insensitive {
497 return None;
498 }
499 let start = match *self.tokens.get(0)? {
500 Token::RecursivePrefix => 1,
501 _ => {
502 return None;
506 }
507 };
508 if self.tokens[start..].is_empty() {
509 return None;
510 }
511 for t in self.tokens[start..].iter() {
512 match *t {
513 Token::Literal('/') => return None,
514 Token::Literal(_) => {} Token::Any | Token::ZeroOrMore => {
516 if !self.opts.literal_separator {
517 return None;
521 }
522 }
523 Token::RecursivePrefix
524 | Token::RecursiveSuffix
525 | Token::RecursiveZeroOrMore => {
526 return None;
527 }
528 Token::Class { .. } | Token::Alternates(..) => {
529 return None;
533 }
534 }
535 }
536 Some(&self.tokens[start..])
537 }
538
539 fn basename_literal(&self) -> Option<String> {
545 let tokens = self.basename_tokens()?;
546 let mut lit = String::new();
547 for t in tokens {
548 let Token::Literal(c) = *t else { return None };
549 lit.push(c);
550 }
551 Some(lit)
552 }
553}
554
555impl<'a> GlobBuilder<'a> {
556 pub fn new(glob: &'a str) -> GlobBuilder<'a> {
560 GlobBuilder { glob, opts: GlobOptions::default() }
561 }
562
563 pub fn build(&self) -> Result<Glob, Error> {
565 let mut p = Parser {
566 glob: &self.glob,
567 stack: vec![Tokens::default()],
568 chars: self.glob.chars().peekable(),
569 prev: None,
570 cur: None,
571 opts: &self.opts,
572 };
573 p.parse()?;
574 if p.stack.is_empty() {
575 Err(Error {
576 glob: Some(self.glob.to_string()),
577 kind: ErrorKind::UnopenedAlternates,
578 })
579 } else if p.stack.len() > 1 {
580 Err(Error {
581 glob: Some(self.glob.to_string()),
582 kind: ErrorKind::UnclosedAlternates,
583 })
584 } else {
585 let tokens = p.stack.pop().unwrap();
586 Ok(Glob {
587 glob: self.glob.to_string(),
588 re: tokens.to_regex_with(&self.opts),
589 opts: self.opts,
590 tokens,
591 })
592 }
593 }
594
595 pub fn case_insensitive(&mut self, yes: bool) -> &mut GlobBuilder<'a> {
599 self.opts.case_insensitive = yes;
600 self
601 }
602
603 pub fn literal_separator(&mut self, yes: bool) -> &mut GlobBuilder<'a> {
607 self.opts.literal_separator = yes;
608 self
609 }
610
611 pub fn backslash_escape(&mut self, yes: bool) -> &mut GlobBuilder<'a> {
620 self.opts.backslash_escape = yes;
621 self
622 }
623
624 pub fn empty_alternates(&mut self, yes: bool) -> &mut GlobBuilder<'a> {
631 self.opts.empty_alternates = yes;
632 self
633 }
634}
635
636impl Tokens {
637 fn to_regex_with(&self, options: &GlobOptions) -> String {
641 let mut re = String::new();
642 re.push_str("(?-u)");
643 if options.case_insensitive {
644 re.push_str("(?i)");
645 }
646 re.push('^');
647 if self.len() == 1 && self[0] == Token::RecursivePrefix {
650 re.push_str(".*");
651 re.push('$');
652 return re;
653 }
654 self.tokens_to_regex(options, &self, &mut re);
655 re.push('$');
656 re
657 }
658
659 fn tokens_to_regex(
660 &self,
661 options: &GlobOptions,
662 tokens: &[Token],
663 re: &mut String,
664 ) {
665 for tok in tokens.iter() {
666 match *tok {
667 Token::Literal(c) => {
668 re.push_str(&char_to_escaped_literal(c));
669 }
670 Token::Any => {
671 if options.literal_separator {
672 re.push_str("[^/]");
673 } else {
674 re.push_str(".");
675 }
676 }
677 Token::ZeroOrMore => {
678 if options.literal_separator {
679 re.push_str("[^/]*");
680 } else {
681 re.push_str(".*");
682 }
683 }
684 Token::RecursivePrefix => {
685 re.push_str("(?:/?|.*/)");
686 }
687 Token::RecursiveSuffix => {
688 re.push_str("/.*");
689 }
690 Token::RecursiveZeroOrMore => {
691 re.push_str("(?:/|/.*/)");
692 }
693 Token::Class { negated, ref ranges } => {
694 re.push('[');
695 if negated {
696 re.push('^');
697 }
698 for r in ranges {
699 if r.0 == r.1 {
700 re.push_str(&char_to_escaped_literal(r.0));
702 } else {
703 re.push_str(&char_to_escaped_literal(r.0));
704 re.push('-');
705 re.push_str(&char_to_escaped_literal(r.1));
706 }
707 }
708 re.push(']');
709 }
710 Token::Alternates(ref patterns) => {
711 let mut parts = vec![];
712 for pat in patterns {
713 let mut altre = String::new();
714 self.tokens_to_regex(options, &pat, &mut altre);
715 if !altre.is_empty() || options.empty_alternates {
716 parts.push(altre);
717 }
718 }
719
720 if !parts.is_empty() {
723 re.push_str("(?:");
724 re.push_str(&parts.join("|"));
725 re.push(')');
726 }
727 }
728 }
729 }
730 }
731}
732
733fn char_to_escaped_literal(c: char) -> String {
736 let mut buf = [0; 4];
737 let bytes = c.encode_utf8(&mut buf).as_bytes();
738 bytes_to_escaped_literal(bytes)
739}
740
741fn bytes_to_escaped_literal(bs: &[u8]) -> String {
744 let mut s = String::with_capacity(bs.len());
745 for &b in bs {
746 if b <= 0x7F {
747 regex_syntax::escape_into(
748 char::from(b).encode_utf8(&mut [0; 4]),
749 &mut s,
750 );
751 } else {
752 write!(&mut s, "\\x{:02x}", b).unwrap();
753 }
754 }
755 s
756}
757
758struct Parser<'a> {
759 glob: &'a str,
760 stack: Vec<Tokens>,
761 chars: std::iter::Peekable<std::str::Chars<'a>>,
762 prev: Option<char>,
763 cur: Option<char>,
764 opts: &'a GlobOptions,
765}
766
767impl<'a> Parser<'a> {
768 fn error(&self, kind: ErrorKind) -> Error {
769 Error { glob: Some(self.glob.to_string()), kind }
770 }
771
772 fn parse(&mut self) -> Result<(), Error> {
773 while let Some(c) = self.bump() {
774 match c {
775 '?' => self.push_token(Token::Any)?,
776 '*' => self.parse_star()?,
777 '[' => self.parse_class()?,
778 '{' => self.push_alternate()?,
779 '}' => self.pop_alternate()?,
780 ',' => self.parse_comma()?,
781 '\\' => self.parse_backslash()?,
782 c => self.push_token(Token::Literal(c))?,
783 }
784 }
785 Ok(())
786 }
787
788 fn push_alternate(&mut self) -> Result<(), Error> {
789 if self.stack.len() > 1 {
790 return Err(self.error(ErrorKind::NestedAlternates));
791 }
792 Ok(self.stack.push(Tokens::default()))
793 }
794
795 fn pop_alternate(&mut self) -> Result<(), Error> {
796 let mut alts = vec![];
797 while self.stack.len() >= 2 {
798 alts.push(self.stack.pop().unwrap());
799 }
800 self.push_token(Token::Alternates(alts))
801 }
802
803 fn push_token(&mut self, tok: Token) -> Result<(), Error> {
804 if let Some(ref mut pat) = self.stack.last_mut() {
805 return Ok(pat.push(tok));
806 }
807 Err(self.error(ErrorKind::UnopenedAlternates))
808 }
809
810 fn pop_token(&mut self) -> Result<Token, Error> {
811 if let Some(ref mut pat) = self.stack.last_mut() {
812 return Ok(pat.pop().unwrap());
813 }
814 Err(self.error(ErrorKind::UnopenedAlternates))
815 }
816
817 fn have_tokens(&self) -> Result<bool, Error> {
818 match self.stack.last() {
819 None => Err(self.error(ErrorKind::UnopenedAlternates)),
820 Some(ref pat) => Ok(!pat.is_empty()),
821 }
822 }
823
824 fn parse_comma(&mut self) -> Result<(), Error> {
825 if self.stack.len() <= 1 {
829 self.push_token(Token::Literal(','))
830 } else {
831 Ok(self.stack.push(Tokens::default()))
832 }
833 }
834
835 fn parse_backslash(&mut self) -> Result<(), Error> {
836 if self.opts.backslash_escape {
837 match self.bump() {
838 None => Err(self.error(ErrorKind::DanglingEscape)),
839 Some(c) => self.push_token(Token::Literal(c)),
840 }
841 } else if is_separator('\\') {
842 self.push_token(Token::Literal('/'))
844 } else {
845 self.push_token(Token::Literal('\\'))
846 }
847 }
848
849 fn parse_star(&mut self) -> Result<(), Error> {
850 let prev = self.prev;
851 if self.peek() != Some('*') {
852 self.push_token(Token::ZeroOrMore)?;
853 return Ok(());
854 }
855 assert!(self.bump() == Some('*'));
856 if !self.have_tokens()? {
857 if !self.peek().map_or(true, is_separator) {
858 self.push_token(Token::ZeroOrMore)?;
859 self.push_token(Token::ZeroOrMore)?;
860 } else {
861 self.push_token(Token::RecursivePrefix)?;
862 assert!(self.bump().map_or(true, is_separator));
863 }
864 return Ok(());
865 }
866
867 if !prev.map(is_separator).unwrap_or(false) {
868 if self.stack.len() <= 1
869 || (prev != Some(',') && prev != Some('{'))
870 {
871 self.push_token(Token::ZeroOrMore)?;
872 self.push_token(Token::ZeroOrMore)?;
873 return Ok(());
874 }
875 }
876 let is_suffix = match self.peek() {
877 None => {
878 assert!(self.bump().is_none());
879 true
880 }
881 Some(',') | Some('}') if self.stack.len() >= 2 => true,
882 Some(c) if is_separator(c) => {
883 assert!(self.bump().map(is_separator).unwrap_or(false));
884 false
885 }
886 _ => {
887 self.push_token(Token::ZeroOrMore)?;
888 self.push_token(Token::ZeroOrMore)?;
889 return Ok(());
890 }
891 };
892 match self.pop_token()? {
893 Token::RecursivePrefix => {
894 self.push_token(Token::RecursivePrefix)?;
895 }
896 Token::RecursiveSuffix => {
897 self.push_token(Token::RecursiveSuffix)?;
898 }
899 _ => {
900 if is_suffix {
901 self.push_token(Token::RecursiveSuffix)?;
902 } else {
903 self.push_token(Token::RecursiveZeroOrMore)?;
904 }
905 }
906 }
907 Ok(())
908 }
909
910 fn parse_class(&mut self) -> Result<(), Error> {
911 fn add_to_last_range(
912 glob: &str,
913 r: &mut (char, char),
914 add: char,
915 ) -> Result<(), Error> {
916 r.1 = add;
917 if r.1 < r.0 {
918 Err(Error {
919 glob: Some(glob.to_string()),
920 kind: ErrorKind::InvalidRange(r.0, r.1),
921 })
922 } else {
923 Ok(())
924 }
925 }
926 let mut ranges = vec![];
927 let negated = match self.chars.peek() {
928 Some(&'!') | Some(&'^') => {
929 let bump = self.bump();
930 assert!(bump == Some('!') || bump == Some('^'));
931 true
932 }
933 _ => false,
934 };
935 let mut first = true;
936 let mut in_range = false;
937 loop {
938 let c = match self.bump() {
939 Some(c) => c,
940 None => return Err(self.error(ErrorKind::UnclosedClass)),
943 };
944 match c {
945 ']' => {
946 if first {
947 ranges.push((']', ']'));
948 } else {
949 break;
950 }
951 }
952 '-' => {
953 if first {
954 ranges.push(('-', '-'));
955 } else if in_range {
956 let r = ranges.last_mut().unwrap();
959 add_to_last_range(&self.glob, r, '-')?;
960 in_range = false;
961 } else {
962 assert!(!ranges.is_empty());
963 in_range = true;
964 }
965 }
966 c => {
967 if in_range {
968 add_to_last_range(
971 &self.glob,
972 ranges.last_mut().unwrap(),
973 c,
974 )?;
975 } else {
976 ranges.push((c, c));
977 }
978 in_range = false;
979 }
980 }
981 first = false;
982 }
983 if in_range {
984 ranges.push(('-', '-'));
987 }
988 self.push_token(Token::Class { negated, ranges })
989 }
990
991 fn bump(&mut self) -> Option<char> {
992 self.prev = self.cur;
993 self.cur = self.chars.next();
994 self.cur
995 }
996
997 fn peek(&mut self) -> Option<char> {
998 self.chars.peek().map(|&ch| ch)
999 }
1000}
1001
1002#[cfg(test)]
1003fn starts_with(needle: &[u8], haystack: &[u8]) -> bool {
1004 needle.len() <= haystack.len() && needle == &haystack[..needle.len()]
1005}
1006
1007#[cfg(test)]
1008fn ends_with(needle: &[u8], haystack: &[u8]) -> bool {
1009 if needle.len() > haystack.len() {
1010 return false;
1011 }
1012 needle == &haystack[haystack.len() - needle.len()..]
1013}
1014
1015#[cfg(test)]
1016mod tests {
1017 use super::Token::*;
1018 use super::{Glob, GlobBuilder, Token};
1019 use crate::{ErrorKind, GlobSetBuilder};
1020
1021 #[derive(Clone, Copy, Debug, Default)]
1022 struct Options {
1023 casei: Option<bool>,
1024 litsep: Option<bool>,
1025 bsesc: Option<bool>,
1026 ealtre: Option<bool>,
1027 }
1028
1029 macro_rules! syntax {
1030 ($name:ident, $pat:expr, $tokens:expr) => {
1031 #[test]
1032 fn $name() {
1033 let pat = Glob::new($pat).unwrap();
1034 assert_eq!($tokens, pat.tokens.0);
1035 }
1036 };
1037 }
1038
1039 macro_rules! syntaxerr {
1040 ($name:ident, $pat:expr, $err:expr) => {
1041 #[test]
1042 fn $name() {
1043 let err = Glob::new($pat).unwrap_err();
1044 assert_eq!(&$err, err.kind());
1045 }
1046 };
1047 }
1048
1049 macro_rules! toregex {
1050 ($name:ident, $pat:expr, $re:expr) => {
1051 toregex!($name, $pat, $re, Options::default());
1052 };
1053 ($name:ident, $pat:expr, $re:expr, $options:expr) => {
1054 #[test]
1055 fn $name() {
1056 let mut builder = GlobBuilder::new($pat);
1057 if let Some(casei) = $options.casei {
1058 builder.case_insensitive(casei);
1059 }
1060 if let Some(litsep) = $options.litsep {
1061 builder.literal_separator(litsep);
1062 }
1063 if let Some(bsesc) = $options.bsesc {
1064 builder.backslash_escape(bsesc);
1065 }
1066 if let Some(ealtre) = $options.ealtre {
1067 builder.empty_alternates(ealtre);
1068 }
1069 let pat = builder.build().unwrap();
1070 assert_eq!(format!("(?-u){}", $re), pat.regex());
1071 }
1072 };
1073 }
1074
1075 macro_rules! matches {
1076 ($name:ident, $pat:expr, $path:expr) => {
1077 matches!($name, $pat, $path, Options::default());
1078 };
1079 ($name:ident, $pat:expr, $path:expr, $options:expr) => {
1080 #[test]
1081 fn $name() {
1082 let mut builder = GlobBuilder::new($pat);
1083 if let Some(casei) = $options.casei {
1084 builder.case_insensitive(casei);
1085 }
1086 if let Some(litsep) = $options.litsep {
1087 builder.literal_separator(litsep);
1088 }
1089 if let Some(bsesc) = $options.bsesc {
1090 builder.backslash_escape(bsesc);
1091 }
1092 if let Some(ealtre) = $options.ealtre {
1093 builder.empty_alternates(ealtre);
1094 }
1095 let pat = builder.build().unwrap();
1096 let matcher = pat.compile_matcher();
1097 let strategic = pat.compile_strategic_matcher();
1098 let set = GlobSetBuilder::new().add(pat).build().unwrap();
1099 assert!(matcher.is_match($path));
1100 assert!(strategic.is_match($path));
1101 assert!(set.is_match($path));
1102 }
1103 };
1104 }
1105
1106 macro_rules! nmatches {
1107 ($name:ident, $pat:expr, $path:expr) => {
1108 nmatches!($name, $pat, $path, Options::default());
1109 };
1110 ($name:ident, $pat:expr, $path:expr, $options:expr) => {
1111 #[test]
1112 fn $name() {
1113 let mut builder = GlobBuilder::new($pat);
1114 if let Some(casei) = $options.casei {
1115 builder.case_insensitive(casei);
1116 }
1117 if let Some(litsep) = $options.litsep {
1118 builder.literal_separator(litsep);
1119 }
1120 if let Some(bsesc) = $options.bsesc {
1121 builder.backslash_escape(bsesc);
1122 }
1123 if let Some(ealtre) = $options.ealtre {
1124 builder.empty_alternates(ealtre);
1125 }
1126 let pat = builder.build().unwrap();
1127 let matcher = pat.compile_matcher();
1128 let strategic = pat.compile_strategic_matcher();
1129 let set = GlobSetBuilder::new().add(pat).build().unwrap();
1130 assert!(!matcher.is_match($path));
1131 assert!(!strategic.is_match($path));
1132 assert!(!set.is_match($path));
1133 }
1134 };
1135 }
1136
1137 fn s(string: &str) -> String {
1138 string.to_string()
1139 }
1140
1141 fn class(s: char, e: char) -> Token {
1142 Class { negated: false, ranges: vec![(s, e)] }
1143 }
1144
1145 fn classn(s: char, e: char) -> Token {
1146 Class { negated: true, ranges: vec![(s, e)] }
1147 }
1148
1149 fn rclass(ranges: &[(char, char)]) -> Token {
1150 Class { negated: false, ranges: ranges.to_vec() }
1151 }
1152
1153 fn rclassn(ranges: &[(char, char)]) -> Token {
1154 Class { negated: true, ranges: ranges.to_vec() }
1155 }
1156
1157 syntax!(literal1, "a", vec![Literal('a')]);
1158 syntax!(literal2, "ab", vec![Literal('a'), Literal('b')]);
1159 syntax!(any1, "?", vec![Any]);
1160 syntax!(any2, "a?b", vec![Literal('a'), Any, Literal('b')]);
1161 syntax!(seq1, "*", vec![ZeroOrMore]);
1162 syntax!(seq2, "a*b", vec![Literal('a'), ZeroOrMore, Literal('b')]);
1163 syntax!(
1164 seq3,
1165 "*a*b*",
1166 vec![ZeroOrMore, Literal('a'), ZeroOrMore, Literal('b'), ZeroOrMore,]
1167 );
1168 syntax!(rseq1, "**", vec![RecursivePrefix]);
1169 syntax!(rseq2, "**/", vec![RecursivePrefix]);
1170 syntax!(rseq3, "/**", vec![RecursiveSuffix]);
1171 syntax!(rseq4, "/**/", vec![RecursiveZeroOrMore]);
1172 syntax!(
1173 rseq5,
1174 "a/**/b",
1175 vec![Literal('a'), RecursiveZeroOrMore, Literal('b'),]
1176 );
1177 syntax!(cls1, "[a]", vec![class('a', 'a')]);
1178 syntax!(cls2, "[!a]", vec![classn('a', 'a')]);
1179 syntax!(cls3, "[a-z]", vec![class('a', 'z')]);
1180 syntax!(cls4, "[!a-z]", vec![classn('a', 'z')]);
1181 syntax!(cls5, "[-]", vec![class('-', '-')]);
1182 syntax!(cls6, "[]]", vec![class(']', ']')]);
1183 syntax!(cls7, "[*]", vec![class('*', '*')]);
1184 syntax!(cls8, "[!!]", vec![classn('!', '!')]);
1185 syntax!(cls9, "[a-]", vec![rclass(&[('a', 'a'), ('-', '-')])]);
1186 syntax!(cls10, "[-a-z]", vec![rclass(&[('-', '-'), ('a', 'z')])]);
1187 syntax!(cls11, "[a-z-]", vec![rclass(&[('a', 'z'), ('-', '-')])]);
1188 syntax!(
1189 cls12,
1190 "[-a-z-]",
1191 vec![rclass(&[('-', '-'), ('a', 'z'), ('-', '-')]),]
1192 );
1193 syntax!(cls13, "[]-z]", vec![class(']', 'z')]);
1194 syntax!(cls14, "[--z]", vec![class('-', 'z')]);
1195 syntax!(cls15, "[ --]", vec![class(' ', '-')]);
1196 syntax!(cls16, "[0-9a-z]", vec![rclass(&[('0', '9'), ('a', 'z')])]);
1197 syntax!(cls17, "[a-z0-9]", vec![rclass(&[('a', 'z'), ('0', '9')])]);
1198 syntax!(cls18, "[!0-9a-z]", vec![rclassn(&[('0', '9'), ('a', 'z')])]);
1199 syntax!(cls19, "[!a-z0-9]", vec![rclassn(&[('a', 'z'), ('0', '9')])]);
1200 syntax!(cls20, "[^a]", vec![classn('a', 'a')]);
1201 syntax!(cls21, "[^a-z]", vec![classn('a', 'z')]);
1202
1203 syntaxerr!(err_unclosed1, "[", ErrorKind::UnclosedClass);
1204 syntaxerr!(err_unclosed2, "[]", ErrorKind::UnclosedClass);
1205 syntaxerr!(err_unclosed3, "[!", ErrorKind::UnclosedClass);
1206 syntaxerr!(err_unclosed4, "[!]", ErrorKind::UnclosedClass);
1207 syntaxerr!(err_range1, "[z-a]", ErrorKind::InvalidRange('z', 'a'));
1208 syntaxerr!(err_range2, "[z--]", ErrorKind::InvalidRange('z', '-'));
1209
1210 const CASEI: Options =
1211 Options { casei: Some(true), litsep: None, bsesc: None, ealtre: None };
1212 const SLASHLIT: Options =
1213 Options { casei: None, litsep: Some(true), bsesc: None, ealtre: None };
1214 const NOBSESC: Options = Options {
1215 casei: None,
1216 litsep: None,
1217 bsesc: Some(false),
1218 ealtre: None,
1219 };
1220 const BSESC: Options =
1221 Options { casei: None, litsep: None, bsesc: Some(true), ealtre: None };
1222 const EALTRE: Options = Options {
1223 casei: None,
1224 litsep: None,
1225 bsesc: Some(true),
1226 ealtre: Some(true),
1227 };
1228
1229 toregex!(re_casei, "a", "(?i)^a$", &CASEI);
1230
1231 toregex!(re_slash1, "?", r"^[^/]$", SLASHLIT);
1232 toregex!(re_slash2, "*", r"^[^/]*$", SLASHLIT);
1233
1234 toregex!(re1, "a", "^a$");
1235 toregex!(re2, "?", "^.$");
1236 toregex!(re3, "*", "^.*$");
1237 toregex!(re4, "a?", "^a.$");
1238 toregex!(re5, "?a", "^.a$");
1239 toregex!(re6, "a*", "^a.*$");
1240 toregex!(re7, "*a", "^.*a$");
1241 toregex!(re8, "[*]", r"^[\*]$");
1242 toregex!(re9, "[+]", r"^[\+]$");
1243 toregex!(re10, "+", r"^\+$");
1244 toregex!(re11, "☃", r"^\xe2\x98\x83$");
1245 toregex!(re12, "**", r"^.*$");
1246 toregex!(re13, "**/", r"^.*$");
1247 toregex!(re14, "**/*", r"^(?:/?|.*/).*$");
1248 toregex!(re15, "**/**", r"^.*$");
1249 toregex!(re16, "**/**/*", r"^(?:/?|.*/).*$");
1250 toregex!(re17, "**/**/**", r"^.*$");
1251 toregex!(re18, "**/**/**/*", r"^(?:/?|.*/).*$");
1252 toregex!(re19, "a/**", r"^a/.*$");
1253 toregex!(re20, "a/**/**", r"^a/.*$");
1254 toregex!(re21, "a/**/**/**", r"^a/.*$");
1255 toregex!(re22, "a/**/b", r"^a(?:/|/.*/)b$");
1256 toregex!(re23, "a/**/**/b", r"^a(?:/|/.*/)b$");
1257 toregex!(re24, "a/**/**/**/b", r"^a(?:/|/.*/)b$");
1258 toregex!(re25, "**/b", r"^(?:/?|.*/)b$");
1259 toregex!(re26, "**/**/b", r"^(?:/?|.*/)b$");
1260 toregex!(re27, "**/**/**/b", r"^(?:/?|.*/)b$");
1261 toregex!(re28, "a**", r"^a.*.*$");
1262 toregex!(re29, "**a", r"^.*.*a$");
1263 toregex!(re30, "a**b", r"^a.*.*b$");
1264 toregex!(re31, "***", r"^.*.*.*$");
1265 toregex!(re32, "/a**", r"^/a.*.*$");
1266 toregex!(re33, "/**a", r"^/.*.*a$");
1267 toregex!(re34, "/a**b", r"^/a.*.*b$");
1268 toregex!(re35, "{a,b}", r"^(?:b|a)$");
1269
1270 matches!(match1, "a", "a");
1271 matches!(match2, "a*b", "a_b");
1272 matches!(match3, "a*b*c", "abc");
1273 matches!(match4, "a*b*c", "a_b_c");
1274 matches!(match5, "a*b*c", "a___b___c");
1275 matches!(match6, "abc*abc*abc", "abcabcabcabcabcabcabc");
1276 matches!(match7, "a*a*a*a*a*a*a*a*a", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
1277 matches!(match8, "a*b[xyz]c*d", "abxcdbxcddd");
1278 matches!(match9, "*.rs", ".rs");
1279 matches!(match10, "☃", "☃");
1280
1281 matches!(matchrec1, "some/**/needle.txt", "some/needle.txt");
1282 matches!(matchrec2, "some/**/needle.txt", "some/one/needle.txt");
1283 matches!(matchrec3, "some/**/needle.txt", "some/one/two/needle.txt");
1284 matches!(matchrec4, "some/**/needle.txt", "some/other/needle.txt");
1285 matches!(matchrec5, "**", "abcde");
1286 matches!(matchrec6, "**", "");
1287 matches!(matchrec7, "**", ".asdf");
1288 matches!(matchrec8, "**", "/x/.asdf");
1289 matches!(matchrec9, "some/**/**/needle.txt", "some/needle.txt");
1290 matches!(matchrec10, "some/**/**/needle.txt", "some/one/needle.txt");
1291 matches!(matchrec11, "some/**/**/needle.txt", "some/one/two/needle.txt");
1292 matches!(matchrec12, "some/**/**/needle.txt", "some/other/needle.txt");
1293 matches!(matchrec13, "**/test", "one/two/test");
1294 matches!(matchrec14, "**/test", "one/test");
1295 matches!(matchrec15, "**/test", "test");
1296 matches!(matchrec16, "/**/test", "/one/two/test");
1297 matches!(matchrec17, "/**/test", "/one/test");
1298 matches!(matchrec18, "/**/test", "/test");
1299 matches!(matchrec19, "**/.*", ".abc");
1300 matches!(matchrec20, "**/.*", "abc/.abc");
1301 matches!(matchrec21, "**/foo/bar", "foo/bar");
1302 matches!(matchrec22, ".*/**", ".abc/abc");
1303 matches!(matchrec23, "test/**", "test/");
1304 matches!(matchrec24, "test/**", "test/one");
1305 matches!(matchrec25, "test/**", "test/one/two");
1306 matches!(matchrec26, "some/*/needle.txt", "some/one/needle.txt");
1307
1308 matches!(matchrange1, "a[0-9]b", "a0b");
1309 matches!(matchrange2, "a[0-9]b", "a9b");
1310 matches!(matchrange3, "a[!0-9]b", "a_b");
1311 matches!(matchrange4, "[a-z123]", "1");
1312 matches!(matchrange5, "[1a-z23]", "1");
1313 matches!(matchrange6, "[123a-z]", "1");
1314 matches!(matchrange7, "[abc-]", "-");
1315 matches!(matchrange8, "[-abc]", "-");
1316 matches!(matchrange9, "[-a-c]", "b");
1317 matches!(matchrange10, "[a-c-]", "b");
1318 matches!(matchrange11, "[-]", "-");
1319 matches!(matchrange12, "a[^0-9]b", "a_b");
1320
1321 matches!(matchpat1, "*hello.txt", "hello.txt");
1322 matches!(matchpat2, "*hello.txt", "gareth_says_hello.txt");
1323 matches!(matchpat3, "*hello.txt", "some/path/to/hello.txt");
1324 matches!(matchpat4, "*hello.txt", "some\\path\\to\\hello.txt");
1325 matches!(matchpat5, "*hello.txt", "/an/absolute/path/to/hello.txt");
1326 matches!(matchpat6, "*some/path/to/hello.txt", "some/path/to/hello.txt");
1327 matches!(
1328 matchpat7,
1329 "*some/path/to/hello.txt",
1330 "a/bigger/some/path/to/hello.txt"
1331 );
1332
1333 matches!(matchescape, "_[[]_[]]_[?]_[*]_!_", "_[_]_?_*_!_");
1334
1335 matches!(matchcasei1, "aBcDeFg", "aBcDeFg", CASEI);
1336 matches!(matchcasei2, "aBcDeFg", "abcdefg", CASEI);
1337 matches!(matchcasei3, "aBcDeFg", "ABCDEFG", CASEI);
1338 matches!(matchcasei4, "aBcDeFg", "AbCdEfG", CASEI);
1339
1340 matches!(matchalt1, "a,b", "a,b");
1341 matches!(matchalt2, ",", ",");
1342 matches!(matchalt3, "{a,b}", "a");
1343 matches!(matchalt4, "{a,b}", "b");
1344 matches!(matchalt5, "{**/src/**,foo}", "abc/src/bar");
1345 matches!(matchalt6, "{**/src/**,foo}", "foo");
1346 matches!(matchalt7, "{[}],foo}", "}");
1347 matches!(matchalt8, "{foo}", "foo");
1348 matches!(matchalt9, "{}", "");
1349 matches!(matchalt10, "{,}", "");
1350 matches!(matchalt11, "{*.foo,*.bar,*.wat}", "test.foo");
1351 matches!(matchalt12, "{*.foo,*.bar,*.wat}", "test.bar");
1352 matches!(matchalt13, "{*.foo,*.bar,*.wat}", "test.wat");
1353 matches!(matchalt14, "foo{,.txt}", "foo.txt");
1354 nmatches!(matchalt15, "foo{,.txt}", "foo");
1355 matches!(matchalt16, "foo{,.txt}", "foo", EALTRE);
1356
1357 matches!(matchslash1, "abc/def", "abc/def", SLASHLIT);
1358 #[cfg(unix)]
1359 nmatches!(matchslash2, "abc?def", "abc/def", SLASHLIT);
1360 #[cfg(not(unix))]
1361 nmatches!(matchslash2, "abc?def", "abc\\def", SLASHLIT);
1362 nmatches!(matchslash3, "abc*def", "abc/def", SLASHLIT);
1363 matches!(matchslash4, "abc[/]def", "abc/def", SLASHLIT); #[cfg(unix)]
1365 nmatches!(matchslash5, "abc\\def", "abc/def", SLASHLIT);
1366 #[cfg(not(unix))]
1367 matches!(matchslash5, "abc\\def", "abc/def", SLASHLIT);
1368
1369 matches!(matchbackslash1, "\\[", "[", BSESC);
1370 matches!(matchbackslash2, "\\?", "?", BSESC);
1371 matches!(matchbackslash3, "\\*", "*", BSESC);
1372 matches!(matchbackslash4, "\\[a-z]", "\\a", NOBSESC);
1373 matches!(matchbackslash5, "\\?", "\\a", NOBSESC);
1374 matches!(matchbackslash6, "\\*", "\\\\", NOBSESC);
1375 #[cfg(unix)]
1376 matches!(matchbackslash7, "\\a", "a");
1377 #[cfg(not(unix))]
1378 matches!(matchbackslash8, "\\a", "/a");
1379
1380 nmatches!(matchnot1, "a*b*c", "abcd");
1381 nmatches!(matchnot2, "abc*abc*abc", "abcabcabcabcabcabcabca");
1382 nmatches!(matchnot3, "some/**/needle.txt", "some/other/notthis.txt");
1383 nmatches!(matchnot4, "some/**/**/needle.txt", "some/other/notthis.txt");
1384 nmatches!(matchnot5, "/**/test", "test");
1385 nmatches!(matchnot6, "/**/test", "/one/notthis");
1386 nmatches!(matchnot7, "/**/test", "/notthis");
1387 nmatches!(matchnot8, "**/.*", "ab.c");
1388 nmatches!(matchnot9, "**/.*", "abc/ab.c");
1389 nmatches!(matchnot10, ".*/**", "a.bc");
1390 nmatches!(matchnot11, ".*/**", "abc/a.bc");
1391 nmatches!(matchnot12, "a[0-9]b", "a_b");
1392 nmatches!(matchnot13, "a[!0-9]b", "a0b");
1393 nmatches!(matchnot14, "a[!0-9]b", "a9b");
1394 nmatches!(matchnot15, "[!-]", "-");
1395 nmatches!(matchnot16, "*hello.txt", "hello.txt-and-then-some");
1396 nmatches!(matchnot17, "*hello.txt", "goodbye.txt");
1397 nmatches!(
1398 matchnot18,
1399 "*some/path/to/hello.txt",
1400 "some/path/to/hello.txt-and-then-some"
1401 );
1402 nmatches!(
1403 matchnot19,
1404 "*some/path/to/hello.txt",
1405 "some/other/path/to/hello.txt"
1406 );
1407 nmatches!(matchnot20, "a", "foo/a");
1408 nmatches!(matchnot21, "./foo", "foo");
1409 nmatches!(matchnot22, "**/foo", "foofoo");
1410 nmatches!(matchnot23, "**/foo/bar", "foofoo/bar");
1411 nmatches!(matchnot24, "/*.c", "mozilla-sha1/sha1.c");
1412 nmatches!(matchnot25, "*.c", "mozilla-sha1/sha1.c", SLASHLIT);
1413 nmatches!(
1414 matchnot26,
1415 "**/m4/ltoptions.m4",
1416 "csharp/src/packages/repositories.config",
1417 SLASHLIT
1418 );
1419 nmatches!(matchnot27, "a[^0-9]b", "a0b");
1420 nmatches!(matchnot28, "a[^0-9]b", "a9b");
1421 nmatches!(matchnot29, "[^-]", "-");
1422 nmatches!(matchnot30, "some/*/needle.txt", "some/needle.txt");
1423 nmatches!(
1424 matchrec31,
1425 "some/*/needle.txt",
1426 "some/one/two/needle.txt",
1427 SLASHLIT
1428 );
1429 nmatches!(
1430 matchrec32,
1431 "some/*/needle.txt",
1432 "some/one/two/three/needle.txt",
1433 SLASHLIT
1434 );
1435 nmatches!(matchrec33, ".*/**", ".abc");
1436 nmatches!(matchrec34, "foo/**", "foo");
1437
1438 macro_rules! extract {
1439 ($which:ident, $name:ident, $pat:expr, $expect:expr) => {
1440 extract!($which, $name, $pat, $expect, Options::default());
1441 };
1442 ($which:ident, $name:ident, $pat:expr, $expect:expr, $options:expr) => {
1443 #[test]
1444 fn $name() {
1445 let mut builder = GlobBuilder::new($pat);
1446 if let Some(casei) = $options.casei {
1447 builder.case_insensitive(casei);
1448 }
1449 if let Some(litsep) = $options.litsep {
1450 builder.literal_separator(litsep);
1451 }
1452 if let Some(bsesc) = $options.bsesc {
1453 builder.backslash_escape(bsesc);
1454 }
1455 if let Some(ealtre) = $options.ealtre {
1456 builder.empty_alternates(ealtre);
1457 }
1458 let pat = builder.build().unwrap();
1459 assert_eq!($expect, pat.$which());
1460 }
1461 };
1462 }
1463
1464 macro_rules! literal {
1465 ($($tt:tt)*) => { extract!(literal, $($tt)*); }
1466 }
1467
1468 macro_rules! basetokens {
1469 ($($tt:tt)*) => { extract!(basename_tokens, $($tt)*); }
1470 }
1471
1472 macro_rules! ext {
1473 ($($tt:tt)*) => { extract!(ext, $($tt)*); }
1474 }
1475
1476 macro_rules! required_ext {
1477 ($($tt:tt)*) => { extract!(required_ext, $($tt)*); }
1478 }
1479
1480 macro_rules! prefix {
1481 ($($tt:tt)*) => { extract!(prefix, $($tt)*); }
1482 }
1483
1484 macro_rules! suffix {
1485 ($($tt:tt)*) => { extract!(suffix, $($tt)*); }
1486 }
1487
1488 macro_rules! baseliteral {
1489 ($($tt:tt)*) => { extract!(basename_literal, $($tt)*); }
1490 }
1491
1492 literal!(extract_lit1, "foo", Some(s("foo")));
1493 literal!(extract_lit2, "foo", None, CASEI);
1494 literal!(extract_lit3, "/foo", Some(s("/foo")));
1495 literal!(extract_lit4, "/foo/", Some(s("/foo/")));
1496 literal!(extract_lit5, "/foo/bar", Some(s("/foo/bar")));
1497 literal!(extract_lit6, "*.foo", None);
1498 literal!(extract_lit7, "foo/bar", Some(s("foo/bar")));
1499 literal!(extract_lit8, "**/foo/bar", None);
1500
1501 basetokens!(
1502 extract_basetoks1,
1503 "**/foo",
1504 Some(&*vec![Literal('f'), Literal('o'), Literal('o'),])
1505 );
1506 basetokens!(extract_basetoks2, "**/foo", None, CASEI);
1507 basetokens!(
1508 extract_basetoks3,
1509 "**/foo",
1510 Some(&*vec![Literal('f'), Literal('o'), Literal('o'),]),
1511 SLASHLIT
1512 );
1513 basetokens!(extract_basetoks4, "*foo", None, SLASHLIT);
1514 basetokens!(extract_basetoks5, "*foo", None);
1515 basetokens!(extract_basetoks6, "**/fo*o", None);
1516 basetokens!(
1517 extract_basetoks7,
1518 "**/fo*o",
1519 Some(&*vec![Literal('f'), Literal('o'), ZeroOrMore, Literal('o'),]),
1520 SLASHLIT
1521 );
1522
1523 ext!(extract_ext1, "**/*.rs", Some(s(".rs")));
1524 ext!(extract_ext2, "**/*.rs.bak", None);
1525 ext!(extract_ext3, "*.rs", Some(s(".rs")));
1526 ext!(extract_ext4, "a*.rs", None);
1527 ext!(extract_ext5, "/*.c", None);
1528 ext!(extract_ext6, "*.c", None, SLASHLIT);
1529 ext!(extract_ext7, "*.c", Some(s(".c")));
1530
1531 required_ext!(extract_req_ext1, "*.rs", Some(s(".rs")));
1532 required_ext!(extract_req_ext2, "/foo/bar/*.rs", Some(s(".rs")));
1533 required_ext!(extract_req_ext3, "/foo/bar/*.rs", Some(s(".rs")));
1534 required_ext!(extract_req_ext4, "/foo/bar/.rs", Some(s(".rs")));
1535 required_ext!(extract_req_ext5, ".rs", Some(s(".rs")));
1536 required_ext!(extract_req_ext6, "./rs", None);
1537 required_ext!(extract_req_ext7, "foo", None);
1538 required_ext!(extract_req_ext8, ".foo/", None);
1539 required_ext!(extract_req_ext9, "foo/", None);
1540
1541 prefix!(extract_prefix1, "/foo", Some(s("/foo")));
1542 prefix!(extract_prefix2, "/foo/*", Some(s("/foo/")));
1543 prefix!(extract_prefix3, "**/foo", None);
1544 prefix!(extract_prefix4, "foo/**", Some(s("foo/")));
1545
1546 suffix!(extract_suffix1, "**/foo/bar", Some((s("/foo/bar"), true)));
1547 suffix!(extract_suffix2, "*/foo/bar", Some((s("/foo/bar"), false)));
1548 suffix!(extract_suffix3, "*/foo/bar", None, SLASHLIT);
1549 suffix!(extract_suffix4, "foo/bar", Some((s("foo/bar"), false)));
1550 suffix!(extract_suffix5, "*.foo", Some((s(".foo"), false)));
1551 suffix!(extract_suffix6, "*.foo", None, SLASHLIT);
1552 suffix!(extract_suffix7, "**/*_test", Some((s("_test"), false)));
1553
1554 baseliteral!(extract_baselit1, "**/foo", Some(s("foo")));
1555 baseliteral!(extract_baselit2, "foo", None);
1556 baseliteral!(extract_baselit3, "*foo", None);
1557 baseliteral!(extract_baselit4, "*/foo", None);
1558}