Skip to main content

_diffctx/utility/
importance.rs

1//! File-importance prior I(f) for impact-need scoring.
2//!
3//! See `crate::config::importance` for the rationale behind the chosen
4//! constants. This module computes I(f) ∈ (0, 1] from the fragment path
5//! using the path/stem patterns defined in config.
6
7use std::path::Path;
8use std::sync::Arc;
9
10use rustc_hash::{FxHashMap, FxHashSet};
11
12use crate::config::importance::{
13    DEFAULT_IMPORTANCE, GENERATED_CAP, GENERATED_DIRS, PERIPHERAL_CAP, PERIPHERAL_DIRS,
14    PERIPHERAL_STEMS, PERIPHERAL_SUFFIXES,
15};
16use crate::types::Fragment;
17
18fn path_components_lower(path: &Path) -> FxHashSet<String> {
19    path.components()
20        .filter_map(|c| c.as_os_str().to_str())
21        .map(|s| s.to_lowercase())
22        .collect()
23}
24
25fn is_peripheral(path: &Path) -> bool {
26    let parts = path_components_lower(path);
27    if PERIPHERAL_DIRS.iter().any(|d| parts.contains(*d)) {
28        return true;
29    }
30    let stem = path
31        .file_stem()
32        .and_then(|s| s.to_str())
33        .unwrap_or("")
34        .to_lowercase();
35    if PERIPHERAL_STEMS.iter().any(|p| stem.starts_with(p)) {
36        return true;
37    }
38    if PERIPHERAL_SUFFIXES.iter().any(|s| stem.ends_with(s)) {
39        return true;
40    }
41    false
42}
43
44fn is_generated(path: &Path) -> bool {
45    let parts = path_components_lower(path);
46    GENERATED_DIRS.iter().any(|d| parts.contains(*d))
47}
48
49/// Compute I(f) for every fragment path in the universe.
50///
51/// Returns a map from path string to importance ∈ {GENERATED_CAP,
52/// PERIPHERAL_CAP, DEFAULT_IMPORTANCE}. Generated takes precedence over
53/// peripheral when both apply (a more conservative downweight).
54pub fn compute_file_importance(fragments: &[Fragment]) -> FxHashMap<Arc<str>, f64> {
55    let mut seen: FxHashSet<Arc<str>> = FxHashSet::default();
56    let mut out: FxHashMap<Arc<str>, f64> = FxHashMap::default();
57    for f in fragments {
58        let path_str = f.path();
59        let key: Arc<str> = Arc::from(path_str);
60        if !seen.insert(key.clone()) {
61            continue;
62        }
63        let path = Path::new(path_str);
64        let imp = if is_generated(path) {
65            GENERATED_CAP
66        } else if is_peripheral(path) {
67            PERIPHERAL_CAP
68        } else {
69            DEFAULT_IMPORTANCE
70        };
71        out.insert(key, imp);
72    }
73    out
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    fn classify(path_str: &str) -> f64 {
81        let path = Path::new(path_str);
82        if is_generated(path) {
83            GENERATED_CAP
84        } else if is_peripheral(path) {
85            PERIPHERAL_CAP
86        } else {
87            DEFAULT_IMPORTANCE
88        }
89    }
90
91    #[test]
92    fn default_for_production_paths() {
93        assert_eq!(classify("src/handler.ts"), DEFAULT_IMPORTANCE);
94        assert_eq!(classify("lib/auth/login.rs"), DEFAULT_IMPORTANCE);
95        assert_eq!(classify("server/main.go"), DEFAULT_IMPORTANCE);
96    }
97
98    #[test]
99    fn peripheral_for_examples_and_demo() {
100        assert_eq!(classify("examples/parsing.ts"), PERIPHERAL_CAP);
101        assert_eq!(classify("demo/usage.py"), PERIPHERAL_CAP);
102        assert_eq!(classify("vendor/lib/foo.go"), PERIPHERAL_CAP);
103        assert_eq!(classify("fixtures/sample.json"), PERIPHERAL_CAP);
104        assert_eq!(classify("docs/guide.md"), PERIPHERAL_CAP);
105    }
106
107    #[test]
108    fn peripheral_for_stem_patterns() {
109        assert_eq!(classify("tests/example_usage.py"), PERIPHERAL_CAP);
110        assert_eq!(classify("src/module_demo.rs"), PERIPHERAL_CAP);
111        assert_eq!(classify("scripts/sample_run.sh"), PERIPHERAL_CAP);
112    }
113
114    #[test]
115    fn generated_takes_precedence() {
116        assert_eq!(classify("generated/protobuf.rs"), GENERATED_CAP);
117        assert_eq!(classify("src/__generated__/api.ts"), GENERATED_CAP);
118        // generated/ in any component triggers, even nested under examples
119        assert_eq!(classify("examples/__generated__/foo.py"), GENERATED_CAP);
120    }
121
122    #[test]
123    fn case_insensitive_directory_matching() {
124        assert_eq!(classify("Examples/foo.py"), PERIPHERAL_CAP);
125        assert_eq!(classify("VENDOR/lib.go"), PERIPHERAL_CAP);
126        assert_eq!(classify("Generated/api.ts"), GENERATED_CAP);
127    }
128
129    #[test]
130    fn ordering_is_stable_under_priors() {
131        // Production caller is at least 6.6x more important than peripheral.
132        assert!(DEFAULT_IMPORTANCE / PERIPHERAL_CAP >= 6.0);
133        // Peripheral is at least 1.4x more important than generated.
134        assert!(PERIPHERAL_CAP / GENERATED_CAP >= 1.4);
135        // Both caps are well below 1 (the cap must bite).
136        assert!(GENERATED_CAP < 0.25);
137        assert!(PERIPHERAL_CAP < 0.25);
138        // Both caps are above 0 (schema-impact signal retained).
139        assert!(GENERATED_CAP > 0.0);
140        assert!(PERIPHERAL_CAP > 0.0);
141    }
142}