Skip to main content

lean_ctx/tools/
ctx_read.rs

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