Skip to main content

code_buster/
lib.rs

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}