use std::{fmt::Display, path::PathBuf};
use clap::ArgMatches;
use super::convert_extra_arg_val;
use crate::{clang_tools::clang_tidy::CompilationUnit, common_fs::FileFilter};
#[derive(PartialEq, Clone, Debug, Default)]
pub enum LinesChangedOnly {
#[default]
Off,
Diff,
On,
}
impl LinesChangedOnly {
fn from_string(val: &str) -> LinesChangedOnly {
match val {
"true" | "on" | "1" => LinesChangedOnly::On,
"diff" => LinesChangedOnly::Diff,
_ => LinesChangedOnly::Off,
}
}
}
impl Display for LinesChangedOnly {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LinesChangedOnly::Off => write!(f, "false"),
LinesChangedOnly::Diff => write!(f, "diff"),
LinesChangedOnly::On => write!(f, "true"),
}
}
}
pub struct Cli {
pub version: String,
pub verbosity: bool,
pub extensions: Vec<String>,
pub repo_root: String,
pub lines_changed_only: LinesChangedOnly,
pub files_changed_only: bool,
pub ignore: Vec<String>,
pub style: String,
pub ignore_format: Option<Vec<String>>,
pub ignore_tidy: Option<Vec<String>>,
pub tidy_checks: String,
pub database: Option<PathBuf>,
pub extra_arg: Option<Vec<String>>,
pub thread_comments: ThreadComments,
pub no_lgtm: bool,
pub step_summary: bool,
pub file_annotations: bool,
pub not_ignored: Option<Vec<String>>,
pub tidy_review: bool,
pub format_review: bool,
pub passive_reviews: bool,
}
impl From<&ArgMatches> for Cli {
fn from(args: &ArgMatches) -> Self {
let ignore = args
.get_many::<String>("ignore")
.unwrap()
.map(|s| s.to_owned())
.collect::<Vec<_>>();
let ignore_tidy = args
.get_many::<String>("ignore-tidy")
.map(|val| val.map(|s| s.to_owned()).collect::<Vec<_>>());
let ignore_format = args
.get_many::<String>("ignore-format")
.map(|val| val.map(|s| s.to_owned()).collect::<Vec<_>>());
let extra_arg = convert_extra_arg_val(args);
let lines_changed_only = LinesChangedOnly::from_string(
args.get_one::<String>("lines-changed-only")
.unwrap()
.as_str(),
);
let thread_comments = ThreadComments::from_string(
args.get_one::<String>("thread-comments").unwrap().as_str(),
);
let extensions = args
.get_many::<String>("extensions")
.unwrap()
.map(|s| s.to_string())
.collect::<Vec<_>>();
Self {
version: args.get_one::<String>("version").unwrap().to_owned(),
verbosity: args.get_one::<String>("verbosity").unwrap().as_str() == "debug",
extensions,
repo_root: args.get_one::<String>("repo-root").unwrap().to_owned(),
lines_changed_only,
files_changed_only: args.get_flag("files-changed-only"),
ignore,
style: args.get_one::<String>("style").unwrap().to_owned(),
ignore_format,
ignore_tidy,
tidy_checks: args.get_one::<String>("tidy-checks").unwrap().to_owned(),
database: args.get_one::<PathBuf>("database").map(|v| v.to_owned()),
extra_arg,
no_lgtm: args.get_flag("no-lgtm"),
step_summary: args.get_flag("step-summary"),
thread_comments,
file_annotations: args.get_flag("file-annotations"),
not_ignored: args
.get_many::<String>("files")
.map(|files| Vec::from_iter(files.map(|v| v.to_owned()))),
tidy_review: args.get_flag("tidy-review"),
format_review: args.get_flag("format-review"),
passive_reviews: args.get_flag("passive-reviews"),
}
}
}
#[derive(PartialEq, Clone, Debug)]
pub enum ThreadComments {
On,
Off,
Update,
}
impl ThreadComments {
fn from_string(val: &str) -> ThreadComments {
match val {
"true" | "on" | "1" => ThreadComments::On,
"update" => ThreadComments::Update,
_ => ThreadComments::Off,
}
}
}
impl Display for ThreadComments {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ThreadComments::On => write!(f, "true"),
ThreadComments::Off => write!(f, "false"),
ThreadComments::Update => write!(f, "update"),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ClangParams {
pub tidy_checks: String,
pub lines_changed_only: LinesChangedOnly,
pub database: Option<PathBuf>,
pub extra_args: Option<Vec<String>>,
pub database_json: Option<Vec<CompilationUnit>>,
pub style: String,
pub clang_tidy_command: Option<PathBuf>,
pub clang_format_command: Option<PathBuf>,
pub tidy_filter: Option<FileFilter>,
pub format_filter: Option<FileFilter>,
pub tidy_review: bool,
pub format_review: bool,
}
impl From<&Cli> for ClangParams {
fn from(args: &Cli) -> Self {
ClangParams {
tidy_checks: args.tidy_checks.clone(),
lines_changed_only: args.lines_changed_only.clone(),
database: args.database.clone(),
extra_args: args.extra_arg.clone(),
database_json: None,
style: args.style.clone(),
clang_tidy_command: None,
clang_format_command: None,
tidy_filter: args
.ignore_tidy
.as_ref()
.map(|ignore_tidy| FileFilter::new(ignore_tidy, args.extensions.clone())),
format_filter: args
.ignore_format
.as_ref()
.map(|ignore_format| FileFilter::new(ignore_format, args.extensions.clone())),
tidy_review: args.tidy_review,
format_review: args.format_review,
}
}
}
pub struct FeedbackInput {
pub thread_comments: ThreadComments,
pub no_lgtm: bool,
pub step_summary: bool,
pub file_annotations: bool,
pub style: String,
pub tidy_review: bool,
pub format_review: bool,
pub passive_reviews: bool,
}
impl From<&Cli> for FeedbackInput {
fn from(args: &Cli) -> Self {
FeedbackInput {
style: args.style.clone(),
no_lgtm: args.no_lgtm,
step_summary: args.step_summary,
thread_comments: args.thread_comments.clone(),
file_annotations: args.file_annotations,
tidy_review: args.tidy_review,
format_review: args.format_review,
passive_reviews: args.passive_reviews,
}
}
}
impl Default for FeedbackInput {
fn default() -> Self {
FeedbackInput {
thread_comments: ThreadComments::Off,
no_lgtm: true,
step_summary: false,
file_annotations: true,
style: "llvm".to_string(),
tidy_review: false,
format_review: false,
passive_reviews: false,
}
}
}
#[cfg(test)]
mod test {
use crate::cli::get_arg_parser;
use super::{Cli, LinesChangedOnly, ThreadComments};
#[test]
fn parse_positional() {
let parser = get_arg_parser();
let args = parser.get_matches_from(["cpp-linter", "file1.c", "file2.h"]);
let cli = Cli::from(&args);
let not_ignored = cli.not_ignored.expect("failed to parse positional args");
assert!(!not_ignored.is_empty());
assert!(not_ignored.contains(&String::from("file1.c")));
assert!(not_ignored.contains(&String::from("file2.h")));
}
#[test]
fn display_lines_changed_only_enum() {
let input = "diff".to_string();
assert_eq!(
LinesChangedOnly::from_string(&input),
LinesChangedOnly::Diff
);
assert_eq!(format!("{}", LinesChangedOnly::Diff), input);
}
#[test]
fn display_thread_comments_enum() {
let input = "false".to_string();
assert_eq!(ThreadComments::from_string(&input), ThreadComments::Off);
assert_eq!(format!("{}", ThreadComments::Off), input);
}
}