ferric_ai/parser/
display.rs

1// Display implementation for Tree structures
2
3use std::collections::HashSet;
4use std::fmt;
5use uuid::Uuid;
6
7use super::tree::Tree;
8
9/// Statistics about the call tree
10#[derive(Debug)]
11pub struct TreeStats {
12    pub total_calls: usize,
13    pub unique_functions: usize,
14    pub root_count: usize,
15}
16
17/// Get statistics about the tree
18fn get_tree_stats(tree: &Tree) -> TreeStats {
19    let mut unique_function_names = HashSet::new();
20    let total_calls = tree.nodes.len();
21
22    for node in tree.nodes.values() {
23        unique_function_names.insert(&node.function_name);
24    }
25
26    TreeStats {
27        total_calls,
28        unique_functions: unique_function_names.len(),
29        root_count: tree.roots.len(),
30    }
31}
32
33/// Helper function to recursively format tree nodes
34fn format_node_recursive(tree: &Tree, f: &mut fmt::Formatter<'_>, node_id: Uuid, prefixes: Vec<bool>) -> fmt::Result {
35    if let Some(node) = tree.nodes.get(&node_id) {
36        // Print children
37        for (i, &child_id) in node.children.iter().enumerate() {
38            let is_last = i == node.children.len() - 1;
39
40            // Print child with proper branching
41            if let Some(child_node) = tree.nodes.get(&child_id) {
42                // Build prefix from parent levels
43                let mut child_prefix = String::new();
44                for &parent_continues in &prefixes {
45                    if parent_continues {
46                        child_prefix.push_str("│ ");
47                    } else {
48                        child_prefix.push_str("  ");
49                    }
50                }
51
52                let branch_char = if is_last { "└─" } else { "├─" };
53                writeln!(f,
54                    "{}{} {} ({:.2}% CPU, {} total, {} self)",
55                    child_prefix, branch_char, child_node.function_name,
56                    child_node.cpu_percent, child_node.sample_count, child_node.self_sample_count
57                )?;
58
59                // Recursively format child's children
60                if !child_node.children.is_empty() {
61                    let mut new_prefixes = prefixes.clone();
62                    new_prefixes.push(!is_last);
63                    format_node_recursive(tree, f, child_id, new_prefixes)?;
64                }
65            }
66        }
67    }
68    Ok(())
69}
70
71impl fmt::Display for Tree {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        let stats = get_tree_stats(self);
74
75        writeln!(f, "=== Flamegraph Call Tree ===")?;
76        writeln!(f, "Total calls: {}", stats.total_calls)?;
77        writeln!(f, "Unique functions: {}", stats.unique_functions)?;
78        writeln!(f, "Root nodes: {}", stats.root_count)?;
79        writeln!(f)?;
80
81        for (i, &root_id) in self.roots.iter().enumerate() {
82            if let Some(root_node) = self.nodes.get(&root_id) {
83                writeln!(f, "{} ({:.2}% CPU, {} total, {} self)",
84                    root_node.function_name, root_node.cpu_percent, root_node.sample_count, root_node.self_sample_count)?;
85                format_node_recursive(self, f, root_id, Vec::new())?;
86                if i < self.roots.len() - 1 {
87                    writeln!(f)?;
88                }
89            }
90        }
91        Ok(())
92    }
93}