Skip to main content

lean_ctx/core/
preservation.rs

1use crate::core::deps;
2use crate::core::signatures;
3
4#[derive(Debug, Clone, Default)]
5pub struct PreservationScore {
6    pub functions_total: usize,
7    pub functions_preserved: usize,
8    pub exports_total: usize,
9    pub exports_preserved: usize,
10    pub imports_total: usize,
11    pub imports_preserved: usize,
12}
13
14impl PreservationScore {
15    pub fn function_rate(&self) -> f64 {
16        if self.functions_total == 0 {
17            return 1.0;
18        }
19        self.functions_preserved as f64 / self.functions_total as f64
20    }
21
22    pub fn export_rate(&self) -> f64 {
23        if self.exports_total == 0 {
24            return 1.0;
25        }
26        self.exports_preserved as f64 / self.exports_total as f64
27    }
28
29    pub fn import_rate(&self) -> f64 {
30        if self.imports_total == 0 {
31            return 1.0;
32        }
33        self.imports_preserved as f64 / self.imports_total as f64
34    }
35
36    pub fn overall(&self) -> f64 {
37        let total = self.functions_total + self.exports_total + self.imports_total;
38        if total == 0 {
39            return 1.0;
40        }
41        let preserved = self.functions_preserved + self.exports_preserved + self.imports_preserved;
42        preserved as f64 / total as f64
43    }
44}
45
46pub fn measure(raw_content: &str, compressed_output: &str, ext: &str) -> PreservationScore {
47    let sigs = signatures::extract_signatures(raw_content, ext);
48    let dep_info = deps::extract_deps(raw_content, ext);
49
50    let function_names: Vec<&str> = sigs
51        .iter()
52        .filter(|s| matches!(s.kind, "fn" | "method"))
53        .map(|s| s.name.as_str())
54        .collect();
55
56    let class_names: Vec<&str> = sigs
57        .iter()
58        .filter(|s| matches!(s.kind, "class" | "struct" | "interface" | "trait" | "enum"))
59        .map(|s| s.name.as_str())
60        .collect();
61
62    let all_symbols: Vec<&str> = function_names
63        .iter()
64        .chain(class_names.iter())
65        .copied()
66        .collect();
67
68    let functions_preserved = all_symbols
69        .iter()
70        .filter(|name| !name.is_empty() && compressed_output.contains(*name))
71        .count();
72
73    let exports_preserved = dep_info
74        .exports
75        .iter()
76        .filter(|e| !e.is_empty() && compressed_output.contains(e.as_str()))
77        .count();
78
79    let imports_preserved = dep_info
80        .imports
81        .iter()
82        .filter(|i| !i.is_empty() && compressed_output.contains(i.as_str()))
83        .count();
84
85    PreservationScore {
86        functions_total: all_symbols.len(),
87        functions_preserved,
88        exports_total: dep_info.exports.len(),
89        exports_preserved,
90        imports_total: dep_info.imports.len(),
91        imports_preserved,
92    }
93}