Skip to main content

lean_ctx/core/patterns/
ansible.rs

1use std::collections::HashMap;
2
3pub fn compress(_cmd: &str, output: &str) -> Option<String> {
4    let trimmed = output.trim();
5    if trimmed.is_empty() {
6        return Some("ok".to_string());
7    }
8
9    let has_recap = trimmed.contains("PLAY RECAP");
10    if has_recap {
11        return Some(compress_playbook(trimmed));
12    }
13
14    if trimmed.contains("TASK [") {
15        return Some(compress_tasks(trimmed));
16    }
17
18    Some(compact_lines(trimmed, 15))
19}
20
21fn compress_playbook(output: &str) -> String {
22    let mut recap_lines = Vec::new();
23    let mut in_recap = false;
24
25    for line in output.lines() {
26        if line.contains("PLAY RECAP") {
27            in_recap = true;
28            continue;
29        }
30        if in_recap {
31            let trimmed = line.trim();
32            if !trimmed.is_empty() {
33                recap_lines.push(trimmed.to_string());
34            }
35        }
36    }
37
38    if recap_lines.is_empty() {
39        return compact_lines(output, 15);
40    }
41
42    let mut result = String::from("PLAY RECAP:");
43    for line in &recap_lines {
44        result.push_str(&format!("\n  {line}"));
45    }
46    result
47}
48
49fn compress_tasks(output: &str) -> String {
50    let mut tasks: HashMap<String, Vec<String>> = HashMap::new();
51
52    for line in output.lines() {
53        let trimmed = line.trim();
54        if trimmed.starts_with("ok:")
55            || trimmed.starts_with("changed:")
56            || trimmed.starts_with("failed:")
57            || trimmed.starts_with("skipping:")
58        {
59            let status = trimmed.split(':').next().unwrap_or("?").to_string();
60            tasks.entry(status).or_default().push(trimmed.to_string());
61        }
62    }
63
64    if tasks.is_empty() {
65        return compact_lines(output, 15);
66    }
67
68    let mut result = Vec::new();
69    for (status, items) in &tasks {
70        result.push(format!("{status}: {}", items.len()));
71    }
72    result.join(", ")
73}
74
75fn compact_lines(text: &str, max: usize) -> String {
76    let lines: Vec<&str> = text.lines().filter(|l| !l.trim().is_empty()).collect();
77    if lines.len() <= max {
78        return lines.join("\n");
79    }
80    format!(
81        "{}\n... ({} more lines)",
82        lines[..max].join("\n"),
83        lines.len() - max
84    )
85}