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