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