lean_ctx_patterns/
glab.rs1pub fn try_glab_pattern(cmd: &str, output: &str) -> Option<String> {
4 let parts: Vec<&str> = cmd.split_whitespace().collect();
5 if parts.is_empty() || parts[0] != "glab" {
6 return None;
7 }
8 if parts.len() < 2 {
9 return None;
10 }
11
12 match parts[1] {
13 "issue" => try_glab_issue(parts.get(2).copied(), output),
14 "mr" => try_glab_mr(parts.get(2).copied(), output),
15 "ci" => try_glab_ci(parts.get(2).copied(), output),
16 _ => None,
17 }
18}
19
20fn try_glab_issue(subcommand: Option<&str>, output: &str) -> Option<String> {
21 match subcommand.unwrap_or("") {
22 "list" => Some(compress_table_output("glab issues", output)),
23 "view" => Some(compress_detail_output("issue", output)),
24 _ => None,
25 }
26}
27
28fn try_glab_mr(subcommand: Option<&str>, output: &str) -> Option<String> {
29 match subcommand.unwrap_or("") {
30 "list" => Some(compress_table_output("glab MRs", output)),
31 "view" => Some(compress_detail_output("MR", output)),
32 _ => None,
33 }
34}
35
36fn try_glab_ci(subcommand: Option<&str>, output: &str) -> Option<String> {
37 match subcommand.unwrap_or("") {
38 "status" | "list" | "view" => Some(compress_ci_output(output)),
39 _ => None,
40 }
41}
42
43fn compress_table_output(label: &str, output: &str) -> String {
44 let lines: Vec<&str> = output.lines().collect();
45 if lines.is_empty() {
46 return format!("{label}: (empty)");
47 }
48 let count = lines.len().saturating_sub(1);
49 let mut result = format!("{label} ({count}):\n");
50 for line in &lines {
51 let trimmed = line.trim();
52 if !trimmed.is_empty() {
53 result.push_str(&format!(" {trimmed}\n"));
54 }
55 }
56 result
57}
58
59fn compress_detail_output(kind: &str, output: &str) -> String {
60 let mut result = String::new();
61 let mut in_body = false;
62 let mut body_lines = 0;
63 const MAX_BODY_LINES: usize = 30;
64
65 for line in output.lines() {
66 let trimmed = line.trim();
67 if trimmed.starts_with("title:")
68 || trimmed.starts_with("state:")
69 || trimmed.starts_with("author:")
70 || trimmed.starts_with("labels:")
71 || trimmed.starts_with("milestone:")
72 || trimmed.starts_with("assignees:")
73 || trimmed.starts_with("created:")
74 || trimmed.starts_with("updated:")
75 {
76 result.push_str(&format!("{trimmed}\n"));
77 in_body = false;
78 } else if trimmed == "--" || trimmed.starts_with("---") {
79 in_body = true;
80 result.push_str("---\n");
81 } else if in_body {
82 body_lines += 1;
83 if body_lines <= MAX_BODY_LINES {
84 result.push_str(&format!("{line}\n"));
85 } else if body_lines == MAX_BODY_LINES + 1 {
86 result.push_str(&format!("... ({kind} body truncated)\n"));
87 }
88 } else if !trimmed.is_empty() {
89 result.push_str(&format!("{trimmed}\n"));
90 }
91 }
92 result
93}
94
95fn compress_ci_output(output: &str) -> String {
96 let mut result = String::from("CI pipeline:\n");
97 for line in output.lines() {
98 let trimmed = line.trim();
99 if trimmed.is_empty() {
100 continue;
101 }
102 result.push_str(&format!(" {trimmed}\n"));
103 }
104 result
105}