lean_ctx_patterns/
pnpm.rs1macro_rules! static_regex {
4 ($pattern:expr_2021) => {{
5 static RE: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();
6 RE.get_or_init(|| {
7 regex::Regex::new($pattern).expect(concat!("BUG: invalid static regex: ", $pattern))
8 })
9 }};
10}
11
12fn pnpm_added_re() -> &'static regex::Regex {
13 static_regex!(r"(\d+) packages? (?:are )?(?:installed|added|updated)")
14}
15
16pub fn compress(command: &str, output: &str) -> Option<String> {
17 if command.contains("install") || command.contains("add") || command.contains("i ") {
18 return Some(compress_install(output));
19 }
20 if command.contains("list") || command.contains("ls") {
21 return Some(compress_list(output));
22 }
23 if command.contains("outdated") {
24 return Some(compress_outdated(output));
25 }
26 if command.contains("run") || command.contains("exec") {
27 return Some(compress_run(output));
28 }
29 if command.contains("test") {
30 return Some(compress_test(output));
31 }
32 if command.contains("why") {
33 return Some(compact_output(output, 10));
34 }
35 if command.contains("store") {
36 return Some(compress_store(output));
37 }
38 None
39}
40
41fn compress_install(output: &str) -> String {
42 let summary = compress_install_summary(output);
43 let advisories: Vec<String> = super::npm::install_advisories(output)
45 .into_iter()
46 .filter(|a| !summary.lines().any(|l| l.trim() == a))
47 .collect();
48 if advisories.is_empty() {
49 summary
50 } else {
51 format!("{summary}\n{}", advisories.join("\n"))
52 }
53}
54
55fn compress_install_summary(output: &str) -> String {
56 let trimmed = output.trim();
57 if trimmed.is_empty() {
58 return "ok".to_string();
59 }
60
61 let mut pkg_count = 0u32;
62 if let Some(caps) = pnpm_added_re().captures(trimmed) {
63 pkg_count = caps[1].parse().unwrap_or(0);
64 }
65
66 let progress_free: Vec<&str> = trimmed
67 .lines()
68 .filter(|l| {
69 let t = l.trim();
70 !t.is_empty()
71 && !t.starts_with("Progress:")
72 && !t.starts_with("Already up to date")
73 && !t.contains("Downloading")
74 && !t.contains("fetched from")
75 })
76 .collect();
77
78 if pkg_count > 0 {
79 return format!("ok ({pkg_count} packages installed)");
80 }
81 if progress_free.len() <= 3 {
82 return progress_free.join("\n");
83 }
84 format!(
85 "ok\n{}",
86 progress_free[progress_free.len() - 3..].join("\n")
87 )
88}
89
90fn compress_list(output: &str) -> String {
91 let lines: Vec<&str> = output.lines().collect();
92 if lines.len() <= 5 {
93 return output.to_string();
94 }
95
96 let _deps: Vec<&str> = lines
97 .iter()
98 .filter(|l| l.contains("dependencies:") || l.starts_with(' '))
99 .copied()
100 .collect();
101
102 let top: Vec<String> = lines
103 .iter()
104 .filter(|l| {
105 let trimmed = l.trim();
106 !trimmed.is_empty()
107 && (trimmed.starts_with('+')
108 || trimmed.starts_with("└")
109 || trimmed.starts_with("├"))
110 })
111 .map(|l| {
112 l.replace("├──", "")
113 .replace("└──", "")
114 .replace("├─", "")
115 .replace("└─", "")
116 .trim()
117 .to_string()
118 })
119 .collect();
120
121 if !top.is_empty() {
122 return format!("{} packages:\n{}", top.len(), top.join("\n"));
123 }
124 compact_output(output, 15)
125}
126
127fn compress_outdated(output: &str) -> String {
128 let lines: Vec<&str> = output.lines().collect();
129 if lines.len() <= 1 {
130 return "all up-to-date".to_string();
131 }
132
133 let mut packages = Vec::new();
134 for line in &lines[1..] {
135 let parts: Vec<&str> = line.split_whitespace().collect();
136 if parts.len() >= 3 {
137 packages.push(format!("{}: {} → {}", parts[0], parts[1], parts[2]));
138 }
139 }
140
141 if packages.is_empty() {
142 return "all up-to-date".to_string();
143 }
144 format!("{} outdated:\n{}", packages.len(), packages.join("\n"))
145}
146
147fn compress_run(output: &str) -> String {
148 let lines: Vec<&str> = output
149 .lines()
150 .filter(|l| {
151 let t = l.trim();
152 !t.is_empty() && !t.starts_with('>')
153 })
154 .collect();
155
156 if lines.len() <= 5 {
157 return lines.join("\n");
158 }
159 let tail = &lines[lines.len() - 3..];
160 format!("...({} lines)\n{}", lines.len(), tail.join("\n"))
161}
162
163fn compress_test(output: &str) -> String {
164 compress_run(output)
165}
166
167fn compress_store(output: &str) -> String {
168 let trimmed = output.trim();
169 if trimmed.is_empty() {
170 return "ok".to_string();
171 }
172 compact_output(trimmed, 5)
173}
174
175fn compact_output(text: &str, max: usize) -> String {
176 let lines: Vec<&str> = text.lines().filter(|l| !l.trim().is_empty()).collect();
177 if lines.len() <= max {
178 return lines.join("\n");
179 }
180 format!(
181 "{}\n{}",
182 lines[..max].join("\n"),
183 super::elision_marker(lines.len() - max)
184 )
185}
186
187#[cfg(test)]
188mod tests {
189 use super::compress;
190
191 #[test]
192 fn install_keeps_vulnerability_summary() {
193 let out = "Packages: +12\n++++++++++++\nProgress: resolved 12, reused 12, downloaded 0, added 12, done\n12 packages are installed\n2 vulnerabilities found\nSeverity: 1 low | 1 high\nRun pnpm audit --fix to resolve\n";
194 let c = compress("pnpm install", out).unwrap();
195 assert!(c.contains("2 vulnerabilities found"), "{c}");
196 assert!(c.contains("pnpm audit --fix"), "{c}");
197 assert_eq!(c.matches("2 vulnerabilities found").count(), 1, "{c}");
198 }
199}