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