use clap::{CommandFactory, Parser, ValueEnum};
use clap_complete::{generate, Shell};
use std::io;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, ValueEnum)]
#[clap(rename_all = "kebab-case")]
pub enum OutputFormat {
#[value(name = "table")]
Table,
#[value(name = "json")]
Json,
#[value(name = "json-pretty")]
JsonPretty,
}
#[derive(Debug, Parser)]
#[command(
version,
about = "Rust based LS command",
long_about = r#"bestls is a Rust-powered file listing CLI tool.
Features:
- Outputs in table or JSON formats.
- Supports sorting by name, size, or modification date.
- Pretty-printed JSON output available.
- Shell completion generation support.
Usage Examples:
bestls -p ./src
bestls --json --sort size
bestls --json-pretty --sort date
bestls completion bash > ~/.local/share/bash-completion/completions/bestls
"#
)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Commands>,
#[arg(
short = 'p',
long = "path",
value_name = "PATH",
help = "Directory path to list files from. Defaults to current directory."
)]
pub path: Option<PathBuf>,
#[arg(
short = 'j',
long = "json",
help = "Output file list in compact JSON format (deprecated, use --format json instead).",
default_value_t = false
)]
pub json: bool,
#[arg(
long = "json-pretty",
help = "Output file list in pretty-printed JSON format (deprecated, use --format json-pretty instead).",
default_value_t = false
)]
pub json_pretty: bool,
#[arg(
short = 's',
long = "sort",
value_enum,
default_value = "name",
help = "Sort files by the given attribute."
)]
pub sort_by: SortBy,
#[arg(
short = 'a',
long = "all",
help = "Include hidden files.",
default_value_t = false
)]
pub all: bool,
#[arg(
long = "compact",
help = "Output in compact single-column format.",
default_value_t = false
)]
pub compact: bool,
#[arg(
long = "columns",
value_name = "COLS",
help = "Comma-separated columns to display: name,type,size,date,permissions,owner,group"
)]
pub columns: Option<String>,
#[arg(
long = "out",
value_name = "FILE",
help = "Export output to file instead of stdout."
)]
pub output_file: Option<std::path::PathBuf>,
#[arg(
long = "format",
value_name = "FORMAT",
value_enum,
default_value = "table",
help = "Output format: table, json, or json-pretty (legacy --json/--json-pretty flags override this for backward compatibility)"
)]
pub format: OutputFormat,
#[arg(
long = "no-color",
help = "Disable colored output.",
default_value_t = false
)]
pub no_color: bool,
#[arg(
long = "tree",
help = "Display directory tree (recursive listing).",
default_value_t = false
)]
pub tree: bool,
#[arg(
long = "depth",
value_name = "N",
requires = "tree",
help = "Maximum depth for tree traversal (requires --tree)."
)]
pub depth: Option<usize>,
#[arg(
long = "filter-ext",
value_name = "EXT",
help = "Filter by file extension (e.g., rs,txt,md). Comma-separated list."
)]
pub filter_ext: Option<String>,
#[arg(
long = "filter-name",
value_name = "PATTERN",
help = "Filter by filename pattern (glob-style, e.g., '*.txt')."
)]
pub filter_name: Option<String>,
#[arg(
long = "min-size",
value_name = "SIZE",
help = "Filter files with minimum size (e.g., 1KB, 1MB, 100B)."
)]
pub min_size: Option<String>,
#[arg(
long = "max-size",
value_name = "SIZE",
help = "Filter files with maximum size (e.g., 1KB, 1MB, 100B)."
)]
pub max_size: Option<String>,
}
#[derive(Debug, Clone, ValueEnum)]
#[clap(rename_all = "lower")]
pub enum SortBy {
Name,
Size,
Date,
}
#[derive(Debug, Parser)]
pub enum Commands {
Completion {
#[arg(value_enum)]
shell: Shell,
},
Theme {
#[command(subcommand)]
subcommand: ThemeSubcommand,
},
}
#[derive(Debug, Parser)]
pub enum ThemeSubcommand {
Init {
#[arg(long)]
show: bool,
},
Path,
Reset,
}
impl Cli {
pub fn effective_format(&self) -> OutputFormat {
if self.json_pretty {
OutputFormat::JsonPretty
} else if self.json {
OutputFormat::Json
} else {
self.format
}
}
pub fn generate_completion(shell: Shell) {
let mut cmd = Self::command();
let name = cmd.get_name().to_string();
generate(shell, &mut cmd, name, &mut io::stdout());
}
}