1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum OutputPolicy {
16 Passthrough,
20
21 Verbatim,
25
26 Compressible,
30}
31
32impl OutputPolicy {
33 pub fn is_protected(&self) -> bool {
36 matches!(self, Self::Passthrough | Self::Verbatim)
37 }
38}
39
40pub fn classify(command: &str, user_excluded: &[String]) -> OutputPolicy {
47 if is_passthrough(command, user_excluded) {
48 return OutputPolicy::Passthrough;
49 }
50 if super::compress::is_verbatim_output(command) {
51 return OutputPolicy::Verbatim;
52 }
53 OutputPolicy::Compressible
54}
55
56fn is_passthrough(command: &str, user_excluded: &[String]) -> bool {
57 super::compress::is_excluded_command(command, user_excluded)
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn gh_auth_is_passthrough() {
66 assert_eq!(classify("gh auth login", &[]), OutputPolicy::Passthrough);
67 }
68
69 #[test]
70 fn gh_api_is_verbatim() {
71 assert_eq!(
73 classify("gh api repos/owner/repo/issues", &[]),
74 OutputPolicy::Verbatim
75 );
76 }
77
78 #[test]
79 fn user_excluded_is_passthrough() {
80 let excl = vec!["mycommand".to_string()];
81 assert_eq!(
82 classify("mycommand --flag", &excl),
83 OutputPolicy::Passthrough
84 );
85 }
86
87 #[test]
88 fn curl_is_verbatim() {
89 assert_eq!(
90 classify("curl https://api.example.com", &[]),
91 OutputPolicy::Verbatim
92 );
93 }
94
95 #[test]
96 fn cat_is_verbatim() {
97 assert_eq!(classify("cat package.json", &[]), OutputPolicy::Verbatim);
98 }
99
100 #[test]
101 fn cargo_build_is_compressible() {
102 assert_eq!(classify("cargo build", &[]), OutputPolicy::Compressible);
103 }
104
105 #[test]
106 fn npm_test_is_compressible() {
107 assert_eq!(classify("npm test", &[]), OutputPolicy::Compressible);
108 }
109
110 #[test]
111 fn dev_server_is_passthrough() {
112 assert_eq!(classify("npm run dev", &[]), OutputPolicy::Passthrough);
113 assert_eq!(classify("cargo watch", &[]), OutputPolicy::Passthrough);
114 assert_eq!(classify("cargo run", &[]), OutputPolicy::Passthrough);
115 }
116
117 #[test]
118 fn git_diff_is_verbatim() {
119 let policy = classify("git diff", &[]);
124 assert_eq!(policy, OutputPolicy::Compressible);
129 }
130
131 #[test]
132 fn auth_commands_are_passthrough() {
133 assert_eq!(classify("az login", &[]), OutputPolicy::Passthrough);
134 assert_eq!(
135 classify("gcloud auth login", &[]),
136 OutputPolicy::Passthrough
137 );
138 assert_eq!(classify("firebase login", &[]), OutputPolicy::Passthrough);
139 }
140
141 #[test]
142 fn jq_is_verbatim() {
143 assert_eq!(
144 classify("jq '.items' data.json", &[]),
145 OutputPolicy::Verbatim
146 );
147 }
148
149 #[test]
150 fn unknown_command_is_compressible() {
151 assert_eq!(
152 classify("some-random-tool --flag", &[]),
153 OutputPolicy::Compressible
154 );
155 }
156
157 #[test]
158 fn piped_jq_is_verbatim() {
159 assert_eq!(
160 classify("kubectl get pods -o json | jq '.items[]'", &[]),
161 OutputPolicy::Verbatim
162 );
163 }
164
165 #[test]
166 fn policy_is_protected() {
167 assert!(OutputPolicy::Passthrough.is_protected());
168 assert!(OutputPolicy::Verbatim.is_protected());
169 assert!(!OutputPolicy::Compressible.is_protected());
170 }
171
172 #[test]
175 fn issue_198_gh_api_jq() {
176 assert_eq!(
178 classify("gh api repos/yvgude/lean-ctx/issues/198 --jq '.body'", &[]),
179 OutputPolicy::Verbatim
180 );
181 }
182
183 #[test]
184 fn issue_159_cat_pubspec() {
185 assert_eq!(classify("cat pubspec.yaml", &[]), OutputPolicy::Verbatim);
186 }
187
188 #[test]
189 fn issue_114_git_stash() {
190 let p = classify("git stash show", &[]);
195 assert_eq!(p, OutputPolicy::Compressible);
196 }
197
198 #[test]
199 fn issue_194_git_diff_raw() {
200 let p = classify("git diff --cached", &[]);
202 assert_eq!(p, OutputPolicy::Compressible);
203 }
204
205 #[test]
206 fn npm_install_is_compressible() {
207 assert_eq!(
209 classify("npm install -g deepseek-tui", &[]),
210 OutputPolicy::Compressible
211 );
212 }
213
214 #[test]
215 fn pip_install_is_compressible() {
216 assert_eq!(
217 classify("pip install flask", &[]),
218 OutputPolicy::Compressible
219 );
220 }
221
222 #[test]
223 fn kubectl_get_yaml_is_verbatim() {
224 assert_eq!(
225 classify("kubectl get pods -o yaml", &[]),
226 OutputPolicy::Verbatim
227 );
228 }
229
230 #[test]
231 fn docker_inspect_is_verbatim() {
232 assert_eq!(
233 classify("docker inspect my-container", &[]),
234 OutputPolicy::Verbatim
235 );
236 }
237
238 #[test]
239 fn terraform_output_is_verbatim() {
240 assert_eq!(classify("terraform output", &[]), OutputPolicy::Verbatim);
241 }
242
243 #[test]
244 fn heroku_logs_is_verbatim() {
245 assert_eq!(classify("heroku logs --tail", &[]), OutputPolicy::Verbatim);
246 }
247
248 #[test]
249 fn gh_pr_list_is_compressible() {
250 assert_eq!(classify("gh pr list", &[]), OutputPolicy::Compressible);
251 }
252
253 #[test]
254 fn emspace_data_is_verbatim_and_listings_compressible() {
255 for data in [
256 "emspace page show core --json",
257 "emspace page show --body core",
258 "emspace schema pages",
259 "emspace run --query prog.js",
260 ] {
261 assert_eq!(classify(data, &[]), OutputPolicy::Verbatim, "{data}");
262 }
263 assert_eq!(
264 classify("emspace search budget", &[]),
265 OutputPolicy::Compressible
266 );
267 assert_eq!(
268 classify("emspace page show core", &[]),
269 OutputPolicy::Compressible
270 );
271 }
272
273 #[test]
274 fn lean_ctx_is_passthrough() {
275 assert_eq!(
276 classify("lean-ctx init powershell", &[]),
277 OutputPolicy::Passthrough
278 );
279 assert_eq!(
280 classify("lean-ctx overview", &[]),
281 OutputPolicy::Passthrough
282 );
283 }
284
285 #[test]
286 fn stripe_list_is_verbatim() {
287 assert_eq!(classify("stripe charges list", &[]), OutputPolicy::Verbatim);
288 }
289
290 #[test]
293 fn git_commit_is_passthrough() {
294 assert_eq!(
295 classify("git commit -m \"feat: add feature\"", &[]),
296 OutputPolicy::Passthrough
297 );
298 }
299
300 #[test]
301 fn git_push_is_passthrough() {
302 assert_eq!(
303 classify("git push origin main", &[]),
304 OutputPolicy::Passthrough
305 );
306 }
307
308 #[test]
309 fn git_pull_is_passthrough() {
310 assert_eq!(
311 classify("git pull --rebase", &[]),
312 OutputPolicy::Passthrough
313 );
314 }
315
316 #[test]
317 fn git_merge_is_passthrough() {
318 assert_eq!(
319 classify("git merge feature-branch", &[]),
320 OutputPolicy::Passthrough
321 );
322 }
323
324 #[test]
325 fn git_rebase_is_passthrough() {
326 assert_eq!(classify("git rebase main", &[]), OutputPolicy::Passthrough);
327 }
328
329 #[test]
330 fn git_cherry_pick_is_passthrough() {
331 assert_eq!(
332 classify("git cherry-pick abc1234", &[]),
333 OutputPolicy::Passthrough
334 );
335 }
336
337 #[test]
338 fn git_tag_is_passthrough() {
339 assert_eq!(classify("git tag v1.0.0", &[]), OutputPolicy::Passthrough);
340 }
341
342 #[test]
343 fn git_status_still_compressible() {
344 assert_eq!(classify("git status", &[]), OutputPolicy::Compressible);
345 }
346
347 #[test]
348 fn git_log_still_compressible() {
349 assert_eq!(
350 classify("git log --oneline", &[]),
351 OutputPolicy::Compressible
352 );
353 }
354}