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