Skip to content

Commit

Permalink
Merge pull request #12 from jprochazk/master
Browse files Browse the repository at this point in the history
Add option to filter files using glob patterns
  • Loading branch information
solidiquis authored Feb 7, 2023
2 parents 7317bf0 + 88a844b commit f078ed4
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 7 deletions.
50 changes: 50 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ lscolors = { version = "0.13.0", features = ["ansi_term"] }
once_cell = "1.17.0"

[dev-dependencies]
indoc = "2.0.0"
strip-ansi-escapes = "0.1.1"
tempdir = "0.3"
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ Options:
-n, --num-threads <NUM_THREADS> Number of threads to use [default: 4]
-o, --order <ORDER> Sort order to display directory content [default: none] [possible values: filename, size, none]
-s, --show-hidden Whether to show hidden files; disabled by default
-g, --glob <GLOB> Include or exclude files using glob patterns
--iglob <GLOB> Include or exclude files using glob patterns; case insensitive
--glob-case-insensitive Process all glob patterns case insensitively
-h, --help Print help (see more with '--help')
-V, --version Print version
Expand Down
76 changes: 71 additions & 5 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use crate::fs::erdtree::order::Order;
use clap::Parser;
use ignore::{WalkBuilder, WalkParallel};
use ignore::{
overrides::{Override, OverrideBuilder},
WalkBuilder, WalkParallel,
};
use std::{
convert::From,
error::Error as StdError,
fmt::{self, Display, Formatter},
path::{Path, PathBuf},
usize,
};
Expand Down Expand Up @@ -36,6 +41,18 @@ pub struct Clargs {
/// Whether to show hidden files; disabled by default
#[arg(short, long)]
pub show_hidden: bool,

/// Include or exclude files using glob patterns
#[arg(short, long)]
glob: Vec<String>,

/// Include or exclude files using glob patterns; case insensitive
#[arg(long)]
iglob: Vec<String>,

/// Process all glob patterns case insensitively
#[arg(long)]
glob_case_insensitive: bool,
}

impl Clargs {
Expand All @@ -58,15 +75,64 @@ impl Clargs {
pub fn max_depth(&self) -> Option<usize> {
self.max_depth
}

/// Ignore file overrides.
pub fn overrides(&self) -> Result<Override, ignore::Error> {
if self.glob.is_empty() && self.iglob.is_empty() {
return Ok(Override::empty());
}

let mut builder = OverrideBuilder::new(self.dir());
if self.glob_case_insensitive {
builder.case_insensitive(true).unwrap();
}

for glob in self.glob.iter() {
builder.add(glob)?;
}

// all subsequent patterns are case insensitive
builder.case_insensitive(true).unwrap();
for glob in self.iglob.iter() {
builder.add(glob)?;
}

builder.build()
}
}

impl From<&Clargs> for WalkParallel {
fn from(clargs: &Clargs) -> WalkParallel {
WalkBuilder::new(clargs.dir())
impl TryFrom<&Clargs> for WalkParallel {
type Error = Error;

fn try_from(clargs: &Clargs) -> Result<Self, Self::Error> {
Ok(WalkBuilder::new(clargs.dir())
.follow_links(false)
.overrides(clargs.overrides()?)
.git_ignore(!clargs.ignore_git_ignore)
.hidden(!clargs.show_hidden)
.threads(clargs.num_threads)
.build_parallel()
.build_parallel())
}
}

/// Errors which may occur during command-line argument parsing.
#[derive(Debug)]
pub enum Error {
InvalidGlobPatterns(ignore::Error),
}

impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Error::InvalidGlobPatterns(e) => write!(f, "Invalid glob patterns: {e}"),
}
}
}

impl StdError for Error {}

impl From<ignore::Error> for Error {
fn from(value: ignore::Error) -> Self {
Self::InvalidGlobPatterns(value)
}
}
15 changes: 13 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::process::ExitCode;

use clap::Parser;
use cli::Clargs;
use fs::erdtree::{self, tree::Tree};
Expand All @@ -9,10 +11,19 @@ mod cli;
/// Filesystem operations.
mod fs;

fn main() -> Result<(), fs::error::Error> {
fn main() -> ExitCode {
if let Err(e) = run() {
eprintln!("{e}");
return ExitCode::FAILURE;
}

ExitCode::SUCCESS
}

fn run() -> Result<(), Box<dyn std::error::Error>> {
erdtree::init_ls_colors();
let clargs = Clargs::parse();
let walker = WalkParallel::from(&clargs);
let walker = WalkParallel::try_from(&clargs)?;
let tree = Tree::new(walker, clargs.order(), clargs.max_depth())?;

println!("{tree}");
Expand Down
1 change: 1 addition & 0 deletions tests/data/a.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
asdf
1 change: 1 addition & 0 deletions tests/data/b.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bvcx
1 change: 1 addition & 0 deletions tests/data/c.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# File
1 change: 1 addition & 0 deletions tests/data/nested/other.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Nested file
89 changes: 89 additions & 0 deletions tests/glob.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use indoc::indoc;
use std::process::Command;
use std::process::Stdio;
use strip_ansi_escapes::strip as strip_ansi_escapes;

fn run_cmd(args: &[&str]) -> String {
let mut cmd = Command::new("cargo");
cmd.arg("run")
.arg("--")
.arg("tests/data")
.arg("--num-threads")
.arg("1")
.arg("--order")
.arg("filename");

for arg in args {
cmd.arg(arg);
}

let output = cmd
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
.unwrap();
assert!(output.status.success());

String::from_utf8(strip_ansi_escapes(output.stdout).unwrap())
.unwrap()
.trim()
.to_string()
}

#[test]
fn glob() {
assert_eq!(
run_cmd(&["--glob", "*.txt"]),
indoc!(
"
data (10.00 B)
├─ a.txt (5.00 B)
├─ b.txt (5.00 B)
└─ nested"
)
)
}

#[test]
fn glob_negative() {
assert_eq!(
run_cmd(&["--glob", "!*.txt"]),
indoc!(
"
data (21.00 B)
├─ c.md (7.00 B)
└─ nested (14.00 B)
└─ other.md (14.00 B)"
)
)
}

#[test]
fn glob_case_insensitive() {
assert_eq!(
run_cmd(&["--glob", "*.TXT", "--glob-case-insensitive"]),
indoc!(
"
data (10.00 B)
├─ a.txt (5.00 B)
├─ b.txt (5.00 B)
└─ nested"
)
)
}

#[test]
fn iglob() {
assert_eq!(
run_cmd(&["--iglob", "*.TXT"]),
indoc!(
"
data (10.00 B)
├─ a.txt (5.00 B)
├─ b.txt (5.00 B)
└─ nested"
)
)
}

0 comments on commit f078ed4

Please sign in to comment.