Skip to main content

code_buster/
lib.rs

1//! Code Buster is a repository-aware static analyzer for AI-assisted development.
2//!
3//! Most users should install and run the `cb` command-line application:
4//!
5//! ```text
6//! cargo install code-buster --locked
7//! cb summary
8//! ```
9//!
10//! See the [Code Buster documentation](https://toolbunker.dev/codebuster/docs/)
11//! for installation, configuration, commands, and rule documentation.
12
13#![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
87/// Runs Code Buster with the supplied command-line arguments.
88///
89/// The arguments must not include the executable name. The returned exit code
90/// is the status produced by the selected Code Buster command.
91///
92/// Most applications should invoke the installed `cb` executable instead of
93/// using this library entry point directly.
94pub 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}