Skip to main content

lean_ctx/core/patterns/
glab.rs

1pub 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}