Skip to content

Commit

Permalink
glob is lookin good
Browse files Browse the repository at this point in the history
  • Loading branch information
vitiral committed Jan 29, 2018
1 parent 020ec9c commit 3add51a
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 61 deletions.
206 changes: 156 additions & 50 deletions src/glob_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,63 @@
use std::io;
use std_prelude::*;
use path_abs::PathType;
use glob;
use path_abs::{PathDir, PathFile, PathType};
use glob_crate;

/// Renamed [`glob::MatchOptions`](glob/struct.MatchOptions.html)
pub type GlobOptions = glob::MatchOptions;
/// 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.
///
Expand All @@ -22,71 +74,88 @@ pub type GlobOptions = glob::MatchOptions;
/// 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 GlobPaths {
paths: glob::Paths,
pub struct GlobPathTypes {
paths: glob_crate::Paths,
}

impl GlobPaths {
/// 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` 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.
/// 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
///
/// ```
/// ```rust
/// # extern crate ergo_fs;
/// use ergo_fs::*;
///
/// # fn try_main() -> ::std::io::Result<()> {
/// let mut count = 0;
/// for entry in GlobPaths::new("src/glob_*.rs").unwrap() {
/// # count += 1; assert!(entry.is_ok());
/// match entry {
/// Ok(PathType::File(file)) => println!("file: {}", file.display()),
/// Ok(PathType::Dir(dir)) => println!("dir: {}", dir.display()),
///
/// // If the path matched at one time but there
/// // was an IO Error later.
/// Err(e) => println!("Error: {}", e),
/// }
/// # 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.
///
/// The above code will print:
/// # Example
/// ```rust
/// # extern crate ergo_fs;
/// use ergo_fs::*;
///
/// ```ignore
/// /path/to/crate/src/glob_wrapper.rs
/// # 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() }
/// ```
///
/// If there were more files with the prefix `glob_` it would print more.
pub fn new(pattern: &str) -> Result<GlobPaths, glob::PatternError> {
Ok(GlobPaths {
paths: glob::glob(pattern)?,
})
}

/// Create a new `GlobPaths` using the options provided.
pub fn with(pattern: &str, options: &GlobOptions) -> Result<GlobPaths, glob::PatternError> {
Ok(GlobPaths {
paths: glob::glob_with(pattern, options)?,
})
pub fn dirs(self) -> GlobPathDirs {
GlobPathDirs { types: self }
}
}

impl Iterator for GlobPaths {
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
Expand All @@ -101,3 +170,40 @@ impl Iterator for GlobPaths {
}
}
}

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
}
}
}
}
31 changes: 20 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,18 @@
//! writeable files.
//! - [`WalkDir`](struct.WalkDir.html): used for recursively walking directories _quickly_.
//! See the **Walkdir** section below.
//! - [`GlobPaths`](struct.GlobPaths.html): a lightweight wrapper around
//! [`glob::glob`](glob/fn.glob.html) that returns `PathType` objects. Consider using
//! with `expand` when you want shell-like behavior.
//!
//!
//! # Methods
//! The following methods are exported.
//!
//! - [`expand`](fn.expand.html): does shell expansion on both tilde (`~` = home dir) and
//! environment variables with the user's home directory + env variables. Also see the
//! exported [`shellexpand`](shellexpand/index.html) crate itself. Consider using with
//! [`GlobPaths`](struct.GlobPaths.html) when you want shell-like behavior.
//!
//! `glob` (see below).
//! - [`glob`](fn.glob.html): a lightweight wrapper around [`glob::glob`](glob/fn.glob.html) that
//! returns `PathType` objects.
//! - [`glob_with`](fn.glob_with.html): a lightweight wrapper around
//! [`glob::glob_with`](glob/fn.glob_with.html) that returns `PathType` objects.
//!
//! # Details
//! Bellow are some additional details about imported types.
Expand Down Expand Up @@ -156,7 +155,7 @@
//! # Ok(()) } fn main() { try_main().unwrap() }
//! ```
pub extern crate glob;
pub extern crate glob as glob_crate;
pub extern crate path_abs;
pub extern crate shellexpand;
pub extern crate std_prelude;
Expand All @@ -178,7 +177,14 @@ pub use walkdir::{Error as WalkError, WalkDir};
mod tmp;
mod glob_wrapper;

pub use glob_wrapper::{GlobOptions, GlobPaths};
pub use glob_wrapper::{
// functions
glob, glob_with,
// renamed types
GlobOptions, GlobPatternError,
// new iterators
GlobPathDirs, GlobPathFiles, GlobPathTypes,
};
pub use tmp::PathTmp;

/// Extension method on the `Path` type.
Expand Down Expand Up @@ -235,16 +241,19 @@ impl PathTypeExt for PathType {}
// ---------------------------------------
// ----------- SHELL EXPANSION -----------

/// Renamed [`shellexpand::LookupError`](../shellexpand/struct.LookupError.html) for better
/// ergonomics.
pub type ExpandError = shellexpand::LookupError<::std::env::VarError>;

/// Performs both tilde and environment shell expansions in the default system context.
/// Performs both tilde and environment shell expansions in the default system context. This is
/// the same as [`shellexpand::full`](../shellexpand/fn.full.html) and is the "typical use case"
/// for expanding strings.
///
/// Note that non-existant variables will result in an `Err` (in `sh` they are silently replaced
/// with an empty string). Also, environment lookup is only done _as needed_ so this function is
/// performant on strings that do not expand.
///
/// For more options and information, use the exported [`shellexpand`](shellexpand/index.html)
/// crate directly.
/// For more options and information, see the exported [`shellexpand`](../shellexpand/index.html).
///
/// # Examples
/// ```
Expand Down

0 comments on commit 3add51a

Please sign in to comment.