use std::{
env,
path::{Path, PathBuf},
slice, vec,
};
use anyhow::{self, Context};
use clap::{Args, Parser, Subcommand, ValueEnum};
use itertools::Itertools;
#[derive(Parser)]
#[command(version, about, long_about = None)]
#[command(propagate_version = true, max_term_width = 100)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
#[command(flatten)]
Lib(LibCommands),
License(LicenseArgs),
}
#[derive(Subcommand)]
pub enum LibCommands {
Check(CommandArgs),
Fix(CommandArgs),
}
impl LibCommands {
pub fn get_args(&self) -> &CommandArgs {
match self {
Self::Check(args) => args,
Self::Fix(args) => args,
}
}
}
#[derive(Args)]
#[command(next_line_help = true)]
pub struct CommandArgs {
targets: Option<Vec<PathBuf>>,
#[command(flatten)]
verbosity: clap_verbosity_flag::Verbosity,
#[command(flatten)]
pub editorconfig_args: EditorConfigArgs,
#[command(flatten)]
pub ignore_args: IgnoreArgs,
}
#[derive(Args)]
#[command(next_help_heading = "Disable EditorConfig properties")]
pub struct EditorConfigArgs {
#[arg(short = 'c', long = "disable-charset", action = clap::ArgAction::SetFalse, help =
"Disables that the charset property of EditorConfig is considered for any file. \
Determining the actual charset of files can be faulty. Use this option for such cases.")]
pub charset: bool,
#[arg(short = 'e', long = "disable-end-of-line", action = clap::ArgAction::SetFalse, help =
"Disables that the end_of_line property of EditorConfig is considered for any file.")]
pub end_of_line: bool,
}
#[derive(Args)]
#[command(next_help_heading = "Control which files are ignored")]
pub struct IgnoreArgs {
#[arg(short = 'd', long = "ignore-hidden", action = clap::ArgAction::SetFalse, help =
"Ignore hidden files and directories (starting with a dot). Default is not to ignore them, \
except for the .git directory with the meta data of a git repository.")]
pub hidden: bool,
#[arg(short = 'g', long = "no-git-settings", action = clap::ArgAction::SetFalse, help =
"Ignore all git settings about ignored files. Default is that in a git repository \
(i.e., root contains .git directory) the git settings are respected \
(i.e., .gitignore and .git/info/exclude files, the git config option core.excludesFile \
and ignoring the .git directory even if hidden directories are not ignored).")]
pub git_settings: bool,
#[arg(short = 'f', long)]
pub ignore_file: Option<PathBuf>,
}
impl CommandArgs {
pub fn new<P: AsRef<Path>>(
target: Option<P>,
editorconfig_args: EditorConfigArgs,
ignore_args: IgnoreArgs,
) -> CommandArgs {
let targets = target.as_ref().map(|p| slice::from_ref(p));
CommandArgs::new_internal(targets, editorconfig_args, ignore_args)
}
pub fn new_multi_target<P: AsRef<Path>>(
targets: &[P],
editorconfig_args: EditorConfigArgs,
ignore_args: IgnoreArgs,
) -> Option<CommandArgs> {
if targets.is_empty() {
None
} else {
Some(CommandArgs::new_internal(
Some(targets),
editorconfig_args,
ignore_args,
))
}
}
fn new_internal<P: AsRef<Path>>(
targets: Option<&[P]>,
editorconfig_args: EditorConfigArgs,
ignore_args: IgnoreArgs,
) -> CommandArgs {
CommandArgs {
targets: targets.map(|s| s.iter().map(|p| p.as_ref().to_path_buf()).collect_vec()),
verbosity: clap_verbosity_flag::Verbosity::new(0, 0),
editorconfig_args,
ignore_args,
}
}
pub(crate) fn targets(&self) -> anyhow::Result<Vec<PathBuf>> {
match self.targets.as_ref() {
Some(t) => Ok(t.iter().map(|p| p.to_path_buf()).collect_vec()),
None => env::current_dir()
.with_context(|| "Current working directory no accessible!")
.map(|p| vec![p]),
}
}
pub fn verbosity(&self) -> clap_verbosity_flag::Verbosity {
self.verbosity
}
}
#[derive(Args)]
#[command(next_line_help = true)]
pub struct LicenseArgs {
#[arg(short, long, default_value_t = MarkdownFormat::Auto, value_enum)]
pub format: MarkdownFormat,
}
#[derive(Clone, ValueEnum)]
pub enum MarkdownFormat {
Auto,
Paginate,
Pretty,
Plain,
}