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