lean_ctx_patterns/
find.rs1use std::collections::HashMap;
4
5pub fn compress(output: &str) -> Option<String> {
6 let lines: Vec<&str> = output.lines().filter(|l| !l.trim().is_empty()).collect();
7 if lines.len() < 5 {
8 return None;
9 }
10
11 let mut by_dir: HashMap<String, Vec<String>> = HashMap::new();
12 let mut total_files = 0usize;
13
14 for line in &lines {
15 let path = line.trim().strip_prefix("./").unwrap_or(line.trim());
16
17 if should_skip(path) {
18 continue;
19 }
20
21 total_files += 1;
22
23 if let Some(slash_pos) = path.rfind('/') {
24 let dir = &path[..slash_pos];
25 let file = &path[slash_pos + 1..];
26 by_dir
27 .entry(dir.to_string())
28 .or_default()
29 .push(file.to_string());
30 } else {
31 by_dir
32 .entry(".".to_string())
33 .or_default()
34 .push(path.to_string());
35 }
36 }
37
38 if total_files == 0 {
39 return None;
40 }
41
42 let mut result = format!("{total_files}F {}D:\n", by_dir.len());
43 let mut sorted_dirs: Vec<_> = by_dir.iter().collect();
44 sorted_dirs.sort_by_key(|(dir, _)| (*dir).clone());
45
46 for (dir, files) in &sorted_dirs {
47 result.push_str(&format!("\n{dir}/"));
48 let show: Vec<_> = files.iter().take(10).collect();
49 let mut line_buf = String::new();
50 for f in &show {
51 if line_buf.len() + f.len() + 1 > 60 {
52 result.push_str(&format!("\n {line_buf}"));
53 line_buf.clear();
54 }
55 if !line_buf.is_empty() {
56 line_buf.push(' ');
57 }
58 line_buf.push_str(f);
59 }
60 if !line_buf.is_empty() {
61 result.push_str(&format!("\n {line_buf}"));
62 }
63 if files.len() > 10 {
64 result.push_str(&format!("\n ... +{} more", files.len() - 10));
65 }
66 }
67
68 Some(result)
69}
70
71fn should_skip(path: &str) -> bool {
72 let skip_dirs = [
73 "node_modules/",
74 ".git/",
75 "target/debug/",
76 "target/release/",
77 "__pycache__/",
78 ".svelte-kit/",
79 ".next/",
80 "dist/",
81 ".DS_Store",
82 ];
83 skip_dirs.iter().any(|d| path.contains(d))
84}