1use std::path::Path;
2
3use crate::core::cache::SessionCache;
4use crate::core::compressor;
5use crate::core::deps;
6use crate::core::entropy;
7use crate::core::protocol;
8use crate::core::signatures;
9use crate::core::symbol_map::{self, SymbolMap};
10use crate::core::tokens::count_tokens;
11use crate::tools::CrpMode;
12
13const COMPRESSED_HINT: &str = "[compressed — use mode=\"full\" for complete source]";
14
15fn append_compressed_hint(output: &str, file_path: &str) -> String {
16 format!("{output}\n{COMPRESSED_HINT}\n ctx_read(\"{file_path}\", mode=\"full\")")
17}
18
19pub fn read_file_lossy(path: &str) -> Result<String, std::io::Error> {
20 let bytes = std::fs::read(path)?;
21 match String::from_utf8(bytes) {
22 Ok(s) => Ok(s),
23 Err(e) => Ok(String::from_utf8_lossy(e.as_bytes()).into_owned()),
24 }
25}
26
27pub fn handle(cache: &mut SessionCache, path: &str, mode: &str, crp_mode: CrpMode) -> String {
28 handle_with_options(cache, path, mode, false, crp_mode, None)
29}
30
31pub fn handle_fresh(cache: &mut SessionCache, path: &str, mode: &str, crp_mode: CrpMode) -> String {
32 handle_with_options(cache, path, mode, true, crp_mode, None)
33}
34
35pub fn handle_with_task(
36 cache: &mut SessionCache,
37 path: &str,
38 mode: &str,
39 crp_mode: CrpMode,
40 task: Option<&str>,
41) -> String {
42 handle_with_options(cache, path, mode, false, crp_mode, task)
43}
44
45pub fn handle_fresh_with_task(
46 cache: &mut SessionCache,
47 path: &str,
48 mode: &str,
49 crp_mode: CrpMode,
50 task: Option<&str>,
51) -> String {
52 handle_with_options(cache, path, mode, true, crp_mode, task)
53}
54
55fn handle_with_options(
56 cache: &mut SessionCache,
57 path: &str,
58 mode: &str,
59 fresh: bool,
60 crp_mode: CrpMode,
61 task: Option<&str>,
62) -> String {
63 let file_ref = cache.get_file_ref(path);
64 let short = protocol::shorten_path(path);
65 let ext = Path::new(path)
66 .extension()
67 .and_then(|e| e.to_str())
68 .unwrap_or("");
69
70 if fresh {
71 cache.invalidate(path);
72 }
73
74 if mode == "diff" {
75 return handle_diff(cache, path, &file_ref);
76 }
77
78 if let Some(existing) = cache.get(path) {
79 if mode == "full" {
80 return handle_full_with_auto_delta(cache, path, &file_ref, &short, ext, crp_mode);
81 }
82 let content = existing.content.clone();
83 let original_tokens = existing.original_tokens;
84 return process_mode(
85 &content,
86 mode,
87 &file_ref,
88 &short,
89 ext,
90 original_tokens,
91 crp_mode,
92 path,
93 task,
94 );
95 }
96
97 let content = match read_file_lossy(path) {
98 Ok(c) => c,
99 Err(e) => return format!("ERROR: {e}"),
100 };
101
102 let similar_hint = find_semantic_similar(path, &content);
103
104 let (entry, _is_hit) = cache.store(path, content.clone());
105
106 update_semantic_index(path, &content);
107
108 if mode == "full" {
109 let mut output =
110 format_full_output(cache, &file_ref, &short, ext, &content, &entry, crp_mode);
111 if let Some(hint) = similar_hint {
112 output.push_str(&format!("\n{hint}"));
113 }
114 return output;
115 }
116
117 let mut output = process_mode(
118 &content,
119 mode,
120 &file_ref,
121 &short,
122 ext,
123 entry.original_tokens,
124 crp_mode,
125 path,
126 task,
127 );
128 if let Some(hint) = similar_hint {
129 output.push_str(&format!("\n{hint}"));
130 }
131 output
132}
133
134fn find_semantic_similar(path: &str, content: &str) -> Option<String> {
135 let project_root = detect_project_root(path);
136 let index = crate::core::semantic_cache::SemanticCacheIndex::load(&project_root)?;
137
138 let similar = index.find_similar(content, 0.7);
139 let relevant: Vec<_> = similar
140 .into_iter()
141 .filter(|(p, _)| p != path)
142 .take(3)
143 .collect();
144
145 if relevant.is_empty() {
146 return None;
147 }
148
149 let hints: Vec<String> = relevant
150 .iter()
151 .map(|(p, score)| format!(" {p} ({:.0}% similar)", score * 100.0))
152 .collect();
153
154 Some(format!(
155 "[semantic: {} similar file(s) in cache]\n{}",
156 relevant.len(),
157 hints.join("\n")
158 ))
159}
160
161fn update_semantic_index(path: &str, content: &str) {
162 let project_root = detect_project_root(path);
163 let session_id = format!("{}", std::process::id());
164 let mut index = crate::core::semantic_cache::SemanticCacheIndex::load_or_create(&project_root);
165 index.add_file(path, content, &session_id);
166 let _ = index.save(&project_root);
167}
168
169fn detect_project_root(path: &str) -> String {
170 crate::core::protocol::detect_project_root_or_cwd(path)
171}
172
173const AUTO_DELTA_THRESHOLD: f64 = 0.6;
174
175fn handle_full_with_auto_delta(
177 cache: &mut SessionCache,
178 path: &str,
179 file_ref: &str,
180 short: &str,
181 ext: &str,
182 crp_mode: CrpMode,
183) -> String {
184 let disk_content = match read_file_lossy(path) {
185 Ok(c) => c,
186 Err(_) => {
187 cache.record_cache_hit(path);
188 let existing = cache.get(path).unwrap();
189 return format!(
190 "[using cached version — file read failed]\n{file_ref}={short} cached {}t {}L",
191 existing.read_count, existing.line_count
192 );
193 }
194 };
195
196 let old_content = cache.get(path).unwrap().content.clone();
197 let (entry, is_hit) = cache.store(path, disk_content.clone());
198
199 if is_hit {
200 return format!(
201 "{file_ref}={short} cached {}t {}L",
202 entry.read_count, entry.line_count
203 );
204 }
205
206 let diff = compressor::diff_content(&old_content, &disk_content);
207 let diff_tokens = count_tokens(&diff);
208 let full_tokens = entry.original_tokens;
209
210 if full_tokens > 0 && (diff_tokens as f64) < (full_tokens as f64 * AUTO_DELTA_THRESHOLD) {
211 let savings = protocol::format_savings(full_tokens, diff_tokens);
212 return format!(
213 "{file_ref}={short} [auto-delta] ∆{}L\n{diff}\n{savings}",
214 disk_content.lines().count()
215 );
216 }
217
218 format_full_output(cache, file_ref, short, ext, &disk_content, &entry, crp_mode)
219}
220
221fn format_full_output(
222 _cache: &mut SessionCache,
223 file_ref: &str,
224 short: &str,
225 ext: &str,
226 content: &str,
227 entry: &crate::core::cache::CacheEntry,
228 _crp_mode: CrpMode,
229) -> String {
230 let tokens = entry.original_tokens;
231 let metadata = build_header(file_ref, short, ext, content, entry.line_count, true);
232
233 let mut sym = SymbolMap::new();
234 let idents = symbol_map::extract_identifiers(content, ext);
235 for ident in &idents {
236 sym.register(ident);
237 }
238
239 let sym_beneficial = if sym.len() >= 3 {
240 let sym_table = sym.format_table();
241 let compressed = sym.apply(content);
242 let original_tok = count_tokens(content);
243 let compressed_tok = count_tokens(&compressed) + count_tokens(&sym_table);
244 let net_saving = original_tok.saturating_sub(compressed_tok);
245 original_tok > 0 && net_saving * 100 / original_tok >= 5
246 } else {
247 false
248 };
249
250 if sym_beneficial {
251 let compressed_content = sym.apply(content);
252 let sym_table = sym.format_table();
253 let output = format!("{compressed_content}{sym_table}\n{metadata}");
254 let sent = count_tokens(&output);
255 let savings = protocol::format_savings(tokens, sent);
256 return format!("{output}\n{savings}");
257 }
258
259 let output = format!("{content}\n{metadata}");
260 let sent = count_tokens(&output);
261 let savings = protocol::format_savings(tokens, sent);
262 format!("{output}\n{savings}")
263}
264
265fn build_header(
266 file_ref: &str,
267 short: &str,
268 ext: &str,
269 content: &str,
270 line_count: usize,
271 include_deps: bool,
272) -> String {
273 let mut header = format!("{file_ref}={short} {line_count}L");
274
275 if include_deps {
276 let dep_info = deps::extract_deps(content, ext);
277 if !dep_info.imports.is_empty() {
278 let imports_str: Vec<&str> = dep_info
279 .imports
280 .iter()
281 .take(8)
282 .map(|s| s.as_str())
283 .collect();
284 header.push_str(&format!("\n deps {}", imports_str.join(",")));
285 }
286 if !dep_info.exports.is_empty() {
287 let exports_str: Vec<&str> = dep_info
288 .exports
289 .iter()
290 .take(8)
291 .map(|s| s.as_str())
292 .collect();
293 header.push_str(&format!("\n exports {}", exports_str.join(",")));
294 }
295 }
296
297 header
298}
299
300#[allow(clippy::too_many_arguments)]
301fn process_mode(
302 content: &str,
303 mode: &str,
304 file_ref: &str,
305 short: &str,
306 ext: &str,
307 original_tokens: usize,
308 crp_mode: CrpMode,
309 file_path: &str,
310 task: Option<&str>,
311) -> String {
312 let line_count = content.lines().count();
313
314 match mode {
315 "auto" => {
316 let sig =
317 crate::core::mode_predictor::FileSignature::from_path(file_path, original_tokens);
318 let predictor = crate::core::mode_predictor::ModePredictor::new();
319 let resolved = predictor
320 .predict_best_mode(&sig)
321 .unwrap_or_else(|| "full".to_string());
322 process_mode(
323 content,
324 &resolved,
325 file_ref,
326 short,
327 ext,
328 original_tokens,
329 crp_mode,
330 file_path,
331 task,
332 )
333 }
334 "signatures" => {
335 let sigs = signatures::extract_signatures(content, ext);
336 let dep_info = deps::extract_deps(content, ext);
337
338 let mut output = format!("{file_ref}={short} {line_count}L");
339 if !dep_info.imports.is_empty() {
340 let imports_str: Vec<&str> = dep_info
341 .imports
342 .iter()
343 .take(8)
344 .map(|s| s.as_str())
345 .collect();
346 output.push_str(&format!("\n deps {}", imports_str.join(",")));
347 }
348 for sig in &sigs {
349 output.push('\n');
350 if crp_mode.is_tdd() {
351 output.push_str(&sig.to_tdd());
352 } else {
353 output.push_str(&sig.to_compact());
354 }
355 }
356 let sent = count_tokens(&output);
357 let savings = protocol::format_savings(original_tokens, sent);
358 append_compressed_hint(&format!("{output}\n{savings}"), file_path)
359 }
360 "map" => {
361 if ext == "php" {
362 if let Some(php_map) = crate::core::patterns::php::compress_php_map(content, short)
363 {
364 let mut output = format!("{file_ref}={short} {line_count}L\n{php_map}");
365 let sent = count_tokens(&output);
366 let savings = protocol::format_savings(original_tokens, sent);
367 output.push('\n');
368 output.push_str(&savings);
369 return append_compressed_hint(&output, file_path);
370 }
371 }
372
373 let sigs = signatures::extract_signatures(content, ext);
374 let dep_info = deps::extract_deps(content, ext);
375
376 let mut output = format!("{file_ref}={short} {line_count}L");
377
378 if !dep_info.imports.is_empty() {
379 output.push_str("\n deps: ");
380 output.push_str(&dep_info.imports.join(", "));
381 }
382
383 if !dep_info.exports.is_empty() {
384 output.push_str("\n exports: ");
385 output.push_str(&dep_info.exports.join(", "));
386 }
387
388 let key_sigs: Vec<&signatures::Signature> = sigs
389 .iter()
390 .filter(|s| s.is_exported || s.indent == 0)
391 .collect();
392
393 if !key_sigs.is_empty() {
394 output.push_str("\n API:");
395 for sig in &key_sigs {
396 output.push_str("\n ");
397 if crp_mode.is_tdd() {
398 output.push_str(&sig.to_tdd());
399 } else {
400 output.push_str(&sig.to_compact());
401 }
402 }
403 }
404
405 let sent = count_tokens(&output);
406 let savings = protocol::format_savings(original_tokens, sent);
407 append_compressed_hint(&format!("{output}\n{savings}"), file_path)
408 }
409 "aggressive" => {
410 let raw = compressor::aggressive_compress(content, Some(ext));
411 let compressed = compressor::safeguard_ratio(content, &raw);
412 let header = build_header(file_ref, short, ext, content, line_count, true);
413
414 let mut sym = SymbolMap::new();
415 let idents = symbol_map::extract_identifiers(&compressed, ext);
416 for ident in &idents {
417 sym.register(ident);
418 }
419
420 let sym_beneficial = if sym.len() >= 3 {
421 let sym_table = sym.format_table();
422 let sym_applied = sym.apply(&compressed);
423 let orig_tok = count_tokens(&compressed);
424 let comp_tok = count_tokens(&sym_applied) + count_tokens(&sym_table);
425 let net = orig_tok.saturating_sub(comp_tok);
426 orig_tok > 0 && net * 100 / orig_tok >= 5
427 } else {
428 false
429 };
430
431 if sym_beneficial {
432 let sym_output = sym.apply(&compressed);
433 let sym_table = sym.format_table();
434 let sent = count_tokens(&sym_output) + count_tokens(&sym_table);
435 let savings = protocol::format_savings(original_tokens, sent);
436 return append_compressed_hint(
437 &format!("{header}\n{sym_output}{sym_table}\n{savings}"),
438 file_path,
439 );
440 }
441
442 let sent = count_tokens(&compressed);
443 let savings = protocol::format_savings(original_tokens, sent);
444 append_compressed_hint(&format!("{header}\n{compressed}\n{savings}"), file_path)
445 }
446 "entropy" => {
447 let result = entropy::entropy_compress_adaptive(content, file_path);
448 let avg_h = entropy::analyze_entropy(content).avg_entropy;
449 let header = build_header(file_ref, short, ext, content, line_count, false);
450 let techs = result.techniques.join(", ");
451 let output = format!("{header} H̄={avg_h:.1} [{techs}]\n{}", result.output);
452 let sent = count_tokens(&output);
453 let savings = protocol::format_savings(original_tokens, sent);
454 append_compressed_hint(&format!("{output}\n{savings}"), file_path)
455 }
456 "task" => {
457 let task_str = task.unwrap_or("");
458 if task_str.is_empty() {
459 let header = build_header(file_ref, short, ext, content, line_count, true);
460 return format!("{header}\n{content}\n[task mode: no task set — returned full]");
461 }
462 let (_files, keywords) = crate::core::task_relevance::parse_task_hints(task_str);
463 if keywords.is_empty() {
464 let header = build_header(file_ref, short, ext, content, line_count, true);
465 return format!(
466 "{header}\n{content}\n[task mode: no keywords extracted — returned full]"
467 );
468 }
469 let filtered =
470 crate::core::task_relevance::information_bottleneck_filter(content, &keywords, 0.3);
471 let filtered_lines = filtered.lines().count();
472 let header = format!(
473 "{file_ref}={short} {line_count}L [task-filtered: {line_count}→{filtered_lines}]"
474 );
475 let sent = count_tokens(&filtered) + count_tokens(&header);
476 let savings = protocol::format_savings(original_tokens, sent);
477 append_compressed_hint(&format!("{header}\n{filtered}\n{savings}"), file_path)
478 }
479 "reference" => {
480 let tok = count_tokens(content);
481 let output = format!("{file_ref}={short}: {line_count} lines, {tok} tok ({ext})");
482 let sent = count_tokens(&output);
483 let savings = protocol::format_savings(original_tokens, sent);
484 format!("{output}\n{savings}")
485 }
486 mode if mode.starts_with("lines:") => {
487 let range_str = &mode[6..];
488 let extracted = extract_line_range(content, range_str);
489 let header = format!("{file_ref}={short} {line_count}L lines:{range_str}");
490 let sent = count_tokens(&extracted);
491 let savings = protocol::format_savings(original_tokens, sent);
492 format!("{header}\n{extracted}\n{savings}")
493 }
494 unknown => {
495 let header = build_header(file_ref, short, ext, content, line_count, true);
496 format!(
497 "[WARNING: unknown mode '{unknown}', falling back to full]\n{header}\n{content}"
498 )
499 }
500 }
501}
502
503fn extract_line_range(content: &str, range_str: &str) -> String {
504 let lines: Vec<&str> = content.lines().collect();
505 let total = lines.len();
506 let mut selected = Vec::new();
507
508 for part in range_str.split(',') {
509 let part = part.trim();
510 if let Some((start_s, end_s)) = part.split_once('-') {
511 let start = start_s.trim().parse::<usize>().unwrap_or(1).max(1);
512 let end = end_s.trim().parse::<usize>().unwrap_or(total).min(total);
513 for i in start..=end {
514 if i >= 1 && i <= total {
515 selected.push(format!("{i:>4}| {}", lines[i - 1]));
516 }
517 }
518 } else if let Ok(n) = part.parse::<usize>() {
519 if n >= 1 && n <= total {
520 selected.push(format!("{n:>4}| {}", lines[n - 1]));
521 }
522 }
523 }
524
525 if selected.is_empty() {
526 "No lines matched the range.".to_string()
527 } else {
528 selected.join("\n")
529 }
530}
531
532fn handle_diff(cache: &mut SessionCache, path: &str, file_ref: &str) -> String {
533 let short = protocol::shorten_path(path);
534 let old_content = cache.get(path).map(|e| e.content.clone());
535
536 let new_content = match read_file_lossy(path) {
537 Ok(c) => c,
538 Err(e) => return format!("ERROR: {e}"),
539 };
540
541 let original_tokens = count_tokens(&new_content);
542
543 let diff_output = if let Some(old) = &old_content {
544 compressor::diff_content(old, &new_content)
545 } else {
546 format!("[first read]\n{new_content}")
547 };
548
549 cache.store(path, new_content);
550
551 let sent = count_tokens(&diff_output);
552 let savings = protocol::format_savings(original_tokens, sent);
553 format!("{file_ref}={short} [diff]\n{diff_output}\n{savings}")
554}
555
556#[cfg(test)]
557mod tests {
558 use super::*;
559
560 #[test]
561 fn test_header_toon_format_no_brackets() {
562 let content = "use std::io;\nfn main() {}\n";
563 let header = build_header("F1", "main.rs", "rs", content, 2, false);
564 assert!(!header.contains('['));
565 assert!(!header.contains(']'));
566 assert!(header.contains("F1=main.rs 2L"));
567 }
568
569 #[test]
570 fn test_header_toon_deps_indented() {
571 let content = "use crate::core::cache;\nuse crate::tools;\npub fn main() {}\n";
572 let header = build_header("F1", "main.rs", "rs", content, 3, true);
573 if header.contains("deps") {
574 assert!(
575 header.contains("\n deps "),
576 "deps should use indented TOON format"
577 );
578 assert!(
579 !header.contains("deps:["),
580 "deps should not use bracket format"
581 );
582 }
583 }
584
585 #[test]
586 fn test_header_toon_saves_tokens() {
587 let content = "use crate::foo;\nuse crate::bar;\npub fn baz() {}\npub fn qux() {}\n";
588 let old_header = "F1=main.rs [4L +] deps:[foo,bar] exports:[baz,qux]".to_string();
589 let new_header = build_header("F1", "main.rs", "rs", content, 4, true);
590 let old_tokens = count_tokens(&old_header);
591 let new_tokens = count_tokens(&new_header);
592 assert!(
593 new_tokens <= old_tokens,
594 "TOON header ({new_tokens} tok) should be <= old format ({old_tokens} tok)"
595 );
596 }
597
598 #[test]
599 fn test_tdd_symbols_are_compact() {
600 let symbols = [
601 "⊕", "⊖", "∆", "→", "⇒", "✓", "✗", "⚠", "λ", "§", "∂", "τ", "ε",
602 ];
603 for sym in &symbols {
604 let tok = count_tokens(sym);
605 assert!(tok <= 2, "Symbol {sym} should be 1-2 tokens, got {tok}");
606 }
607 }
608
609 #[test]
610 fn test_task_mode_filters_content() {
611 let content = (0..200)
612 .map(|i| {
613 if i % 20 == 0 {
614 format!("fn validate_token(token: &str) -> bool {{ /* line {i} */ }}")
615 } else {
616 format!("fn unrelated_helper_{i}(x: i32) -> i32 {{ x + {i} }}")
617 }
618 })
619 .collect::<Vec<_>>()
620 .join("\n");
621 let full_tokens = count_tokens(&content);
622 let task = Some("fix bug in validate_token");
623 let result = process_mode(
624 &content,
625 "task",
626 "F1",
627 "test.rs",
628 "rs",
629 full_tokens,
630 CrpMode::Off,
631 "test.rs",
632 task,
633 );
634 let result_tokens = count_tokens(&result);
635 assert!(
636 result_tokens < full_tokens,
637 "task mode ({result_tokens} tok) should be less than full ({full_tokens} tok)"
638 );
639 assert!(
640 result.contains("task-filtered"),
641 "output should contain task-filtered marker"
642 );
643 }
644
645 #[test]
646 fn test_task_mode_without_task_returns_full() {
647 let content = "fn main() {}\nfn helper() {}\n";
648 let tokens = count_tokens(content);
649 let result = process_mode(
650 content,
651 "task",
652 "F1",
653 "test.rs",
654 "rs",
655 tokens,
656 CrpMode::Off,
657 "test.rs",
658 None,
659 );
660 assert!(
661 result.contains("no task set"),
662 "should indicate no task: {result}"
663 );
664 }
665
666 #[test]
667 fn test_reference_mode_one_line() {
668 let content = "fn main() {}\nfn helper() {}\nfn other() {}\n";
669 let tokens = count_tokens(content);
670 let result = process_mode(
671 content,
672 "reference",
673 "F1",
674 "test.rs",
675 "rs",
676 tokens,
677 CrpMode::Off,
678 "test.rs",
679 None,
680 );
681 let lines: Vec<&str> = result.lines().collect();
682 assert!(
683 lines.len() <= 3,
684 "reference mode should be very compact, got {} lines",
685 lines.len()
686 );
687 assert!(result.contains("lines"), "should contain line count");
688 assert!(result.contains("tok"), "should contain token count");
689 }
690
691 #[test]
692 fn benchmark_task_conditioned_compression() {
693 let content = generate_benchmark_code(500);
694 let full_tokens = count_tokens(&content);
695 let task = Some("fix authentication in validate_token");
696
697 let full_output = process_mode(
698 &content,
699 "full",
700 "F1",
701 "server.rs",
702 "rs",
703 full_tokens,
704 CrpMode::Off,
705 "server.rs",
706 task,
707 );
708 let task_output = process_mode(
709 &content,
710 "task",
711 "F1",
712 "server.rs",
713 "rs",
714 full_tokens,
715 CrpMode::Off,
716 "server.rs",
717 task,
718 );
719 let sig_output = process_mode(
720 &content,
721 "signatures",
722 "F1",
723 "server.rs",
724 "rs",
725 full_tokens,
726 CrpMode::Off,
727 "server.rs",
728 task,
729 );
730 let ref_output = process_mode(
731 &content,
732 "reference",
733 "F1",
734 "server.rs",
735 "rs",
736 full_tokens,
737 CrpMode::Off,
738 "server.rs",
739 task,
740 );
741
742 let full_tok = count_tokens(&full_output);
743 let task_tok = count_tokens(&task_output);
744 let sig_tok = count_tokens(&sig_output);
745 let ref_tok = count_tokens(&ref_output);
746
747 eprintln!("\n=== Task-Conditioned Compression Benchmark ===");
748 eprintln!("Source: 500-line Rust file, task='fix authentication in validate_token'");
749 eprintln!(" full: {full_tok:>6} tokens (baseline)");
750 eprintln!(
751 " task: {task_tok:>6} tokens ({:.0}% savings)",
752 (1.0 - task_tok as f64 / full_tok as f64) * 100.0
753 );
754 eprintln!(
755 " signatures: {sig_tok:>6} tokens ({:.0}% savings)",
756 (1.0 - sig_tok as f64 / full_tok as f64) * 100.0
757 );
758 eprintln!(
759 " reference: {ref_tok:>6} tokens ({:.0}% savings)",
760 (1.0 - ref_tok as f64 / full_tok as f64) * 100.0
761 );
762 eprintln!("================================================\n");
763
764 assert!(task_tok < full_tok, "task mode should save tokens");
765 assert!(sig_tok < full_tok, "signatures should save tokens");
766 assert!(ref_tok < sig_tok, "reference should be most compact");
767 }
768
769 fn generate_benchmark_code(lines: usize) -> String {
770 let mut code = Vec::with_capacity(lines);
771 code.push("use std::collections::HashMap;".to_string());
772 code.push("use crate::core::auth;".to_string());
773 code.push(String::new());
774 code.push("pub struct Server {".to_string());
775 code.push(" config: Config,".to_string());
776 code.push(" cache: HashMap<String, String>,".to_string());
777 code.push("}".to_string());
778 code.push(String::new());
779 code.push("impl Server {".to_string());
780 code.push(
781 " pub fn validate_token(&self, token: &str) -> Result<Claims, AuthError> {"
782 .to_string(),
783 );
784 code.push(" let decoded = auth::decode_jwt(token)?;".to_string());
785 code.push(" if decoded.exp < chrono::Utc::now().timestamp() {".to_string());
786 code.push(" return Err(AuthError::Expired);".to_string());
787 code.push(" }".to_string());
788 code.push(" Ok(decoded.claims)".to_string());
789 code.push(" }".to_string());
790 code.push(String::new());
791
792 let remaining = lines.saturating_sub(code.len());
793 for i in 0..remaining {
794 if i % 30 == 0 {
795 code.push(format!(
796 " pub fn handler_{i}(&self, req: Request) -> Response {{"
797 ));
798 } else if i % 30 == 29 {
799 code.push(" }".to_string());
800 } else {
801 code.push(format!(" let val_{i} = self.cache.get(\"key_{i}\").unwrap_or(&\"default\".to_string());"));
802 }
803 }
804 code.push("}".to_string());
805 code.join("\n")
806 }
807}