#![deny(clippy::unwrap_used)]
use std::path::PathBuf;
#[cfg(feature = "bin")]
use std::str::FromStr;
#[cfg(feature = "bin")]
use clang_installer::RequestedVersion;
#[cfg(feature = "bin")]
use clap::{
ArgAction, Args, Parser, Subcommand, ValueEnum,
builder::{FalseyValueParser, NonEmptyStringValueParser},
value_parser,
};
mod structs;
pub use structs::{ClangParams, FeedbackInput, LinesChangedOnly, ThreadComments};
#[cfg(feature = "bin")]
#[derive(Debug, Clone, PartialEq, Eq, ValueEnum)]
pub enum Verbosity {
Info,
Debug,
}
#[cfg(feature = "bin")]
impl Verbosity {
pub fn is_debug(&self) -> bool {
matches!(self, Verbosity::Debug)
}
}
#[cfg(feature = "bin")]
#[derive(Debug, Clone, Parser)]
#[command(author, about)]
pub struct Cli {
#[command(flatten)]
pub general_options: GeneralOptions,
#[command(flatten)]
pub source_options: SourceOptions,
#[command(flatten)]
pub format_options: FormatOptions,
#[command(flatten)]
pub tidy_options: TidyOptions,
#[command(flatten)]
pub feedback_options: FeedbackOptions,
#[arg(
name = "files",
value_name = "FILE",
action = ArgAction::Append,
verbatim_doc_comment,
)]
pub not_ignored: Option<Vec<String>>,
#[command(subcommand)]
pub commands: Option<CliCommand>,
}
#[cfg(feature = "bin")]
#[derive(Debug, Clone, Subcommand)]
pub enum CliCommand {
Version,
}
#[cfg(feature = "bin")]
#[derive(Debug, Clone, Args)]
#[group(id = "General options", multiple = true, required = false)]
pub struct GeneralOptions {
#[cfg_attr(
feature = "bin",
arg(
short = 'V',
long,
default_missing_value = "CPP-LINTER-VERSION",
num_args = 0..=1,
value_parser = RequestedVersion::from_str,
default_value = "",
help_heading = "General options",
verbatim_doc_comment
)
)]
pub version: RequestedVersion,
#[cfg_attr(
feature = "bin",
arg(
short = 'v',
long,
default_value = "info",
default_missing_value = "debug",
num_args = 0..=1,
help_heading = "General options"
)
)]
pub verbosity: Verbosity,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "bin", derive(Args))]
#[cfg_attr(
feature = "bin",
group(id = "Source options", multiple = true, required = false)
)]
pub struct SourceOptions {
#[cfg_attr(
feature = "bin",
arg(
short,
long,
value_delimiter = ',',
default_value = "c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx",
value_parser = NonEmptyStringValueParser::new(),
help_heading = "Source options"
)
)]
pub extensions: Vec<String>,
#[cfg_attr(
feature = "bin",
arg(short, long, default_value = ".", help_heading = "Source options")
)]
pub repo_root: String,
#[cfg_attr(
feature = "bin",
arg(
short,
long,
default_value = "true",
help_heading = "Source options",
ignore_case = true,
verbatim_doc_comment
)
)]
pub lines_changed_only: LinesChangedOnly,
#[cfg_attr(
feature = "bin",
arg(
short,
long,
default_value = "false",
default_missing_value = "true",
default_value_ifs = [
("lines-changed-only", "true", "true"),
("lines-changed-only", "on", "true"),
("lines-changed-only", "1", "true"),
("lines-changed-only", "diff", "true"),
],
num_args = 0..=1,
action = ArgAction::Set,
value_parser = FalseyValueParser::new(),
help_heading = "Source options",
verbatim_doc_comment,
)
)]
pub files_changed_only: bool,
#[cfg_attr(
feature = "bin",
arg(
short,
long,
value_delimiter = '|',
default_value = ".github|target",
help_heading = "Source options",
verbatim_doc_comment
)
)]
pub ignore: Vec<String>,
#[cfg_attr(
feature = "bin",
arg(
short = 'b',
long,
value_name = "REF",
help_heading = "Source options",
verbatim_doc_comment
)
)]
pub diff_base: Option<String>,
#[cfg_attr(
feature = "bin",
arg(default_value_t = false, long, help_heading = "Source options")
)]
pub ignore_index: bool,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "bin", derive(Args))]
#[cfg_attr(
feature = "bin",
group(id = "Clang-format options", multiple = true, required = false)
)]
pub struct FormatOptions {
#[cfg_attr(
feature = "bin",
arg(
short,
long,
default_value = "llvm",
default_missing_value = "file",
num_args = 0..=1,
help_heading = "Clang-format options",
verbatim_doc_comment
)
)]
pub style: String,
#[cfg_attr(
feature = "bin",
arg(
short = 'M',
long,
value_delimiter = '|',
help_heading = "Clang-format options"
)
)]
pub ignore_format: Option<Vec<String>>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "bin", derive(Args))]
#[cfg_attr(
feature = "bin",
group(id = "Clang-tidy options", multiple = true, required = false)
)]
pub struct TidyOptions {
#[cfg_attr(
feature = "bin",
arg(
short = 'D',
long,
value_delimiter = '|',
help_heading = "Clang-tidy options"
)
)]
pub ignore_tidy: Option<Vec<String>>,
#[cfg_attr(feature = "bin", arg(
short = 'c',
long,
default_value = "boost-*,bugprone-*,performance-*,readability-*,portability-*,modernize-*,clang-analyzer-*,cppcoreguidelines-*",
default_missing_value = "",
num_args = 0..=1,
help_heading = "Clang-tidy options",
verbatim_doc_comment
))]
pub tidy_checks: String,
#[cfg_attr(feature = "bin", arg(
short = 'p',
long,
value_name = "PATH",
value_parser = value_parser!(PathBuf),
help_heading = "Clang-tidy options",
))]
pub database: Option<PathBuf>,
#[cfg_attr(feature = "bin", arg(
short = 'x',
long,
action = ArgAction::Append,
help_heading = "Clang-tidy options",
verbatim_doc_comment
))]
pub extra_arg: Vec<String>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "bin", derive(Args))]
#[cfg_attr(
feature = "bin",
group(id = "Feedback options", multiple = true, required = false)
)]
pub struct FeedbackOptions {
#[cfg_attr(feature = "bin", arg(
short = 'g',
long,
default_value = "false",
default_missing_value = "update",
num_args = 0..=1,
help_heading = "Feedback options",
ignore_case = true,
verbatim_doc_comment
))]
pub thread_comments: ThreadComments,
#[cfg_attr(feature = "bin", arg(
short = 't',
long,
default_value_t = true,
action = ArgAction::Set,
value_parser = FalseyValueParser::new(),
help_heading = "Feedback options",
verbatim_doc_comment,
))]
pub no_lgtm: bool,
#[cfg_attr(feature = "bin", arg(
short = 'w',
long,
default_value_t = false,
default_missing_value = "true",
num_args = 0..=1,
action = ArgAction::Set,
value_parser = FalseyValueParser::new(),
help_heading = "Feedback options",
))]
pub step_summary: bool,
#[cfg_attr(feature = "bin", arg(
short = 'a',
long,
default_value_t = true,
action = ArgAction::Set,
value_parser = FalseyValueParser::new(),
help_heading = "Feedback options",
))]
pub file_annotations: bool,
#[cfg_attr(feature = "bin", arg(
short = 'd',
long,
default_value_t = false,
default_missing_value = "true",
num_args = 0..=1,
action = ArgAction::Set,
value_parser = FalseyValueParser::new(),
help_heading = "Feedback options",
))]
pub tidy_review: bool,
#[cfg_attr(feature = "bin", arg(
short = 'm',
long,
default_value_t = false,
default_missing_value = "true",
num_args = 0..=1,
action = ArgAction::Set,
value_parser = FalseyValueParser::new(),
help_heading = "Feedback options",
))]
pub format_review: bool,
#[cfg_attr(feature = "bin", arg(
short = 'R',
long,
default_value_t = false,
default_missing_value = "true",
num_args = 0..=1,
action = ArgAction::Set,
value_parser = FalseyValueParser::new(),
help_heading = "Feedback options",
))]
pub passive_reviews: bool,
}
pub fn convert_extra_arg_val(args: &[String]) -> Vec<String> {
let mut val = args.iter();
if args.len() == 1
&& let Some(v) = val.next()
{
v.trim_matches('\'')
.trim_matches('"')
.split(' ')
.map(|i| i.to_string())
.collect()
} else {
val.map(|i| i.to_string()).collect()
}
}
#[cfg(all(test, feature = "bin"))]
mod test {
#![allow(clippy::unwrap_used)]
use super::{Cli, convert_extra_arg_val};
use clap::Parser;
#[test]
fn error_on_blank_extensions() {
let cli = Cli::try_parse_from(vec!["cpp-linter", "-e", "c,,h"]);
assert!(cli.is_err());
println!("{}", cli.unwrap_err());
}
#[test]
fn extra_arg_0() {
let args = Cli::parse_from(vec!["cpp-linter"]);
let extras = convert_extra_arg_val(&args.tidy_options.extra_arg);
assert!(extras.is_empty());
}
#[test]
fn extra_arg_1() {
let args = Cli::parse_from(vec!["cpp-linter", "--extra-arg='-std=c++17 -Wall'"]);
let extra_args = convert_extra_arg_val(&args.tidy_options.extra_arg);
assert_eq!(extra_args.len(), 2);
assert_eq!(extra_args, ["-std=c++17", "-Wall"])
}
#[test]
fn extra_arg_2() {
let args = Cli::parse_from(vec![
"cpp-linter",
"--extra-arg=-std=c++17",
"--extra-arg=-Wall",
]);
let extra_args = convert_extra_arg_val(&args.tidy_options.extra_arg);
assert_eq!(extra_args.len(), 2);
assert_eq!(extra_args, ["-std=c++17", "-Wall"])
}
}