lean_ctx/core/
preservation.rs1use 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}