mod overview;
mod validation;
use anyhow::Context;
use clap::{ArgAction, Parser, Subcommand, ValueEnum};
use crawk::version;
use overview::generate_after_help;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::path::PathBuf;
use tracing_subscriber::filter::LevelFilter;
use validation::{validate_depth, validate_module_path};
#[derive(Parser, Debug, Clone)]
#[command(
version = version::VERSION,
long_version = version::LONG_VERSION_MESSAGE,
after_help = generate_after_help(false),
after_long_help = generate_after_help(true)
)]
#[clap(verbatim_doc_comment)]
pub(crate) struct CrawkArgs {
#[clap(flatten)]
options: CrawkOptions,
#[command(subcommand)]
pub command: CrawkCommands,
}
#[derive(Parser, Debug, Clone)]
pub(crate) struct CrawkOptions {
#[arg(short = 'p', long = "path")]
path: Option<PathBuf>,
#[arg(short = 'v', long = "verbose", action = ArgAction::Count)]
verbose: u8,
#[arg(short = 'l', long = "log-file")]
log_file: Option<PathBuf>,
}
impl CrawkArgs {
pub(crate) fn crate_root(&self) -> anyhow::Result<PathBuf> {
match self.options.path.as_ref() {
Some(path) => {
if !path.exists() {
anyhow::bail!("Provided path '{}' does not exist", path.display());
}
Ok(path.clone())
}
None => std::env::current_dir().context("Failed to get current directory"),
}
}
#[must_use]
pub(crate) const fn verbosity(&self) -> LevelFilter {
match self.options.verbose {
0 => LevelFilter::WARN,
1 => LevelFilter::INFO,
_ => LevelFilter::DEBUG,
}
}
#[must_use]
pub(crate) const fn log_file(&self) -> Option<&PathBuf> {
self.options.log_file.as_ref()
}
#[must_use]
pub(crate) const fn file_verbosity(&self) -> LevelFilter {
match self.options.verbose {
0 | 1 => LevelFilter::INFO,
_ => LevelFilter::DEBUG,
}
}
}
#[derive(Subcommand, Debug, Clone)]
pub(crate) enum CrawkCommands {
#[clap(verbatim_doc_comment)]
Use(UseArgs),
}
#[derive(ValueEnum, Debug, Clone, Default, PartialEq, Eq)]
pub(crate) enum UseOutputFormat {
#[default]
Plain,
Grouped,
}
impl Display for UseOutputFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::Plain => f.write_str("plain"),
Self::Grouped => f.write_str("grouped"),
}
}
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Parser, Debug, Clone)]
pub(crate) struct UseArgs {
#[clap(verbatim_doc_comment)]
#[arg(value_parser = validate_module_path)]
pub module_path: String,
#[clap(verbatim_doc_comment)]
#[arg(short = 'r', long = "recursive", default_value_t = false)]
pub recursive: bool,
#[clap(verbatim_doc_comment)]
#[arg(short = 't', long = "include-tests", default_value_t = false)]
pub include_tests: bool,
#[clap(verbatim_doc_comment)]
#[arg(short = 'e', long = "expand", default_value_t = false)]
pub expand: bool,
#[clap(verbatim_doc_comment)]
#[arg(short = 'd', long = "depth", value_parser = validate_depth)]
pub depth: Option<usize>,
#[clap(verbatim_doc_comment)]
#[arg(short = 'f', long = "format", default_value_t = UseOutputFormat::Plain)]
pub format: UseOutputFormat,
#[clap(verbatim_doc_comment)]
#[arg(short = 'G', long = "resolve-globs", default_value_t = false)]
pub resolve_globs: bool,
}