Skip to main content

lean_ctx_patterns/
ansible.rs

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