1#![doc(
59 html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
60 html_favicon_url = "https://www.rust-lang.org/favicon.ico",
61 html_root_url = "https://docs.rs/glob/0.3.1"
62)]
63#![deny(missing_docs)]
64#![allow(clippy::while_let_loop)]
65
66#[cfg(test)]
67#[macro_use]
68extern crate doc_comment;
69
70#[cfg(test)]
71doctest!("../README.md");
72
73use std::cmp;
74use std::cmp::Ordering;
75use std::error::Error;
76use std::fmt;
77use std::fs;
78use std::fs::DirEntry;
79use std::io;
80use std::ops::Deref;
81use std::path::{self, Component, Path, PathBuf};
82use std::str::FromStr;
83
84use CharSpecifier::{CharRange, SingleChar};
85use MatchResult::{EntirePatternDoesntMatch, Match, SubPatternDoesntMatch};
86use PatternToken::AnyExcept;
87use PatternToken::{AnyChar, AnyRecursiveSequence, AnySequence, AnyWithin, Char};
88
89#[derive(Debug)]
99pub struct Paths {
100 dir_patterns: Vec<Pattern>,
101 require_dir: bool,
102 options: MatchOptions,
103 todo: Vec<Result<(PathWrapper, usize), GlobError>>,
104 scope: Option<PathWrapper>,
105}
106
107pub fn glob(pattern: &str) -> Result<Paths, PatternError> {
165 glob_with(pattern, MatchOptions::new())
166}
167
168pub fn glob_with(pattern: &str, options: MatchOptions) -> Result<Paths, PatternError> {
182 #[cfg(windows)]
183 fn check_windows_verbatim(p: &Path) -> bool {
184 match p.components().next() {
185 Some(Component::Prefix(ref p)) => {
186 p.kind().is_verbatim()
188 && if let std::path::Prefix::VerbatimDisk(_) = p.kind() {
189 false
190 } else {
191 true
192 }
193 }
194 _ => false,
195 }
196 }
197 #[cfg(not(windows))]
198 fn check_windows_verbatim(_: &Path) -> bool {
199 false
200 }
201
202 #[cfg(windows)]
203 fn to_scope(p: &Path) -> PathBuf {
204 p.to_path_buf()
206 }
207 #[cfg(not(windows))]
208 fn to_scope(p: &Path) -> PathBuf {
209 p.to_path_buf()
210 }
211
212 let _ = Pattern::new(pattern)?;
214
215 let mut components = Path::new(pattern).components().peekable();
216 loop {
217 match components.peek() {
218 Some(&Component::Prefix(..)) | Some(&Component::RootDir) => {
219 components.next();
220 }
221 _ => break,
222 }
223 }
224 let rest = components.map(|s| s.as_os_str()).collect::<PathBuf>();
225 let normalized_pattern = Path::new(pattern).iter().collect::<PathBuf>();
226 let root_len = normalized_pattern.to_str().unwrap().len() - rest.to_str().unwrap().len();
227 let root = if root_len > 0 {
228 Some(Path::new(&pattern[..root_len]))
229 } else {
230 None
231 };
232
233 if root_len > 0 && check_windows_verbatim(root.unwrap()) {
234 return Ok(Paths {
238 dir_patterns: Vec::new(),
239 require_dir: false,
240 options,
241 todo: Vec::new(),
242 scope: None,
243 });
244 }
245
246 let scope = root.map_or_else(|| PathBuf::from("."), to_scope);
247 let scope = PathWrapper::from_path(scope);
248
249 let mut dir_patterns = Vec::new();
250 let components =
251 pattern[cmp::min(root_len, pattern.len())..].split_terminator(path::is_separator);
252
253 for component in components {
254 dir_patterns.push(Pattern::new(component)?);
255 }
256
257 if root_len == pattern.len() {
258 dir_patterns.push(Pattern {
259 original: "".to_string(),
260 tokens: Vec::new(),
261 is_recursive: false,
262 has_metachars: false,
263 });
264 }
265
266 let last_is_separator = pattern.chars().next_back().map(path::is_separator);
267 let require_dir = last_is_separator == Some(true);
268 let todo = Vec::new();
269
270 Ok(Paths {
271 dir_patterns,
272 require_dir,
273 options,
274 todo,
275 scope: Some(scope),
276 })
277}
278
279#[derive(Debug)]
285pub struct GlobError {
286 path: PathBuf,
287 error: io::Error,
288}
289
290impl GlobError {
291 pub fn path(&self) -> &Path {
293 &self.path
294 }
295
296 pub fn error(&self) -> &io::Error {
298 &self.error
299 }
300
301 pub fn into_error(self) -> io::Error {
303 self.error
304 }
305}
306
307impl Error for GlobError {
308 #[allow(deprecated)]
309 fn description(&self) -> &str {
310 self.error.description()
311 }
312
313 #[allow(unknown_lints, bare_trait_objects)]
314 fn cause(&self) -> Option<&Error> {
315 Some(&self.error)
316 }
317}
318
319impl fmt::Display for GlobError {
320 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
321 write!(
322 f,
323 "attempting to read `{}` resulted in an error: {}",
324 self.path.display(),
325 self.error
326 )
327 }
328}
329
330#[derive(Debug)]
331struct PathWrapper {
332 path: PathBuf,
333 is_directory: bool,
334}
335
336impl PathWrapper {
337 fn from_dir_entry(path: PathBuf, e: DirEntry) -> Self {
338 let is_directory = e
339 .file_type()
340 .ok()
341 .and_then(|file_type| {
342 if file_type.is_symlink() {
345 None
346 } else {
347 Some(file_type.is_dir())
348 }
349 })
350 .or_else(|| fs::metadata(&path).map(|m| m.is_dir()).ok())
351 .unwrap_or(false);
352 Self { path, is_directory }
353 }
354 fn from_path(path: PathBuf) -> Self {
355 let is_directory = fs::metadata(&path).map(|m| m.is_dir()).unwrap_or(false);
356 Self { path, is_directory }
357 }
358
359 fn into_path(self) -> PathBuf {
360 self.path
361 }
362}
363
364impl Deref for PathWrapper {
365 type Target = Path;
366
367 fn deref(&self) -> &Self::Target {
368 self.path.deref()
369 }
370}
371
372impl AsRef<Path> for PathWrapper {
373 fn as_ref(&self) -> &Path {
374 self.path.as_ref()
375 }
376}
377
378pub type GlobResult = Result<PathBuf, GlobError>;
383
384impl Iterator for Paths {
385 type Item = GlobResult;
386
387 fn next(&mut self) -> Option<GlobResult> {
388 if let Some(scope) = self.scope.take() {
393 if !self.dir_patterns.is_empty() {
394 assert!(self.dir_patterns.len() < usize::MAX);
396
397 fill_todo(&mut self.todo, &self.dir_patterns, 0, &scope, self.options);
398 }
399 }
400
401 loop {
402 if self.dir_patterns.is_empty() || self.todo.is_empty() {
403 return None;
404 }
405
406 let (path, mut idx) = match self.todo.pop().unwrap() {
407 Ok(pair) => pair,
408 Err(e) => return Some(Err(e)),
409 };
410
411 if idx == usize::MAX {
414 if self.require_dir && !path.is_directory {
415 continue;
416 }
417 return Some(Ok(path.into_path()));
418 }
419
420 if self.dir_patterns[idx].is_recursive {
421 let mut next = idx;
422
423 while (next + 1) < self.dir_patterns.len()
425 && self.dir_patterns[next + 1].is_recursive
426 {
427 next += 1;
428 }
429
430 if path.is_directory {
431 fill_todo(
435 &mut self.todo,
436 &self.dir_patterns,
437 next,
438 &path,
439 self.options,
440 );
441
442 if next == self.dir_patterns.len() - 1 {
443 return Some(Ok(path.into_path()));
446 } else {
447 idx = next + 1;
449 }
450 } else if next == self.dir_patterns.len() - 1 {
451 continue;
454 } else {
455 idx = next + 1;
457 }
458 }
459
460 if self.dir_patterns[idx].matches_with(
462 {
463 match path.file_name().and_then(|s| s.to_str()) {
464 None => continue,
468 Some(x) => x,
469 }
470 },
471 self.options,
472 ) {
473 if idx == self.dir_patterns.len() - 1 {
474 if !self.require_dir || path.is_directory {
479 return Some(Ok(path.into_path()));
480 }
481 } else {
482 fill_todo(
483 &mut self.todo,
484 &self.dir_patterns,
485 idx + 1,
486 &path,
487 self.options,
488 );
489 }
490 }
491 }
492 }
493}
494
495#[derive(Debug)]
497#[allow(missing_copy_implementations)]
498pub struct PatternError {
499 pub pos: usize,
501
502 pub msg: &'static str,
504}
505
506impl Error for PatternError {
507 fn description(&self) -> &str {
508 self.msg
509 }
510}
511
512impl fmt::Display for PatternError {
513 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
514 write!(
515 f,
516 "Pattern syntax error near position {}: {}",
517 self.pos, self.msg
518 )
519 }
520}
521
522#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
552pub struct Pattern {
553 original: String,
554 tokens: Vec<PatternToken>,
555 is_recursive: bool,
556 has_metachars: bool,
559}
560
561impl fmt::Display for Pattern {
563 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
564 self.original.fmt(f)
565 }
566}
567
568impl FromStr for Pattern {
569 type Err = PatternError;
570
571 fn from_str(s: &str) -> Result<Self, PatternError> {
572 Self::new(s)
573 }
574}
575
576#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
577enum PatternToken {
578 Char(char),
579 AnyChar,
580 AnySequence,
581 AnyRecursiveSequence,
582 AnyWithin(Vec<CharSpecifier>),
583 AnyExcept(Vec<CharSpecifier>),
584}
585
586#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
587enum CharSpecifier {
588 SingleChar(char),
589 CharRange(char, char),
590}
591
592#[derive(Copy, Clone, PartialEq)]
593enum MatchResult {
594 Match,
595 SubPatternDoesntMatch,
596 EntirePatternDoesntMatch,
597}
598
599const ERROR_WILDCARDS: &str = "wildcards are either regular `*` or recursive `**`";
600const ERROR_RECURSIVE_WILDCARDS: &str = "recursive wildcards must form a single path \
601 component";
602const ERROR_INVALID_RANGE: &str = "invalid range pattern";
603
604impl Pattern {
605 pub fn new(pattern: &str) -> Result<Self, PatternError> {
609 let chars = pattern.chars().collect::<Vec<_>>();
610 let mut tokens = Vec::new();
611 let mut is_recursive = false;
612 let mut has_metachars = false;
613 let mut i = 0;
614
615 while i < chars.len() {
616 match chars[i] {
617 '?' => {
618 has_metachars = true;
619 tokens.push(AnyChar);
620 i += 1;
621 }
622 '*' => {
623 has_metachars = true;
624
625 let old = i;
626
627 while i < chars.len() && chars[i] == '*' {
628 i += 1;
629 }
630
631 let count = i - old;
632
633 match count.cmp(&2) {
634 Ordering::Greater => {
635 return Err(PatternError {
636 pos: old + 2,
637 msg: ERROR_WILDCARDS,
638 })
639 }
640 Ordering::Equal => {
641 let is_valid = if i == 2 || path::is_separator(chars[i - count - 1]) {
645 if i < chars.len() && path::is_separator(chars[i]) {
647 i += 1;
648 true
649 } else if i == chars.len() {
652 true
653 } else {
655 return Err(PatternError {
656 pos: i,
657 msg: ERROR_RECURSIVE_WILDCARDS,
658 });
659 }
660 } else {
662 return Err(PatternError {
663 pos: old - 1,
664 msg: ERROR_RECURSIVE_WILDCARDS,
665 });
666 };
667
668 if is_valid {
669 let tokens_len = tokens.len();
673
674 if !(tokens_len > 1
675 && tokens[tokens_len - 1] == AnyRecursiveSequence)
676 {
677 is_recursive = true;
678 tokens.push(AnyRecursiveSequence);
679 }
680 }
681 }
682 Ordering::Less => tokens.push(AnySequence),
683 }
684 }
685 '[' => {
686 has_metachars = true;
687
688 if i + 4 <= chars.len() && chars[i + 1] == '!' {
689 match chars[i + 3..].iter().position(|x| *x == ']') {
690 None => (),
691 Some(j) => {
692 let chars = &chars[i + 2..i + 3 + j];
693 let cs = parse_char_specifiers(chars);
694 tokens.push(AnyExcept(cs));
695 i += j + 4;
696 continue;
697 }
698 }
699 } else if i + 3 <= chars.len() && chars[i + 1] != '!' {
700 match chars[i + 2..].iter().position(|x| *x == ']') {
701 None => (),
702 Some(j) => {
703 let cs = parse_char_specifiers(&chars[i + 1..i + 2 + j]);
704 tokens.push(AnyWithin(cs));
705 i += j + 3;
706 continue;
707 }
708 }
709 }
710
711 return Err(PatternError {
713 pos: i,
714 msg: ERROR_INVALID_RANGE,
715 });
716 }
717 c => {
718 tokens.push(Char(c));
719 i += 1;
720 }
721 }
722 }
723
724 Ok(Self {
725 tokens,
726 original: pattern.to_string(),
727 is_recursive,
728 has_metachars,
729 })
730 }
731
732 pub fn escape(s: &str) -> String {
736 let mut escaped = String::new();
737 for c in s.chars() {
738 match c {
739 '?' | '*' | '[' | ']' => {
742 escaped.push('[');
743 escaped.push(c);
744 escaped.push(']');
745 }
746 c => {
747 escaped.push(c);
748 }
749 }
750 }
751 escaped
752 }
753
754 pub fn matches(&self, str: &str) -> bool {
767 self.matches_with(str, MatchOptions::new())
768 }
769
770 pub fn matches_path(&self, path: &Path) -> bool {
773 path.to_str().map_or(false, |s| self.matches(s))
775 }
776
777 pub fn matches_with(&self, str: &str, options: MatchOptions) -> bool {
780 self.matches_from(true, str.chars(), 0, options) == Match
781 }
782
783 pub fn matches_path_with(&self, path: &Path, options: MatchOptions) -> bool {
786 path.to_str()
788 .map_or(false, |s| self.matches_with(s, options))
789 }
790
791 pub fn as_str(&self) -> &str {
793 &self.original
794 }
795
796 fn matches_from(
797 &self,
798 mut follows_separator: bool,
799 mut file: std::str::Chars,
800 i: usize,
801 options: MatchOptions,
802 ) -> MatchResult {
803 for (ti, token) in self.tokens[i..].iter().enumerate() {
804 match *token {
805 AnySequence | AnyRecursiveSequence => {
806 debug_assert!(match *token {
808 AnyRecursiveSequence => follows_separator,
809 _ => true,
810 });
811
812 match self.matches_from(follows_separator, file.clone(), i + ti + 1, options) {
814 SubPatternDoesntMatch => (), m => return m,
816 };
817
818 while let Some(c) = file.next() {
819 if follows_separator && options.require_literal_leading_dot && c == '.' {
820 return SubPatternDoesntMatch;
821 }
822 follows_separator = path::is_separator(c);
823 match *token {
824 AnyRecursiveSequence if !follows_separator => continue,
825 AnySequence
826 if options.require_literal_separator && follows_separator =>
827 {
828 return SubPatternDoesntMatch
829 }
830 _ => (),
831 }
832 match self.matches_from(
833 follows_separator,
834 file.clone(),
835 i + ti + 1,
836 options,
837 ) {
838 SubPatternDoesntMatch => (), m => return m,
840 }
841 }
842 }
843 _ => {
844 let c = match file.next() {
845 Some(c) => c,
846 None => return EntirePatternDoesntMatch,
847 };
848
849 let is_sep = path::is_separator(c);
850
851 if !match *token {
852 AnyChar | AnyWithin(..) | AnyExcept(..)
853 if (options.require_literal_separator && is_sep)
854 || (follows_separator
855 && options.require_literal_leading_dot
856 && c == '.') =>
857 {
858 false
859 }
860 AnyChar => true,
861 AnyWithin(ref specifiers) => in_char_specifiers(specifiers, c, options),
862 AnyExcept(ref specifiers) => !in_char_specifiers(specifiers, c, options),
863 Char(c2) => chars_eq(c, c2, options.case_sensitive),
864 AnySequence | AnyRecursiveSequence => unreachable!(),
865 } {
866 return SubPatternDoesntMatch;
867 }
868 follows_separator = is_sep;
869 }
870 }
871 }
872
873 if file.next().is_none() {
875 Match
876 } else {
877 SubPatternDoesntMatch
878 }
879 }
880}
881
882fn fill_todo(
886 todo: &mut Vec<Result<(PathWrapper, usize), GlobError>>,
887 patterns: &[Pattern],
888 idx: usize,
889 path: &PathWrapper,
890 options: MatchOptions,
891) {
892 let add = |todo: &mut Vec<_>, next_path: PathWrapper| {
893 if idx + 1 == patterns.len() {
894 todo.push(Ok((next_path, usize::MAX)));
898 } else {
899 fill_todo(todo, patterns, idx + 1, &next_path, options);
900 }
901 };
902
903 let pattern = &patterns[idx];
904 let is_dir = path.is_directory;
905 let curdir = path.as_ref() == Path::new(".");
906 match (pattern.has_metachars, is_dir) {
907 (false, _) => {
908 debug_assert!(
909 pattern
910 .tokens
911 .iter()
912 .all(|tok| matches!(tok, PatternToken::Char(_))),
913 "broken invariant: pattern has metachars but shouldn't"
914 );
915 let s = pattern.as_str();
916
917 let special = "." == s || ".." == s;
923 let next_path = if curdir {
924 PathBuf::from(s)
925 } else {
926 path.join(s)
927 };
928 let next_path = PathWrapper::from_path(next_path);
929 if (special && is_dir)
930 || (!special
931 && (fs::metadata(&next_path).is_ok()
932 || fs::symlink_metadata(&next_path).is_ok()))
933 {
934 add(todo, next_path);
935 }
936 }
937 (true, true) => {
938 let dirs = fs::read_dir(path).and_then(|d| {
939 d.map(|e| {
940 e.map(|e| {
941 let path = if curdir {
942 PathBuf::from(e.path().file_name().unwrap())
943 } else {
944 e.path()
945 };
946 PathWrapper::from_dir_entry(path, e)
947 })
948 })
949 .collect::<Result<Vec<_>, _>>()
950 });
951 match dirs {
952 Ok(mut children) => {
953 if options.require_literal_leading_dot {
954 children
955 .retain(|x| !x.file_name().unwrap().to_str().unwrap().starts_with('.'));
956 }
957 children.sort_by(|p1, p2| p2.file_name().cmp(&p1.file_name()));
958 todo.extend(children.into_iter().map(|x| Ok((x, idx))));
959
960 if !pattern.tokens.is_empty() && pattern.tokens[0] == Char('.') {
966 for &special in &[".", ".."] {
967 if pattern.matches_with(special, options) {
968 add(todo, PathWrapper::from_path(path.join(special)));
969 }
970 }
971 }
972 }
973 Err(e) => {
974 todo.push(Err(GlobError {
975 path: path.to_path_buf(),
976 error: e,
977 }));
978 }
979 }
980 }
981 (true, false) => {
982 }
984 }
985}
986
987fn parse_char_specifiers(s: &[char]) -> Vec<CharSpecifier> {
988 let mut cs = Vec::new();
989 let mut i = 0;
990 while i < s.len() {
991 if i + 3 <= s.len() && s[i + 1] == '-' {
992 cs.push(CharRange(s[i], s[i + 2]));
993 i += 3;
994 } else {
995 cs.push(SingleChar(s[i]));
996 i += 1;
997 }
998 }
999 cs
1000}
1001
1002fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: MatchOptions) -> bool {
1003 for &specifier in specifiers.iter() {
1004 match specifier {
1005 SingleChar(sc) => {
1006 if chars_eq(c, sc, options.case_sensitive) {
1007 return true;
1008 }
1009 }
1010 CharRange(start, end) => {
1011 if !options.case_sensitive && c.is_ascii() && start.is_ascii() && end.is_ascii() {
1013 let start = start.to_ascii_lowercase();
1014 let end = end.to_ascii_lowercase();
1015
1016 let start_up = start.to_uppercase().next().unwrap();
1017 let end_up = end.to_uppercase().next().unwrap();
1018
1019 if start != start_up && end != end_up {
1022 let c = c.to_ascii_lowercase();
1023 if c >= start && c <= end {
1024 return true;
1025 }
1026 }
1027 }
1028
1029 if c >= start && c <= end {
1030 return true;
1031 }
1032 }
1033 }
1034 }
1035
1036 false
1037}
1038
1039fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool {
1041 if cfg!(windows) && path::is_separator(a) && path::is_separator(b) {
1042 true
1043 } else if !case_sensitive && a.is_ascii() && b.is_ascii() {
1044 a.eq_ignore_ascii_case(&b)
1046 } else {
1047 a == b
1048 }
1049}
1050
1051#[allow(missing_copy_implementations)]
1053#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
1054pub struct MatchOptions {
1055 pub case_sensitive: bool,
1060
1061 pub require_literal_separator: bool,
1065
1066 pub require_literal_leading_dot: bool,
1072}
1073
1074impl MatchOptions {
1075 pub fn new() -> Self {
1094 Self {
1095 case_sensitive: true,
1096 require_literal_separator: false,
1097 require_literal_leading_dot: false,
1098 }
1099 }
1100}
1101
1102#[cfg(test)]
1103mod test {
1104 use super::{glob, MatchOptions, Pattern};
1105 use std::path::Path;
1106
1107 #[test]
1108 fn test_pattern_from_str() {
1109 assert!("a*b".parse::<Pattern>().unwrap().matches("a_b"));
1110 assert!("a/**b".parse::<Pattern>().unwrap_err().pos == 4);
1111 }
1112
1113 #[test]
1114 fn test_wildcard_errors() {
1115 assert!(Pattern::new("a/**b").unwrap_err().pos == 4);
1116 assert!(Pattern::new("a/bc**").unwrap_err().pos == 3);
1117 assert!(Pattern::new("a/*****").unwrap_err().pos == 4);
1118 assert!(Pattern::new("a/b**c**d").unwrap_err().pos == 2);
1119 assert!(Pattern::new("a**b").unwrap_err().pos == 0);
1120 }
1121
1122 #[test]
1123 fn test_unclosed_bracket_errors() {
1124 assert!(Pattern::new("abc[def").unwrap_err().pos == 3);
1125 assert!(Pattern::new("abc[!def").unwrap_err().pos == 3);
1126 assert!(Pattern::new("abc[").unwrap_err().pos == 3);
1127 assert!(Pattern::new("abc[!").unwrap_err().pos == 3);
1128 assert!(Pattern::new("abc[d").unwrap_err().pos == 3);
1129 assert!(Pattern::new("abc[!d").unwrap_err().pos == 3);
1130 assert!(Pattern::new("abc[]").unwrap_err().pos == 3);
1131 assert!(Pattern::new("abc[!]").unwrap_err().pos == 3);
1132 }
1133
1134 #[test]
1135 fn test_glob_errors() {
1136 assert!(glob("a/**b").err().unwrap().pos == 4);
1137 assert!(glob("abc[def").err().unwrap().pos == 3);
1138 }
1139
1140 #[cfg(all(unix, not(target_os = "macos")))]
1144 #[test]
1145 fn test_iteration_errors() {
1146 use std::io;
1147 let mut iter = glob("/root/*").unwrap();
1148
1149 let next = iter.next();
1151 assert!(next.is_some());
1152
1153 let err = next.unwrap();
1154 assert!(err.is_err());
1155
1156 let err = err.err().unwrap();
1157 assert!(err.path() == Path::new("/root"));
1158 assert!(err.error().kind() == io::ErrorKind::PermissionDenied);
1159 }
1160
1161 #[test]
1162 fn test_absolute_pattern() {
1163 assert!(glob("/").unwrap().next().is_some());
1164 assert!(glob("//").unwrap().next().is_some());
1165
1166 assert!(glob("/*").unwrap().next().is_some());
1168
1169 #[cfg(not(windows))]
1170 fn win() {}
1171
1172 #[cfg(windows)]
1173 fn win() {
1174 use std::env::current_dir;
1175 use std::path::Component;
1176
1177 let root_with_device = current_dir()
1179 .ok()
1180 .and_then(|p| match p.components().next().unwrap() {
1181 Component::Prefix(prefix_component) => {
1182 let path = Path::new(prefix_component.as_os_str()).join("*");
1183 Some(path.to_path_buf())
1184 }
1185 _ => panic!("no prefix in this path"),
1186 })
1187 .unwrap();
1188 assert!(glob(root_with_device.as_os_str().to_str().unwrap())
1190 .unwrap()
1191 .next()
1192 .is_some());
1193 }
1194 win()
1195 }
1196
1197 #[test]
1198 fn test_wildcards() {
1199 assert!(Pattern::new("a*b").unwrap().matches("a_b"));
1200 assert!(Pattern::new("a*b*c").unwrap().matches("abc"));
1201 assert!(!Pattern::new("a*b*c").unwrap().matches("abcd"));
1202 assert!(Pattern::new("a*b*c").unwrap().matches("a_b_c"));
1203 assert!(Pattern::new("a*b*c").unwrap().matches("a___b___c"));
1204 assert!(Pattern::new("abc*abc*abc")
1205 .unwrap()
1206 .matches("abcabcabcabcabcabcabc"));
1207 assert!(!Pattern::new("abc*abc*abc")
1208 .unwrap()
1209 .matches("abcabcabcabcabcabcabca"));
1210 assert!(Pattern::new("a*a*a*a*a*a*a*a*a")
1211 .unwrap()
1212 .matches("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
1213 assert!(Pattern::new("a*b[xyz]c*d").unwrap().matches("abxcdbxcddd"));
1214 }
1215
1216 #[test]
1217 fn test_recursive_wildcards() {
1218 let pat = Pattern::new("some/**/needle.txt").unwrap();
1219 assert!(pat.matches("some/needle.txt"));
1220 assert!(pat.matches("some/one/needle.txt"));
1221 assert!(pat.matches("some/one/two/needle.txt"));
1222 assert!(pat.matches("some/other/needle.txt"));
1223 assert!(!pat.matches("some/other/notthis.txt"));
1224
1225 let pat = Pattern::new("**").unwrap();
1228 assert!(pat.is_recursive);
1229 assert!(pat.matches("abcde"));
1230 assert!(pat.matches(""));
1231 assert!(pat.matches(".asdf"));
1232 assert!(pat.matches("/x/.asdf"));
1233
1234 let pat = Pattern::new("some/**/**/needle.txt").unwrap();
1236 assert!(pat.matches("some/needle.txt"));
1237 assert!(pat.matches("some/one/needle.txt"));
1238 assert!(pat.matches("some/one/two/needle.txt"));
1239 assert!(pat.matches("some/other/needle.txt"));
1240 assert!(!pat.matches("some/other/notthis.txt"));
1241
1242 let pat = Pattern::new("**/test").unwrap();
1244 assert!(pat.matches("one/two/test"));
1245 assert!(pat.matches("one/test"));
1246 assert!(pat.matches("test"));
1247
1248 let pat = Pattern::new("/**/test").unwrap();
1250 assert!(pat.matches("/one/two/test"));
1251 assert!(pat.matches("/one/test"));
1252 assert!(pat.matches("/test"));
1253 assert!(!pat.matches("/one/notthis"));
1254 assert!(!pat.matches("/notthis"));
1255
1256 let pat = Pattern::new("**/.*").unwrap();
1258 assert!(pat.matches(".abc"));
1259 assert!(pat.matches("abc/.abc"));
1260 assert!(!pat.matches("ab.c"));
1261 assert!(!pat.matches("abc/ab.c"));
1262 }
1263
1264 #[test]
1265 fn test_lots_of_files() {
1266 glob("/*/*/*/*").unwrap().skip(10000).next();
1268 }
1269
1270 #[test]
1271 fn test_range_pattern() {
1272 let pat = Pattern::new("a[0-9]b").unwrap();
1273 for i in 0..10 {
1274 assert!(pat.matches(&format!("a{}b", i)));
1275 }
1276 assert!(!pat.matches("a_b"));
1277
1278 let pat = Pattern::new("a[!0-9]b").unwrap();
1279 for i in 0..10 {
1280 assert!(!pat.matches(&format!("a{}b", i)));
1281 }
1282 assert!(pat.matches("a_b"));
1283
1284 let pats = ["[a-z123]", "[1a-z23]", "[123a-z]"];
1285 for &p in pats.iter() {
1286 let pat = Pattern::new(p).unwrap();
1287 for c in "abcdefghijklmnopqrstuvwxyz".chars() {
1288 assert!(pat.matches(&c.to_string()));
1289 }
1290 for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars() {
1291 let options = MatchOptions {
1292 case_sensitive: false,
1293 ..MatchOptions::new()
1294 };
1295 assert!(pat.matches_with(&c.to_string(), options));
1296 }
1297 assert!(pat.matches("1"));
1298 assert!(pat.matches("2"));
1299 assert!(pat.matches("3"));
1300 }
1301
1302 let pats = ["[abc-]", "[-abc]", "[a-c-]"];
1303 for &p in pats.iter() {
1304 let pat = Pattern::new(p).unwrap();
1305 assert!(pat.matches("a"));
1306 assert!(pat.matches("b"));
1307 assert!(pat.matches("c"));
1308 assert!(pat.matches("-"));
1309 assert!(!pat.matches("d"));
1310 }
1311
1312 let pat = Pattern::new("[2-1]").unwrap();
1313 assert!(!pat.matches("1"));
1314 assert!(!pat.matches("2"));
1315
1316 assert!(Pattern::new("[-]").unwrap().matches("-"));
1317 assert!(!Pattern::new("[!-]").unwrap().matches("-"));
1318 }
1319
1320 #[test]
1321 fn test_pattern_matches() {
1322 let txt_pat = Pattern::new("*hello.txt").unwrap();
1323 assert!(txt_pat.matches("hello.txt"));
1324 assert!(txt_pat.matches("gareth_says_hello.txt"));
1325 assert!(txt_pat.matches("some/path/to/hello.txt"));
1326 assert!(txt_pat.matches("some\\path\\to\\hello.txt"));
1327 assert!(txt_pat.matches("/an/absolute/path/to/hello.txt"));
1328 assert!(!txt_pat.matches("hello.txt-and-then-some"));
1329 assert!(!txt_pat.matches("goodbye.txt"));
1330
1331 let dir_pat = Pattern::new("*some/path/to/hello.txt").unwrap();
1332 assert!(dir_pat.matches("some/path/to/hello.txt"));
1333 assert!(dir_pat.matches("a/bigger/some/path/to/hello.txt"));
1334 assert!(!dir_pat.matches("some/path/to/hello.txt-and-then-some"));
1335 assert!(!dir_pat.matches("some/other/path/to/hello.txt"));
1336 }
1337
1338 #[test]
1339 fn test_pattern_escape() {
1340 let s = "_[_]_?_*_!_";
1341 assert_eq!(Pattern::escape(s), "_[[]_[]]_[?]_[*]_!_".to_string());
1342 assert!(Pattern::new(&Pattern::escape(s)).unwrap().matches(s));
1343 }
1344
1345 #[test]
1346 fn test_pattern_matches_case_insensitive() {
1347 let pat = Pattern::new("aBcDeFg").unwrap();
1348 let options = MatchOptions {
1349 case_sensitive: false,
1350 require_literal_separator: false,
1351 require_literal_leading_dot: false,
1352 };
1353
1354 assert!(pat.matches_with("aBcDeFg", options));
1355 assert!(pat.matches_with("abcdefg", options));
1356 assert!(pat.matches_with("ABCDEFG", options));
1357 assert!(pat.matches_with("AbCdEfG", options));
1358 }
1359
1360 #[test]
1361 fn test_pattern_matches_case_insensitive_range() {
1362 let pat_within = Pattern::new("[a]").unwrap();
1363 let pat_except = Pattern::new("[!a]").unwrap();
1364
1365 let options_case_insensitive = MatchOptions {
1366 case_sensitive: false,
1367 require_literal_separator: false,
1368 require_literal_leading_dot: false,
1369 };
1370 let options_case_sensitive = MatchOptions {
1371 case_sensitive: true,
1372 require_literal_separator: false,
1373 require_literal_leading_dot: false,
1374 };
1375
1376 assert!(pat_within.matches_with("a", options_case_insensitive));
1377 assert!(pat_within.matches_with("A", options_case_insensitive));
1378 assert!(!pat_within.matches_with("A", options_case_sensitive));
1379
1380 assert!(!pat_except.matches_with("a", options_case_insensitive));
1381 assert!(!pat_except.matches_with("A", options_case_insensitive));
1382 assert!(pat_except.matches_with("A", options_case_sensitive));
1383 }
1384
1385 #[test]
1386 fn test_pattern_matches_require_literal_separator() {
1387 let options_require_literal = MatchOptions {
1388 case_sensitive: true,
1389 require_literal_separator: true,
1390 require_literal_leading_dot: false,
1391 };
1392 let options_not_require_literal = MatchOptions {
1393 case_sensitive: true,
1394 require_literal_separator: false,
1395 require_literal_leading_dot: false,
1396 };
1397
1398 assert!(Pattern::new("abc/def")
1399 .unwrap()
1400 .matches_with("abc/def", options_require_literal));
1401 assert!(!Pattern::new("abc?def")
1402 .unwrap()
1403 .matches_with("abc/def", options_require_literal));
1404 assert!(!Pattern::new("abc*def")
1405 .unwrap()
1406 .matches_with("abc/def", options_require_literal));
1407 assert!(!Pattern::new("abc[/]def")
1408 .unwrap()
1409 .matches_with("abc/def", options_require_literal));
1410
1411 assert!(Pattern::new("abc/def")
1412 .unwrap()
1413 .matches_with("abc/def", options_not_require_literal));
1414 assert!(Pattern::new("abc?def")
1415 .unwrap()
1416 .matches_with("abc/def", options_not_require_literal));
1417 assert!(Pattern::new("abc*def")
1418 .unwrap()
1419 .matches_with("abc/def", options_not_require_literal));
1420 assert!(Pattern::new("abc[/]def")
1421 .unwrap()
1422 .matches_with("abc/def", options_not_require_literal));
1423 }
1424
1425 #[test]
1426 fn test_pattern_matches_require_literal_leading_dot() {
1427 let options_require_literal_leading_dot = MatchOptions {
1428 case_sensitive: true,
1429 require_literal_separator: false,
1430 require_literal_leading_dot: true,
1431 };
1432 let options_not_require_literal_leading_dot = MatchOptions {
1433 case_sensitive: true,
1434 require_literal_separator: false,
1435 require_literal_leading_dot: false,
1436 };
1437
1438 let f = |options| {
1439 Pattern::new("*.txt")
1440 .unwrap()
1441 .matches_with(".hello.txt", options)
1442 };
1443 assert!(f(options_not_require_literal_leading_dot));
1444 assert!(!f(options_require_literal_leading_dot));
1445
1446 let f = |options| {
1447 Pattern::new(".*.*")
1448 .unwrap()
1449 .matches_with(".hello.txt", options)
1450 };
1451 assert!(f(options_not_require_literal_leading_dot));
1452 assert!(f(options_require_literal_leading_dot));
1453
1454 let f = |options| {
1455 Pattern::new("aaa/bbb/*")
1456 .unwrap()
1457 .matches_with("aaa/bbb/.ccc", options)
1458 };
1459 assert!(f(options_not_require_literal_leading_dot));
1460 assert!(!f(options_require_literal_leading_dot));
1461
1462 let f = |options| {
1463 Pattern::new("aaa/bbb/*")
1464 .unwrap()
1465 .matches_with("aaa/bbb/c.c.c.", options)
1466 };
1467 assert!(f(options_not_require_literal_leading_dot));
1468 assert!(f(options_require_literal_leading_dot));
1469
1470 let f = |options| {
1471 Pattern::new("aaa/bbb/.*")
1472 .unwrap()
1473 .matches_with("aaa/bbb/.ccc", options)
1474 };
1475 assert!(f(options_not_require_literal_leading_dot));
1476 assert!(f(options_require_literal_leading_dot));
1477
1478 let f = |options| {
1479 Pattern::new("aaa/?bbb")
1480 .unwrap()
1481 .matches_with("aaa/.bbb", options)
1482 };
1483 assert!(f(options_not_require_literal_leading_dot));
1484 assert!(!f(options_require_literal_leading_dot));
1485
1486 let f = |options| {
1487 Pattern::new("aaa/[.]bbb")
1488 .unwrap()
1489 .matches_with("aaa/.bbb", options)
1490 };
1491 assert!(f(options_not_require_literal_leading_dot));
1492 assert!(!f(options_require_literal_leading_dot));
1493
1494 let f = |options| Pattern::new("**/*").unwrap().matches_with(".bbb", options);
1495 assert!(f(options_not_require_literal_leading_dot));
1496 assert!(!f(options_require_literal_leading_dot));
1497 }
1498
1499 #[test]
1500 fn test_matches_path() {
1501 assert!(Pattern::new("a/b").unwrap().matches_path(Path::new("a/b")));
1504 }
1505
1506 #[test]
1507 fn test_path_join() {
1508 let pattern = Path::new("one").join(Path::new("**/*.rs"));
1509 assert!(Pattern::new(pattern.to_str().unwrap()).is_ok());
1510 }
1511}