use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
#[derive(Parser, Debug)]
#[command(
name = "fsearch",
bin_name = "fsearch",
version,
about = "⚡ Fast file search & duplicate finder",
long_about = "⚡ fsearch — blazingly fast, cross-platform file & content search\n\n\
Subcommands:\n\
\n fsearch find <PATTERN> Search for files/content\
\n fsearch dup [PATH] Find duplicate files\
\n fsearch config Config helpers\
\n\nRun `fsearch <SUBCOMMAND> --help` for details.",
author = "Hadi Cahyadi <cumulus13@gmail.com>"
)]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
}
#[derive(Subcommand, Debug)]
pub enum Command {
#[command(alias = "f")]
Find(FindArgs),
#[command(alias = "d", alias = "dupes", alias = "duplicates")]
Dup(DupArgs),
#[command(alias = "cfg")]
Config(ConfigArgs),
}
#[derive(Args, Debug)]
#[command(
about = "🔍 Search for files by name or content",
after_help = "EXAMPLES:\n\
\n fsearch find '*.rs' # files by glob\
\n fsearch find TODO -f -i '*.py' # content search\
\n fsearch find README -C -d 5 # case-sensitive, depth 5\n"
)]
pub struct FindArgs {
pub pattern: String,
#[arg(short = 'm', long, value_name = "1|2", default_value = "1")]
pub method: SearchMethod,
#[arg(short = 'c', long = "case-insensitive", action = ArgAction::SetTrue)]
pub case_insensitive: bool,
#[arg(short = 'C', long = "case-sensitive", action = ArgAction::SetTrue)]
pub case_sensitive: bool,
#[arg(short = 'd', long = "deep", value_name = "DEPTH", default_value = "1")]
pub depth: u32,
#[arg(short = 'p', long, value_name = "PATH")]
pub path: Option<String>,
#[arg(short = 'D', long = "no-dir", action = ArgAction::SetTrue)]
pub no_dir: bool,
#[arg(short = 'f', long = "file", action = ArgAction::SetTrue)]
pub search_in_files: bool,
#[arg(short = 'i', long, value_name = "GLOBS", default_value = "")]
pub include: String,
#[arg(short = 'x', long, value_name = "DIRS", default_value = "")]
pub exclude: String,
#[arg(short = 'n', long, value_name = "N", default_value = "0")]
pub max_results: usize,
#[arg(short = 'v', long, action = ArgAction::SetTrue)]
pub verbose: bool,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum SearchMethod {
#[value(name = "1")]
Walkdir = 1,
#[value(name = "2")]
Recursive = 2,
}
#[derive(Args, Debug)]
#[command(
about = "🔁 Find duplicate files",
after_help = "EXAMPLES:\n\
\n fsearch dup ~/Downloads # content duplicates\
\n fsearch dup . --mode name # same filename in different dirs\
\n fsearch dup . --mode size # same size (fast, imprecise)\
\n fsearch dup . --algo md5 -d 5 # use MD5, depth 5\
\n fsearch dup . -i '*.jpg,*.png' # only images\
\n fsearch dup . --min-size 1048576 # files >= 1 MiB only\n"
)]
pub struct DupArgs {
#[arg(value_name = "PATH", default_value = ".")]
pub path: String,
#[arg(long, value_name = "MODE", default_value = "content")]
pub mode: DupMode,
#[arg(long, value_name = "ALGO", default_value = "sha256")]
pub algo: DupAlgo,
#[arg(short = 'd', long = "deep", value_name = "DEPTH", default_value = "10")]
pub depth: u32,
#[arg(short = 'i', long, value_name = "GLOBS", default_value = "")]
pub include: String,
#[arg(short = 'x', long, value_name = "DIRS", default_value = "")]
pub exclude: String,
#[arg(long, value_name = "BYTES", default_value = "1")]
pub min_size: u64,
#[arg(long, value_name = "BYTES", default_value = "0")]
pub max_size: u64,
#[arg(long, action = ArgAction::SetTrue)]
pub skip_binary: bool,
#[arg(short = 'n', long, value_name = "N", default_value = "0")]
pub max_results: usize,
#[arg(short = 'v', long, action = ArgAction::SetTrue)]
pub verbose: bool,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum DupMode {
Content,
Name,
Size,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
pub enum DupAlgo {
Md5,
Sha256,
}
#[derive(Args, Debug)]
#[command(about = "⚙️ Configuration helpers")]
pub struct ConfigArgs {
#[command(subcommand)]
pub action: ConfigAction,
}
#[derive(Subcommand, Debug)]
pub enum ConfigAction {
Init,
Show,
Path,
}