ferric_ai/parser/
display.rs1use std::collections::HashSet;
4use std::fmt;
5use uuid::Uuid;
6
7use super::tree::Tree;
8
9#[derive(Debug)]
11pub struct TreeStats {
12 pub total_calls: usize,
13 pub unique_functions: usize,
14 pub root_count: usize,
15}
16
17fn 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
33fn 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 for (i, &child_id) in node.children.iter().enumerate() {
38 let is_last = i == node.children.len() - 1;
39
40 if let Some(child_node) = tree.nodes.get(&child_id) {
42 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 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}