1use crate::core::preservation;
2use crate::core::tokens::count_tokens;
3
4macro_rules! static_regex {
5 ($pattern:expr_2021) => {{
6 static RE: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();
7 RE.get_or_init(|| {
8 regex::Regex::new($pattern).expect(concat!("BUG: invalid static regex: ", $pattern))
9 })
10 }};
11}
12
13const QUALITY_THRESHOLD: f64 = 0.95;
14const MIN_DENSITY: f64 = 0.15;
15
16#[derive(Debug, Clone)]
17pub struct QualityScore {
18 pub ast_score: f64,
19 pub identifier_score: f64,
20 pub line_score: f64,
21 pub density: f64,
22 pub composite: f64,
23 pub passed: bool,
24}
25
26impl QualityScore {
27 pub fn format_compact(&self) -> String {
28 if self.passed {
29 format!(
30 "Q:{:.0}% (ast:{:.0} id:{:.0} ln:{:.0} ρ:{:.0}) ✓",
31 self.composite * 100.0,
32 self.ast_score * 100.0,
33 self.identifier_score * 100.0,
34 self.line_score * 100.0,
35 self.density * 100.0,
36 )
37 } else {
38 format!(
39 "Q:{:.0}% (ast:{:.0} id:{:.0} ln:{:.0} ρ:{:.0}) ✗ BELOW THRESHOLD",
40 self.composite * 100.0,
41 self.ast_score * 100.0,
42 self.identifier_score * 100.0,
43 self.line_score * 100.0,
44 self.density * 100.0,
45 )
46 }
47 }
48}
49
50pub fn score(original: &str, compressed: &str, ext: &str) -> QualityScore {
51 let pres = preservation::measure(original, compressed, ext);
52 let ast_score = pres.overall();
53
54 let identifier_score = measure_identifier_preservation(original, compressed);
55 let line_score = measure_line_preservation(original, compressed);
56 let density = information_density(original, compressed, ext);
57
58 let composite = ast_score * 0.5 + identifier_score * 0.3 + line_score * 0.2;
59
60 let compression_ratio = measure_line_preservation(original, compressed);
61 let adaptive_threshold = QUALITY_THRESHOLD - 0.05 * (1.0 - compression_ratio);
62 let passed = composite >= adaptive_threshold && density >= MIN_DENSITY;
63
64 QualityScore {
65 ast_score,
66 identifier_score,
67 line_score,
68 density,
69 composite,
70 passed,
71 }
72}
73
74pub fn information_density(original: &str, compressed: &str, ext: &str) -> f64 {
77 let output_tokens = count_tokens(compressed);
78 if output_tokens == 0 {
79 return 1.0;
80 }
81
82 let pres = preservation::measure(original, compressed, ext);
83 let semantic_items = pres.functions_preserved + pres.exports_preserved + pres.imports_preserved;
84 let ident_re = static_regex!(r"\b[a-zA-Z_][a-zA-Z0-9_]{3,}\b");
85 let unique_idents: std::collections::HashSet<&str> =
86 ident_re.find_iter(compressed).map(|m| m.as_str()).collect();
87 let semantic_token_estimate = semantic_items + unique_idents.len();
88
89 (semantic_token_estimate as f64 / output_tokens as f64).min(1.0)
90}
91
92pub fn guard(original: &str, compressed: &str, ext: &str) -> (String, QualityScore) {
94 let q = score(original, compressed, ext);
95 if q.passed {
96 (compressed.to_string(), q)
97 } else {
98 (original.to_string(), q)
99 }
100}
101
102pub fn guard_density(
109 original: &str,
110 compressed: &str,
111 ext: &str,
112 target: f64,
113) -> (String, QualityScore) {
114 let pres = preservation::measure(original, compressed, ext);
115 let ast_score = pres.overall();
116 let identifier_score = measure_identifier_preservation(original, compressed);
117 let line_score = measure_line_preservation(original, compressed);
118 let density = information_density(original, compressed, ext);
119
120 let structure_composite = ast_score * 0.6 + identifier_score * 0.4;
122
123 let structure_threshold = 0.85;
126 let identifier_floor = target.clamp(0.05, structure_threshold);
131 let density_floor = MIN_DENSITY.min(information_density(original, original, ext));
135 let passed = ast_score >= structure_threshold
136 && identifier_score >= identifier_floor
137 && density >= density_floor;
138
139 let q = QualityScore {
140 ast_score,
141 identifier_score,
142 line_score,
143 density,
144 composite: structure_composite,
145 passed,
146 };
147
148 if passed {
149 (compressed.to_string(), q)
150 } else {
151 tracing::debug!(
152 target = target,
153 composite = structure_composite,
154 threshold = structure_threshold,
155 "density quality gate rejected → full fallback"
156 );
157 (original.to_string(), q)
158 }
159}
160
161fn measure_identifier_preservation(original: &str, compressed: &str) -> f64 {
162 let ident_re = static_regex!(r"\b[a-zA-Z_][a-zA-Z0-9_]{3,}\b");
163
164 let original_idents: std::collections::HashSet<&str> =
165 ident_re.find_iter(original).map(|m| m.as_str()).collect();
166
167 if original_idents.is_empty() {
168 return 1.0;
169 }
170
171 let preserved = original_idents
172 .iter()
173 .filter(|id| compressed.contains(*id))
174 .count();
175
176 preserved as f64 / original_idents.len() as f64
177}
178
179fn measure_line_preservation(original: &str, compressed: &str) -> f64 {
180 let original_lines: usize = original.lines().filter(|l| !l.trim().is_empty()).count();
181 if original_lines == 0 {
182 return 1.0;
183 }
184
185 let compressed_lines: usize = compressed.lines().filter(|l| !l.trim().is_empty()).count();
186 let ratio = compressed_lines as f64 / original_lines as f64;
187
188 ratio.min(1.0)
189}
190
191#[cfg(test)]
192pub mod tests {
193 use super::*;
194
195 #[test]
196 fn test_perfect_score_identity() {
197 let code = "fn main() {\n println!(\"hello\");\n}\n";
198 let q = score(code, code, "rs");
199 assert!(q.composite >= 0.99);
200 assert!(q.passed);
201 }
202
203 #[test]
204 fn test_score_below_threshold_returns_original() {
205 let original = "fn validate_token() {\n let result = check();\n return result;\n}\n";
206 let bad_compressed = "removed everything";
207 let (output, q) = guard(original, bad_compressed, "rs");
208 assert!(!q.passed);
209 assert_eq!(output, original);
210 }
211
212 #[test]
213 fn test_good_compression_passes() {
214 let original = "fn validate_token() {\n let result = check();\n return result;\n}\n";
215 let compressed = "fn validate_token() { let result = check(); return result; }";
216 let q = score(original, compressed, "rs");
217 assert!(q.ast_score >= 0.9);
218 assert!(q.identifier_score >= 0.9);
219 }
220
221 #[test]
222 fn test_score_format_compact() {
223 let code = "fn main() {}\n";
224 let q = score(code, code, "rs");
225 let formatted = q.format_compact();
226 assert!(formatted.contains("Q:"));
227 assert!(formatted.contains("✓"));
228 }
229
230 #[test]
231 fn test_empty_content_scores_perfect() {
232 let q = score("", "", "rs");
233 assert!(q.passed);
234 assert!(q.composite >= 0.99);
235 }
236
237 #[test]
238 fn test_rust_file_with_structs() {
239 let original = "pub struct Config {\n pub name: String,\n pub value: usize,\n}\n\nimpl Config {\n pub fn new() -> Self {\n Self { name: String::new(), value: 0 }\n }\n}\n";
240 let compressed = "pub struct Config { pub name: String, pub value: usize }\nimpl Config { pub fn new() -> Self { Self { name: String::new(), value: 0 } } }";
241 let q = score(original, compressed, "rs");
242 assert!(q.identifier_score >= 0.9);
243 }
244
245 #[test]
246 fn test_typescript_file() {
247 let original = "export function fetchData(url: string): Promise<Response> {\n return fetch(url);\n}\n\nexport const API_URL = 'https://api.example.com';\n";
248 let compressed = "export function fetchData(url: string): Promise<Response> { return fetch(url); }\nexport const API_URL = 'https://api.example.com';";
249 let q = score(original, compressed, "ts");
250 assert!(q.identifier_score >= 0.9);
251 }
252
253 #[test]
254 fn test_python_file() {
255 let original = "def validate_credentials(username: str, password: str) -> bool:\n user = find_user(username)\n return verify_hash(user.password_hash, password)\n";
256 let compressed = "def validate_credentials(username, password): user = find_user(username); return verify_hash(user.password_hash, password)";
257 let q = score(original, compressed, "py");
258 assert!(q.identifier_score >= 0.8);
259 }
260
261 #[test]
262 fn test_density_high_for_meaningful_compression() {
263 let original = "pub fn calculate_total(items: Vec<Item>) -> f64 {\n items.iter().map(|i| i.price * i.quantity as f64).sum()\n}\n";
264 let d = information_density(original, original, "rs");
265 assert!(d > 0.15, "identity should have high density: {d}");
266 }
267
268 #[test]
269 fn test_density_low_for_garbage() {
270 let original = "pub fn calculate_total(items: Vec<Item>) -> f64 {\n items.iter().map(|i| i.price).sum()\n}\n";
271 let garbage = "xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx xxx";
272 let d = information_density(original, garbage, "rs");
273 assert!(d < 0.5, "garbage output should have low density: {d}");
274 }
275
276 #[test]
277 fn test_density_in_quality_score() {
278 let code = "fn main() {\n println!(\"hello\");\n}\n";
279 let q = score(code, code, "rs");
280 assert!(q.density > 0.0, "density should be computed");
281 }
282
283 #[test]
284 fn test_adaptive_threshold_looser_for_heavy_compression() {
285 let original = "fn validate_token() {\n let result = check();\n return result;\n}\nfn other() {\n let x = 1;\n}\n";
286 let compressed = "fn validate_token() { let result = check(); return result; }";
287 let q = score(original, compressed, "rs");
288 assert!(
289 q.density >= MIN_DENSITY,
290 "compressed code should maintain minimum density"
291 );
292 }
293}