1#![warn(missing_docs)]
14
15mod app;
16mod engine;
17mod graph;
18mod languages;
19mod model;
20mod reporting;
21mod rules;
22
23use app::cli;
24use app::commands::{
25 baseline as baseline_command, completions as completions_command,
26 complexity as complexity_command, config as config_command, dead as dead_command,
27 doctor as doctor_command, duplication as duplication_command, explain as explain_command,
28 fix as fix_command, graph as graph_command, hotspots as hotspots_command, init as init_command,
29 quality as quality_command, repository_view as repository_view_command, rules as rules_command,
30 structure as structure_command, summary as summary_command, update as update_command,
31 workflow as workflow_command,
32};
33use engine::{
34 analysis_cache, analysis_engine, discovery, finding_controls, preview, repository_defaults,
35 semantic_diff, source_analysis_context,
36};
37use graph::{
38 architecture as architecture_graph, dead_analysis, dependency as dependency_graph,
39 export_analysis,
40};
41use languages::{
42 cpp::rules as cpp_rules,
43 csharp::rules as csharp_rules,
44 css::rules as css_rules,
45 dart::{
46 analyzer_config as dart_analyzer_config, core as dart_core_rules,
47 flutter_build as dart_flutter_build, lifecycle as dart_lifecycle,
48 model_contracts as dart_model_contracts, model_duplication as dart_model_duplication,
49 mvvm as dart_mvvm_rules, null_assertion as dart_null_assertion, oop as dart_oop_rules,
50 package_cycle as dart_package_cycle, performance as dart_performance,
51 reliability as dart_reliability, security as dart_security,
52 },
53 functions as function_sources,
54 go::rules as go_rules,
55 html::rules as html_rules,
56 java::rules as java_rules,
57 lua::rules as lua_rules,
58 mojo::rules as mojo_rules,
59 nim::rules as nim_rules,
60 python::rules as python_rules,
61 rust::rules as rust_rules,
62 sql::rules as sql_rules,
63 typescript::rules as typescript_rules,
64 wren::rules as wren_rules,
65};
66use model::{config, findings};
67use reporting::{report, sarif_ingestion};
68use rules::{
69 advanced_duplication, ai_security as ai_security_rules, cargo as cargo_rules,
70 catalog as rule_catalog, clean_code as clean_code_rules, complexity, constant_arguments,
71 diff_quality as diff_quality_rules, duplication, feature_flags,
72 frameworks::{
73 fastapi as fastapi_rules, flutter as flutter_quality_rules, pixijs as pixijs_rules,
74 svelte as svelte_rules,
75 },
76 generated_code_risks, generic as generic_rules, github_actions as github_actions_rules,
77 layout as layout_rules, object_soup as object_soup_rules, oop as oop_rules,
78 pattern as pattern_rules, regex as regex_rules,
79 repository_contracts as repository_contract_rules, runtime_bootstrap as runtime_bootstrap_rule,
80 sql_inline as sql_inline_rule, structure, trivial_wrappers, ui_consistency,
81 yagni as yagni_rules, yagni_call_index, yagni_concentration,
82};
83
84use std::ffi::OsString;
85use std::process::ExitCode;
86
87pub fn run(arguments: impl IntoIterator<Item = OsString>) -> ExitCode {
95 match cli::parse(arguments) {
96 Ok(cli::Command::Help) => {
97 println!("{}", cli::usage());
98 ExitCode::SUCCESS
99 }
100 Ok(cli::Command::Version) => {
101 println!("cb {}", env!("CARGO_PKG_VERSION"));
102 println!("runtime: Rust");
103 ExitCode::SUCCESS
104 }
105 Ok(cli::Command::Summary(options)) => summary_exit(summary_command::execute(&options)),
106 Ok(cli::Command::Baseline(options)) => match baseline_command::execute(&options) {
107 Ok(baseline_command::BaselineOutcome::Success) => ExitCode::SUCCESS,
108 Ok(baseline_command::BaselineOutcome::Empty) => ExitCode::from(1),
109 Err(error) => {
110 eprintln!("{error}");
111 ExitCode::from(2)
112 }
113 },
114 Ok(cli::Command::Fix(options)) => match fix_command::execute(&options) {
115 Ok(fix_command::FixOutcome::Success) => ExitCode::SUCCESS,
116 Ok(fix_command::FixOutcome::Empty) => ExitCode::from(1),
117 Err(error) => {
118 eprintln!("{error}");
119 ExitCode::from(2)
120 }
121 },
122 Ok(cli::Command::Quality(options, view)) => quality_exit(&options, view),
123 Ok(cli::Command::Review(options, view)) => review_exit(&options, view),
124 Ok(cli::Command::Update(options)) => match update_command::execute(&options) {
125 Ok(update_command::UpdateOutcome::Success) => ExitCode::SUCCESS,
126 Ok(update_command::UpdateOutcome::Refused) => ExitCode::from(2),
127 Ok(update_command::UpdateOutcome::Failed(code)) => ExitCode::from(code),
128 Err(error) => {
129 eprintln!("{error}");
130 ExitCode::from(2)
131 }
132 },
133 Ok(cli::Command::ConfigFiles(options)) => match preview::execute(&options) {
134 Ok(()) => ExitCode::SUCCESS,
135 Err(error) => {
136 eprintln!("{error}");
137 ExitCode::from(2)
138 }
139 },
140 Ok(cli::Command::Completions(shell)) => {
141 completions_command::execute(&shell);
142 ExitCode::SUCCESS
143 }
144 Ok(cli::Command::Dead(options)) => match dead_command::execute(&options) {
145 Ok(dead_command::DeadOutcome::Success) => ExitCode::SUCCESS,
146 Ok(dead_command::DeadOutcome::Empty) => ExitCode::from(1),
147 Ok(dead_command::DeadOutcome::Findings) => ExitCode::from(2),
148 Err(error) => {
149 eprintln!("{error}");
150 ExitCode::from(2)
151 }
152 },
153 Ok(cli::Command::Graph(options)) => match graph_command::execute(&options) {
154 Ok(graph_command::GraphOutcome::Success) => ExitCode::SUCCESS,
155 Ok(graph_command::GraphOutcome::Empty) => ExitCode::from(1),
156 Err(error) => {
157 eprintln!("{error}");
158 ExitCode::from(2)
159 }
160 },
161 Ok(cli::Command::Rules(options)) => {
162 if options.targets.is_empty() {
163 match rules_command::execute(&options.common) {
164 Ok(()) => ExitCode::SUCCESS,
165 Err(error) => {
166 eprintln!("{error}");
167 ExitCode::from(2)
168 }
169 }
170 } else {
171 repository_view_exit(Ok(explain_command::execute(&options)))
172 }
173 }
174 Ok(cli::Command::Inspect(options, view)) => match view {
175 cli::InspectView::Details => {
176 repository_view_exit(repository_view_command::execute_inspect(&options))
177 }
178 cli::InspectView::Related => {
179 repository_view_exit(repository_view_command::execute_related(&options))
180 }
181 cli::InspectView::Why => {
182 repository_view_exit(repository_view_command::execute_why(&options))
183 }
184 cli::InspectView::Path => {
185 repository_view_exit(repository_view_command::execute_path(&options))
186 }
187 },
188 Ok(cli::Command::Config(options)) => match config_command::execute(&options) {
189 Ok(()) => ExitCode::SUCCESS,
190 Err(error) => {
191 eprintln!("{error}");
192 ExitCode::from(2)
193 }
194 },
195 Ok(cli::Command::Doctor(options)) => match doctor_command::execute(&options) {
196 Ok(()) => ExitCode::SUCCESS,
197 Err(error) => {
198 eprintln!("{error}");
199 ExitCode::from(2)
200 }
201 },
202 Ok(cli::Command::Duplication(options)) => {
203 duplication_exit(duplication_command::execute(&options))
204 }
205 Ok(cli::Command::Init(options)) => match init_command::execute(&options) {
206 Ok(init_command::InitOutcome::Created) => ExitCode::SUCCESS,
207 Ok(init_command::InitOutcome::Exists) => ExitCode::from(1),
208 Err(error) => {
209 eprintln!("{error}");
210 ExitCode::from(2)
211 }
212 },
213 Err(error) => {
214 if error.starts_with("Unknown Code Buster command:")
215 || error == "--no-cache cannot be combined with --cache-dir"
216 {
217 eprintln!("{error}\n{}", cli::argument_usage());
218 } else {
219 eprintln!("{error}");
220 }
221 ExitCode::from(2)
222 }
223 }
224}
225
226fn review_exit(options: &cli::PreviewOptions, view: cli::ReviewView) -> ExitCode {
227 match view {
228 cli::ReviewView::Findings => {
229 summary_exit(summary_command::execute_report(options, "review"))
230 }
231 cli::ReviewView::Plan => unit_exit(workflow_command::execute_plan(options)),
232 cli::ReviewView::Actions => match workflow_command::execute_actions(options) {
233 Ok(workflow_command::WorkflowOutcome::Success) => ExitCode::SUCCESS,
234 Ok(workflow_command::WorkflowOutcome::Findings) => ExitCode::from(2),
235 Err(error) => error_exit(error),
236 },
237 }
238}
239
240fn quality_exit(options: &cli::PreviewOptions, view: cli::QualityView) -> ExitCode {
241 match view {
242 cli::QualityView::Overview => match quality_command::execute_quality(options) {
243 Ok(quality_command::QualityOutcome::Success) => ExitCode::SUCCESS,
244 Ok(quality_command::QualityOutcome::Findings) => ExitCode::from(2),
245 Err(error) => error_exit(error),
246 },
247 cli::QualityView::Score => unit_exit(quality_command::execute_score(options)),
248 cli::QualityView::Complexity => match complexity_command::execute(options) {
249 Ok(complexity_command::ComplexityOutcome::Success) => ExitCode::SUCCESS,
250 Ok(complexity_command::ComplexityOutcome::Empty) => ExitCode::from(1),
251 Ok(complexity_command::ComplexityOutcome::Findings) => ExitCode::from(2),
252 Err(error) => error_exit(error),
253 },
254 cli::QualityView::Hotspots => match hotspots_command::execute(options) {
255 Ok(hotspots_command::HotspotsOutcome::Success) => ExitCode::SUCCESS,
256 Ok(hotspots_command::HotspotsOutcome::Empty) => ExitCode::from(1),
257 Err(error) => error_exit(error),
258 },
259 cli::QualityView::Structure => match structure_command::execute(options) {
260 Ok(structure_command::StructureOutcome::Success) => ExitCode::SUCCESS,
261 Ok(structure_command::StructureOutcome::Empty) => ExitCode::from(1),
262 Ok(structure_command::StructureOutcome::Findings) => ExitCode::from(2),
263 Err(error) => error_exit(error),
264 },
265 }
266}
267
268fn duplication_exit(result: Result<duplication_command::DuplicationOutcome, String>) -> ExitCode {
269 match result {
270 Ok(duplication_command::DuplicationOutcome::Success) => ExitCode::SUCCESS,
271 Ok(duplication_command::DuplicationOutcome::Empty) => ExitCode::from(1),
272 Ok(duplication_command::DuplicationOutcome::Findings) => ExitCode::from(2),
273 Err(error) => error_exit(error),
274 }
275}
276
277fn unit_exit(result: Result<(), String>) -> ExitCode {
278 match result {
279 Ok(()) => ExitCode::SUCCESS,
280 Err(error) => error_exit(error),
281 }
282}
283
284fn error_exit(error: String) -> ExitCode {
285 eprintln!("{error}");
286 ExitCode::from(2)
287}
288
289fn summary_exit(result: Result<summary_command::SummaryOutcome, String>) -> ExitCode {
290 match result {
291 Ok(summary_command::SummaryOutcome::Success) => ExitCode::SUCCESS,
292 Ok(summary_command::SummaryOutcome::Empty) => ExitCode::from(1),
293 Ok(summary_command::SummaryOutcome::Findings) => ExitCode::from(2),
294 Err(error) => {
295 eprintln!("{error}");
296 ExitCode::from(2)
297 }
298 }
299}
300
301fn repository_view_exit(
302 result: Result<repository_view_command::RepositoryViewOutcome, String>,
303) -> ExitCode {
304 match result {
305 Ok(repository_view_command::RepositoryViewOutcome::Success) => ExitCode::SUCCESS,
306 Ok(repository_view_command::RepositoryViewOutcome::Findings) => ExitCode::from(2),
307 Ok(repository_view_command::RepositoryViewOutcome::NotFound) => ExitCode::from(1),
308 Ok(repository_view_command::RepositoryViewOutcome::Usage) => ExitCode::from(64),
309 Err(error) => {
310 eprintln!("{error}");
311 ExitCode::from(2)
312 }
313 }
314}