1use std::{fmt::Display, path::PathBuf};
2
3use clap::ArgMatches;
4
5use super::convert_extra_arg_val;
6use crate::{clang_tools::clang_tidy::CompilationUnit, common_fs::FileFilter};
7
8#[derive(PartialEq, Clone, Debug, Default)]
10pub enum LinesChangedOnly {
11 #[default]
13 Off,
14 Diff,
16 On,
18}
19
20impl LinesChangedOnly {
21 fn from_string(val: &str) -> LinesChangedOnly {
22 match val {
23 "true" | "on" | "1" => LinesChangedOnly::On,
24 "diff" => LinesChangedOnly::Diff,
25 _ => LinesChangedOnly::Off,
26 }
27 }
28}
29
30impl Display for LinesChangedOnly {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 LinesChangedOnly::Off => write!(f, "false"),
34 LinesChangedOnly::Diff => write!(f, "diff"),
35 LinesChangedOnly::On => write!(f, "true"),
36 }
37 }
38}
39
40pub struct Cli {
42 pub version: String,
43 pub verbosity: bool,
44 pub extensions: Vec<String>,
45 pub repo_root: String,
46 pub lines_changed_only: LinesChangedOnly,
47 pub files_changed_only: bool,
48 pub ignore: Vec<String>,
49 pub style: String,
50 pub ignore_format: Option<Vec<String>>,
51 pub ignore_tidy: Option<Vec<String>>,
52 pub tidy_checks: String,
53 pub database: Option<PathBuf>,
54 pub extra_arg: Vec<String>,
55 pub thread_comments: ThreadComments,
56 pub no_lgtm: bool,
57 pub step_summary: bool,
58 pub file_annotations: bool,
59 pub not_ignored: Option<Vec<String>>,
60 pub tidy_review: bool,
61 pub format_review: bool,
62 pub passive_reviews: bool,
63}
64
65impl From<&ArgMatches> for Cli {
66 fn from(args: &ArgMatches) -> Self {
68 let ignore = args
69 .get_many::<String>("ignore")
70 .unwrap()
71 .map(|s| s.to_owned())
72 .collect::<Vec<_>>();
73 let ignore_tidy = args
74 .get_many::<String>("ignore-tidy")
75 .map(|val| val.map(|s| s.to_owned()).collect::<Vec<_>>());
76 let ignore_format = args
77 .get_many::<String>("ignore-format")
78 .map(|val| val.map(|s| s.to_owned()).collect::<Vec<_>>());
79 let extra_arg = convert_extra_arg_val(args);
80
81 let lines_changed_only = LinesChangedOnly::from_string(
82 args.get_one::<String>("lines-changed-only")
83 .unwrap()
84 .as_str(),
85 );
86
87 let thread_comments = ThreadComments::from_string(
88 args.get_one::<String>("thread-comments").unwrap().as_str(),
89 );
90
91 let extensions = args
92 .get_many::<String>("extensions")
93 .unwrap()
94 .map(|s| s.to_string())
95 .collect::<Vec<_>>();
96
97 Self {
98 version: args.get_one::<String>("version").unwrap().to_owned(),
99 verbosity: args.get_one::<String>("verbosity").unwrap().as_str() == "debug",
100 extensions,
101 repo_root: args.get_one::<String>("repo-root").unwrap().to_owned(),
102 lines_changed_only,
103 files_changed_only: args.get_flag("files-changed-only"),
104 ignore,
105 style: args.get_one::<String>("style").unwrap().to_owned(),
106 ignore_format,
107 ignore_tidy,
108 tidy_checks: args.get_one::<String>("tidy-checks").unwrap().to_owned(),
109 database: args.get_one::<PathBuf>("database").map(|v| v.to_owned()),
110 extra_arg,
111 no_lgtm: args.get_flag("no-lgtm"),
112 step_summary: args.get_flag("step-summary"),
113 thread_comments,
114 file_annotations: args.get_flag("file-annotations"),
115 not_ignored: args
116 .get_many::<String>("files")
117 .map(|files| Vec::from_iter(files.map(|v| v.to_owned()))),
118 tidy_review: args.get_flag("tidy-review"),
119 format_review: args.get_flag("format-review"),
120 passive_reviews: args.get_flag("passive-reviews"),
121 }
122 }
123}
124
125#[derive(PartialEq, Clone, Debug)]
127pub enum ThreadComments {
128 On,
130 Off,
132 Update,
135}
136
137impl ThreadComments {
138 fn from_string(val: &str) -> ThreadComments {
139 match val {
140 "true" | "on" | "1" => ThreadComments::On,
141 "update" => ThreadComments::Update,
142 _ => ThreadComments::Off,
143 }
144 }
145}
146
147impl Display for ThreadComments {
148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 match self {
150 ThreadComments::On => write!(f, "true"),
151 ThreadComments::Off => write!(f, "false"),
152 ThreadComments::Update => write!(f, "update"),
153 }
154 }
155}
156
157#[derive(Debug, Clone, Default)]
160pub struct ClangParams {
161 pub tidy_checks: String,
162 pub lines_changed_only: LinesChangedOnly,
163 pub database: Option<PathBuf>,
164 pub extra_args: Vec<String>,
165 pub database_json: Option<Vec<CompilationUnit>>,
166 pub style: String,
167 pub clang_tidy_command: Option<PathBuf>,
168 pub clang_format_command: Option<PathBuf>,
169 pub tidy_filter: Option<FileFilter>,
170 pub format_filter: Option<FileFilter>,
171 pub tidy_review: bool,
172 pub format_review: bool,
173}
174
175impl From<&Cli> for ClangParams {
176 fn from(args: &Cli) -> Self {
178 ClangParams {
179 tidy_checks: args.tidy_checks.clone(),
180 lines_changed_only: args.lines_changed_only.clone(),
181 database: args.database.clone(),
182 extra_args: args.extra_arg.clone(),
183 database_json: None,
184 style: args.style.clone(),
185 clang_tidy_command: None,
186 clang_format_command: None,
187 tidy_filter: args
188 .ignore_tidy
189 .as_ref()
190 .map(|ignore_tidy| FileFilter::new(ignore_tidy, args.extensions.clone())),
191 format_filter: args
192 .ignore_format
193 .as_ref()
194 .map(|ignore_format| FileFilter::new(ignore_format, args.extensions.clone())),
195 tidy_review: args.tidy_review,
196 format_review: args.format_review,
197 }
198 }
199}
200
201pub struct FeedbackInput {
204 pub thread_comments: ThreadComments,
205 pub no_lgtm: bool,
206 pub step_summary: bool,
207 pub file_annotations: bool,
208 pub style: String,
209 pub tidy_review: bool,
210 pub format_review: bool,
211 pub passive_reviews: bool,
212}
213
214impl From<&Cli> for FeedbackInput {
215 fn from(args: &Cli) -> Self {
217 FeedbackInput {
218 style: args.style.clone(),
219 no_lgtm: args.no_lgtm,
220 step_summary: args.step_summary,
221 thread_comments: args.thread_comments.clone(),
222 file_annotations: args.file_annotations,
223 tidy_review: args.tidy_review,
224 format_review: args.format_review,
225 passive_reviews: args.passive_reviews,
226 }
227 }
228}
229
230impl Default for FeedbackInput {
231 fn default() -> Self {
233 FeedbackInput {
234 thread_comments: ThreadComments::Off,
235 no_lgtm: true,
236 step_summary: false,
237 file_annotations: true,
238 style: "llvm".to_string(),
239 tidy_review: false,
240 format_review: false,
241 passive_reviews: false,
242 }
243 }
244}
245
246#[cfg(test)]
247mod test {
248 use crate::cli::get_arg_parser;
249
250 use super::{Cli, LinesChangedOnly, ThreadComments};
251
252 #[test]
253 fn parse_positional() {
254 let parser = get_arg_parser();
255 let args = parser.get_matches_from(["cpp-linter", "file1.c", "file2.h"]);
256 let cli = Cli::from(&args);
257 let not_ignored = cli.not_ignored.expect("failed to parse positional args");
258 assert!(!not_ignored.is_empty());
259 assert!(not_ignored.contains(&String::from("file1.c")));
260 assert!(not_ignored.contains(&String::from("file2.h")));
261 }
262
263 #[test]
264 fn display_lines_changed_only_enum() {
265 let input = "diff".to_string();
266 assert_eq!(
267 LinesChangedOnly::from_string(&input),
268 LinesChangedOnly::Diff
269 );
270 assert_eq!(format!("{}", LinesChangedOnly::Diff), input);
271 }
272
273 #[test]
274 fn display_thread_comments_enum() {
275 let input = "false".to_string();
276 assert_eq!(ThreadComments::from_string(&input), ThreadComments::Off);
277 assert_eq!(format!("{}", ThreadComments::Off), input);
278 }
279}