Skip to main content

jigs_log/
tree.rs

1use jigs_trace::Entry;
2
3/// Render entries as a depth-indented tree with status mark and duration.
4pub fn render_tree(entries: &[Entry]) -> String {
5    let labels: Vec<String> = entries
6        .iter()
7        .map(|e| {
8            let indent = if e.depth == 0 {
9                String::new()
10            } else {
11                format!("{}└─ ", "  ".repeat(e.depth - 1))
12            };
13            format!("{}{}", indent, e.name)
14        })
15        .collect();
16    let width = labels.iter().map(|l| l.chars().count()).max().unwrap_or(0);
17
18    let mut out = String::new();
19    for (label, e) in labels.iter().zip(entries) {
20        let pad = width - label.chars().count();
21        let mark = if e.ok { "ok" } else { "err" };
22        let detail = match &e.error {
23            Some(msg) => format!("ERROR: {msg}"),
24            None => format!("{:?}", e.duration),
25        };
26        out.push_str(&format!(
27            "{}{}  {}  {}\n",
28            label,
29            " ".repeat(pad),
30            mark,
31            detail
32        ));
33    }
34    out
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40    use std::time::Duration;
41
42    fn entry(name: &'static str, depth: usize, ok: bool, err: Option<&str>) -> Entry {
43        Entry {
44            name,
45            depth,
46            duration: Duration::from_micros(100),
47            ok,
48            error: err.map(|s| s.to_string()),
49        }
50    }
51
52    #[test]
53    fn empty_input_renders_empty_string() {
54        assert_eq!(render_tree(&[]), "");
55    }
56
57    #[test]
58    fn nested_entries_indent_by_depth() {
59        let entries = [
60            entry("handle", 0, true, None),
61            entry("inner", 1, true, None),
62        ];
63        let out = render_tree(&entries);
64        assert!(out.contains("handle"));
65        let inner_line = out.lines().nth(1).unwrap();
66        assert!(inner_line.starts_with("└─ inner"));
67    }
68
69    #[test]
70    fn errors_are_rendered_with_message() {
71        let entries = [entry("step", 0, false, Some("boom"))];
72        let out = render_tree(&entries);
73        assert!(out.contains("err"));
74        assert!(out.contains("ERROR: boom"));
75    }
76}