the_code_graph_cli/commands/
risk.rs1use domain::analysis::risk::split_into_segments;
2use domain::error::Result;
3use domain::model::RiskConfig;
4use domain::use_cases::risk::RiskUseCase;
5
6use crate::commands::helpers::open_graph;
7use crate::commands::RiskArgs;
8use crate::config::load_config;
9use crate::output::{print, OutputFormat, RiskScoreDetail};
10
11pub fn run_risk(args: &RiskArgs, output_format: OutputFormat) -> Result<()> {
12 let (store, root) = open_graph()?;
13 let config = load_config(&root)?;
14
15 let mut risk_config = RiskConfig::default();
17
18 if let Some(rc) = &config.risk {
19 if let Some(w) = rc.weight_criticality {
21 risk_config.weights.criticality = w;
22 }
23 if let Some(w) = rc.weight_coupling {
24 risk_config.weights.coupling = w;
25 }
26 if let Some(w) = rc.weight_test_gap {
27 risk_config.weights.test_gap = w;
28 }
29 if let Some(w) = rc.weight_sensitivity {
30 risk_config.weights.sensitivity = w;
31 }
32
33 if let Some(extra) = &rc.extra_security_patterns {
35 risk_config.security_patterns.extend(extra.clone());
36 }
37 if let Some(excluded) = &rc.excluded_security_patterns {
38 risk_config
39 .security_patterns
40 .retain(|p| !excluded.contains(p));
41 }
42 }
43
44 risk_config.weights = risk_config.weights.normalized();
46
47 let uc = RiskUseCase::new(store);
48
49 if let Some(ref target) = args.target {
50 let score = uc.score_symbol(target, &risk_config)?;
52 let segments = split_into_segments(&score.qualified_name);
54 let lower_patterns: Vec<String> = risk_config
55 .security_patterns
56 .iter()
57 .map(|p| p.to_lowercase())
58 .collect();
59 let matched_patterns: Vec<String> = lower_patterns
60 .iter()
61 .filter(|pat| segments.iter().any(|seg| seg.starts_with(pat.as_str())))
62 .cloned()
63 .collect();
64 let detail = RiskScoreDetail {
65 score,
66 matched_patterns,
67 weights: risk_config.weights.clone(),
68 };
69 print(&detail, output_format);
70 } else if args.symbols {
71 let analysis = uc.analyze(&risk_config)?;
73 let filtered: Vec<_> = analysis
74 .symbol_scores
75 .into_iter()
76 .filter(|s| s.composite >= args.min_score)
77 .take(args.limit)
78 .collect();
79 print(&filtered, output_format);
80 } else {
81 let analysis = uc.analyze(&risk_config)?;
83 let mut filtered_analysis = analysis;
84 filtered_analysis
85 .file_scores
86 .retain(|f| f.composite >= args.min_score);
87 filtered_analysis.file_scores.truncate(args.limit);
88 print(&filtered_analysis, output_format);
89 }
90 Ok(())
91}