use std::io;
use std::path::PathBuf;
use clap::Parser;
use tree_rust::filter::Filter;
use tree_rust::printer::{print_tree, OutputFormat, PrintConfig};
use tree_rust::sort::SortKey;
use tree_rust::tree::{walk_directory, TreeConfig, TreeStats};
#[derive(Parser, Debug)]
#[command(name = "tree-rust")]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(default_value = ".")]
directory: PathBuf,
#[arg(short = 'a', long = "all")]
all: bool,
#[arg(short = 'd', long = "dirs-only")]
dirs_only: bool,
#[arg(short = 'l', long = "follow")]
follow_symlinks: bool,
#[arg(short = 'f', long = "full-path")]
full_path: bool,
#[arg(short = 'L', long = "level")]
level: Option<usize>,
#[arg(short = 'P', long = "pattern")]
pattern: Option<Vec<String>>,
#[arg(short = 'I', long = "ignore")]
ignore: Option<Vec<String>>,
#[arg(long = "ignore-case")]
ignore_case: bool,
#[arg(long = "noreport")]
noreport: bool,
#[arg(short = 'p', long = "perm")]
permissions: bool,
#[arg(short = 's', long = "size")]
size: bool,
#[arg(short = 'h', long = "human")]
human: bool,
#[arg(long = "si")]
si: bool,
#[arg(short = 'D', long = "date")]
date: bool,
#[arg(long = "timefmt")]
timefmt: Option<String>,
#[arg(short = 'F', long = "classify")]
classify: bool,
#[arg(short = 't', long = "sort-time")]
sort_time: bool,
#[arg(short = 'U', long = "unsorted")]
unsorted: bool,
#[arg(short = 'r', long = "reverse")]
reverse: bool,
#[arg(long = "dirsfirst")]
dirsfirst: bool,
#[arg(long = "sort")]
sort: Option<String>,
#[arg(short = 'i', long = "noindent")]
noindent: bool,
#[arg(short = 'n', long = "nocolor")]
nocolor: bool,
#[arg(short = 'C', long = "color")]
color: bool,
#[arg(short = 'J', long = "json")]
json: bool,
#[arg(short = 'T', long = "toon")]
toon: bool,
}
fn main() {
let args = Args::parse();
let mut filter = Filter::new();
filter.ignore_case = args.ignore_case;
if let Some(patterns) = &args.pattern {
for p in patterns {
if let Err(e) = filter.add_include(p) {
eprintln!("Invalid pattern '{}': {}", p, e);
std::process::exit(1);
}
}
}
if let Some(ignores) = &args.ignore {
for p in ignores {
if let Err(e) = filter.add_exclude(p) {
eprintln!("Invalid ignore pattern '{}': {}", p, e);
std::process::exit(1);
}
}
}
let sort_key = if args.unsorted {
SortKey::None
} else if args.sort_time {
SortKey::Time
} else if let Some(ref sort_str) = args.sort {
SortKey::from_str(sort_str)
} else {
SortKey::Name
};
let tree_config = TreeConfig {
show_hidden: args.all,
dirs_only: args.dirs_only,
max_depth: args.level,
follow_symlinks: args.follow_symlinks,
full_path: args.full_path,
filter,
sort_key,
sort_reverse: args.reverse,
dirs_first: args.dirsfirst,
};
let colorize = if args.nocolor {
false
} else if args.color {
true
} else {
atty::is(atty::Stream::Stdout)
};
let output_format = if args.json {
OutputFormat::Json
} else if args.toon {
OutputFormat::Toon
} else {
OutputFormat::Text
};
let print_config = PrintConfig {
colorize,
show_permissions: args.permissions,
show_size: args.size || args.human || args.si,
human_readable: args.human || args.si,
si_units: args.si,
show_date: args.date,
time_format: args.timefmt,
show_type_indicator: args.classify,
no_indent: args.noindent,
full_path: args.full_path,
no_report: args.noreport,
output_format,
};
let mut stats = TreeStats::default();
let path = args.directory.canonicalize().unwrap_or(args.directory);
let tree = walk_directory(&path, &tree_config, &mut stats, 0);
let stdout = io::stdout();
let mut handle = stdout.lock();
if let Err(e) = print_tree(&mut handle, &tree, &print_config, &stats) {
eprintln!("Error writing output: {}", e);
std::process::exit(1);
}
}