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
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}