use clap::Parser;
use dirwalk::output::{self, ColorMode, DisplayOptions, Format, GroupBy};
use dirwalk::{Sort, Threads, WalkBuilder};
use std::io::{self, BufWriter, IsTerminal, Write};
use std::process;
#[derive(Parser)]
#[command(
name = "dirwalk",
about = "Platform-optimized recursive directory walker"
)]
struct Cli {
#[arg(default_value = ".")]
path: String,
#[arg(long)]
max_depth: Option<u32>,
#[arg(long)]
hidden: bool,
#[arg(long)]
gitignore: bool,
#[arg(long)]
follow_links: bool,
#[arg(long, value_delimiter = ',')]
extensions: Option<Vec<String>>,
#[arg(long)]
glob: Option<String>,
#[arg(long)]
min_size: Option<u64>,
#[arg(long)]
max_size: Option<u64>,
#[arg(long, value_enum)]
sort: Option<Sort>,
#[arg(long)]
dirs_first: bool,
#[arg(long, value_enum)]
group_by: Option<GroupBy>,
#[arg(long)]
stats: bool,
#[arg(long, value_enum)]
format: Option<Format>,
#[arg(long, conflicts_with = "format")]
short: bool,
#[arg(long, value_enum, default_value = "auto")]
color: ColorMode,
#[arg(long, conflicts_with = "color")]
no_color: bool,
#[arg(long, default_value = "0")]
threads: Threads,
}
fn main() {
let cli = Cli::parse();
let mut builder = WalkBuilder::new(&cli.path)
.hidden(cli.hidden)
.follow_links(cli.follow_links)
.gitignore(cli.gitignore)
.stats(cli.stats)
.dirs_first(cli.dirs_first)
.threads(cli.threads);
if let Some(depth) = cli.max_depth {
builder = builder.max_depth(depth);
}
if let Some(ref exts) = cli.extensions {
builder = builder.extensions(exts);
}
if let Some(ref pattern) = cli.glob {
builder = match builder.glob(pattern) {
Ok(b) => b,
Err(e) => {
eprintln!("invalid glob pattern: {}", e);
process::exit(1);
}
};
}
if let Some(size) = cli.min_size {
builder = builder.min_size(size);
}
if let Some(size) = cli.max_size {
builder = builder.max_size(size);
}
if let Some(sort) = cli.sort {
builder = builder.sort(sort);
}
let result = match builder.build() {
Ok(r) => r,
Err(e) => {
eprintln!("error: {}", e);
process::exit(1);
}
};
let is_tty = io::stdout().is_terminal();
let color_mode = if cli.no_color {
ColorMode::Never
} else {
cli.color
};
let color_enabled = color_mode.resolve(is_tty);
let use_rich = cli.format.is_none() && (is_tty || cli.short);
let display = if use_rich {
if color_enabled {
output::color::enable_ansi();
}
let terminal_width = terminal_size::terminal_size()
.map(|(w, _)| w.0)
.unwrap_or(80);
let ls_colors = lscolors::LsColors::from_env().unwrap_or_default();
Some(DisplayOptions {
short: cli.short,
classify: true,
color_enabled,
terminal_width,
ls_colors,
})
} else {
None
};
let format = cli.format.unwrap_or(Format::Plain);
let stdout = io::stdout().lock();
let mut w = BufWriter::new(stdout);
if let Err(e) = output::write_entries(
&mut w,
&result.entries,
format,
cli.group_by,
display.as_ref(),
) {
eprintln!("output error: {}", e);
process::exit(1);
}
if let Some(ref stats) = result.stats
&& let Err(e) = output::write_stats(&mut w, stats)
{
eprintln!("output error: {}", e);
process::exit(1);
}
if let Err(e) = w.flush() {
if e.kind() != std::io::ErrorKind::BrokenPipe {
eprintln!("output error: {}", e);
process::exit(1);
}
}
}