1mod baseline;
2mod cache;
3pub mod config;
4pub mod graph;
5mod health;
6pub mod html_reporter;
7mod ignore;
8mod model;
9mod plugin;
10pub mod plugins;
11mod registry;
12pub mod reporter;
13mod source;
14pub mod wasm;
15
16pub use baseline::Baseline;
17pub use cache::AnalysisCache;
18pub use config::{Config, DebtWeights, LanguageConfig, Strictness, builtin_language_profile};
19pub use health::{Grade, HealthScore, score_files};
20pub use ignore::filter_ignored;
21pub use model::*;
22pub use plugin::*;
23pub use registry::PluginRegistry;
24pub use reporter::{JsonReporter, LlmContextReporter, Reporter, SarifReporter, TerminalReporter};
25pub use source::*;
26
27pub fn is_zero_f64(v: &f64) -> bool {
29 *v == 0.0
30}
31
32pub fn prioritize_findings(findings: &mut [Finding]) {
35 let per_file: std::collections::HashMap<std::path::PathBuf, usize> = {
36 let mut m = std::collections::HashMap::new();
37 for f in findings.iter() {
38 *m.entry(f.location.path.clone()).or_default() += 1;
39 }
40 m
41 };
42 findings.sort_by(|a, b| {
43 let pa = finding_priority(a, &per_file);
44 let pb = finding_priority(b, &per_file);
45 pb.partial_cmp(&pa).unwrap_or(std::cmp::Ordering::Equal)
46 });
47}
48
49fn finding_priority(
50 f: &Finding,
51 per_file: &std::collections::HashMap<std::path::PathBuf, usize>,
52) -> f64 {
53 let sev = match f.severity {
54 Severity::Error => 3.0,
55 Severity::Warning => 2.0,
56 Severity::Hint => 1.0,
57 };
58 let overshoot = match (f.actual_value, f.threshold) {
59 (Some(a), Some(t)) if t > 0.0 => (a / t).max(1.0),
60 _ => 1.0,
61 };
62 let compound = if *per_file.get(&f.location.path).unwrap_or(&1) > 3 {
63 1.5
64 } else {
65 1.0
66 };
67 sev * overshoot * compound
68}
69
70pub fn findings_json_schema() -> String {
72 let schema = schemars::schema_for!(Vec<Finding>);
73 serde_json::to_string_pretty(&schema).unwrap_or_default()
74}
75
76#[cfg(test)]
77mod tests;