1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/* Much of the documentation in this file was taken from the `glob` crate.
 *
 * Copyright (c) 2018 Garrett Berg, vitiral@gmail.com
 * Copyright (c) 2014 The Rust Project Developers.
 *
 * Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
 * http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
 * http://opensource.org/licenses/MIT>, at your option. This file may not be
 * copied, modified, or distributed except according to those terms.
 */
//! Wrapper around the `glob` crate.

use std::io;
use std_prelude::*;
use path_abs::{PathDir, PathFile, PathType};
use glob_crate;

/// Renamed [`glob::MatchOptions`](../glob/struct.MatchOptions.html)
pub type GlobOptions = glob_crate::MatchOptions;

/// Renamed [`glob::PatternError`](../glob/struct.PatternError.html)
pub type GlobPatternError = glob_crate::PatternError;

#[inline(always)]
/// Return an iterator that produces all the `PathType`s that match the given pattern, which may be
/// absolute or relative to the current working directory.
///
/// This may return an error if the pattern is invalid.
///
/// This method uses the default match options and is equivalent to calling
/// `glob_with(pattern, GlobOptions::new())`. Use [`glob_with`](fn.glob_with.html) directly if you
/// want to use non-default match options.
///
/// When iterating, each result is a `io::Result` which expresses the possibility that there was an
/// `io::Error` when attempting to read the contents of the matched path.
///
/// # Example
///
/// ```rust
/// # extern crate ergo_fs;
/// use ergo_fs::*;
///
/// # fn try_main() -> ::std::io::Result<()> {
/// let mut count = 0;
/// for entry in glob("src/glob_*.rs").unwrap() {
///     # count += 1; assert!(entry.is_ok());
///     match entry? {
///         PathType::File(file) => println!("file: {}", file.display()),
///         PathType::Dir(dir) => println!("dir: {}", dir.display()),
///     }
/// }
/// # assert_eq!(count, 1);
/// # Ok(()) } fn main() { try_main().unwrap() }
/// ```
///
/// The above code will print:
///
/// ```ignore
/// /path/to/crate/src/glob_wrapper.rs
/// ```
///
/// If there were more files with the prefix `glob_` it would print more.
pub fn glob(pattern: &str) -> Result<GlobPathTypes, GlobPatternError> {
    GlobPathTypes::new(pattern)
}

#[inline(always)]
/// The same as [`glob`](fn.glob.html) but with additional options.
pub fn glob_with(pattern: &str, options: &GlobOptions) -> Result<GlobPathTypes, GlobPatternError> {
    GlobPathTypes::with(pattern, options)
}

/// An iterator that yields `PathType`s from the filesystem that match a particular pattern.
///
/// Note that it yields `Result<PathType, path_abs::Error>` in order to report any IoErrors that
/// may arise during iteration.  If a directory matches but is unreadable, thereby preventing its
/// contents from being checked for matches, a `path_abs::Error` is returned to express this.
///
/// See the [`glob`](fn.glob.html) function for more details.
pub struct GlobPathTypes {
    paths: glob_crate::Paths,
}

/// Returns an iterator of only `PathFile`s, any directories that matched the glob are ignored.
///
/// See [`GlobPathTypes::files`](struct.GlobPathTypes.html#method=files)
pub struct GlobPathFiles {
    types: GlobPathTypes,
}

/// Returns an iterator of only `PathDirs`s, any files that matched the glob are ignored.
///
/// See [`GlobPathTypes::dirs`](struct.GlobPathTypes.html#method=dirs)
pub struct GlobPathDirs {
    types: GlobPathTypes,
}

impl GlobPathTypes {
    #[inline(always)]
    fn new(pattern: &str) -> Result<GlobPathTypes, glob_crate::PatternError> {
        Ok(GlobPathTypes {
            paths: glob_crate::glob(pattern)?,
        })
    }

    #[inline(always)]
    fn with(
        pattern: &str,
        options: &GlobOptions,
    ) -> Result<GlobPathTypes, glob_crate::PatternError> {
        Ok(GlobPathTypes {
            paths: glob_crate::glob_with(pattern, options)?,
        })
    }

    #[inline(always)]
    /// Consume self and return an iterator over only the files, ignoring any directories.
    ///
    /// # Example
    /// ```rust
    /// # extern crate ergo_fs;
    /// use ergo_fs::*;
    ///
    /// # fn try_main() -> ::std::io::Result<()> {
    /// # let mut count = 0;
    /// // unwrap since we know we are inputing a good pattern
    /// for file in glob("src/glob*.rs").unwrap().files() {
    ///     println!("file: {}", file?.display());
    ///     # count += 1;
    /// }
    /// # assert_eq!(count, 1);
    /// # Ok(()) } fn main() { try_main().unwrap() }
    /// ```
    pub fn files(self) -> GlobPathFiles {
        GlobPathFiles { types: self }
    }

    #[inline(always)]
    /// Consume self and return an iterator over only the directories, ignoring any files.
    ///
    /// # Example
    /// ```rust
    /// # extern crate ergo_fs;
    /// use ergo_fs::*;
    ///
    /// # fn try_main() -> ::std::io::Result<()> {
    /// # let mut count = 0;
    /// // unwrap since we know we are inputing a good pattern
    /// for dir in glob("src/*").unwrap().dirs() {
    ///     println!("dir: {}", dir?.display());
    ///     # count += 1;
    /// }
    /// # assert_eq!(count, 0);
    /// # Ok(()) } fn main() { try_main().unwrap() }
    /// ```
    pub fn dirs(self) -> GlobPathDirs {
        GlobPathDirs { types: self }
    }
}

impl Iterator for GlobPathTypes {
    type Item = io::Result<PathType>;
    // FIXME: if we can get an owned value of the io::Error then we can
    // make this return path_abs::Error
    fn next(&mut self) -> Option<io::Result<PathType>> {
        if let Some(result) = self.paths.next() {
            match result {
                Ok(path) => Some(PathType::new(path).map_err(|err| err.into())),
                Err(err) => Some(Err(io::Error::new(err.error().kind(), err))),
            }
        } else {
            None
        }
    }
}

impl Iterator for GlobPathFiles {
    // FIXME: make path_abs::Error
    type Item = io::Result<PathFile>;
    fn next(&mut self) -> Option<io::Result<PathFile>> {
        loop {
            match self.types.next() {
                Some(Ok(ty)) => match ty {
                    PathType::File(file) => return Some(Ok(file)),
                    // ignore directories
                    PathType::Dir(_) => {}
                },
                Some(Err(err)) => return Some(Err(err)),
                // iterator exahusted
                None => return None,
            }
        }
    }
}

impl Iterator for GlobPathDirs {
    // FIXME: make path_abs::Error
    type Item = io::Result<PathDir>;
    fn next(&mut self) -> Option<io::Result<PathDir>> {
        loop {
            match self.types.next() {
                Some(Ok(ty)) => match ty {
                    PathType::Dir(dir) => return Some(Ok(dir)),
                    // ignore files
                    PathType::File(_) => {}
                },
                Some(Err(err)) => return Some(Err(err)),
                None => return None, // iterator exahusted
            }
        }
    }
}