1use serde_json::Value;
2
3pub fn format_actor_table(actors: &[Value]) -> String {
5 if actors.is_empty() {
6 return "No actors found.\n".to_string();
7 }
8 let mut out = format!(
9 "{:<40} {:<10} {:>5} {:<14} {:>10} {:>12}\n",
10 "PATH", "STATE", "CORE", "MAILBOX", "MESSAGES", "COMPUTE"
11 );
12 out.push_str(&"-".repeat(97));
13 out.push('\n');
14 for a in actors {
15 let path = a["path"].as_str().unwrap_or("-");
16 let state = a["state"].as_str().unwrap_or("-");
17 let core_id = a["core_id"].as_u64().unwrap_or(0);
18 let depth = a["mailbox_depth"].as_u64().unwrap_or(0);
19 let cap = a["mailbox_capacity"].as_u64().unwrap_or(0);
20 let msgs = a["message_count"].as_u64().unwrap_or(0);
21 let compute = a["total_compute_time_ns"].as_u64().unwrap_or(0);
22 let mailbox = format!("{depth}/{cap}");
23 out.push_str(&format!(
24 "{:<40} {:<10} {:>5} {:<14} {:>10} {:>12}\n",
25 path,
26 state,
27 core_id,
28 mailbox,
29 msgs,
30 format_nanos(compute)
31 ));
32 }
33 out
34}
35
36pub fn format_actor_info(info: &Value) -> String {
38 let mut out = String::new();
39 out.push_str(&format!(
40 "Path: {}\n",
41 info["path"].as_str().unwrap_or("-")
42 ));
43 out.push_str(&format!(
44 "State: {}\n",
45 info["state"].as_str().unwrap_or("-")
46 ));
47 out.push_str(&format!(
48 "Core: {}\n",
49 info["core_id"].as_u64().unwrap_or(0)
50 ));
51 out.push_str(&format!(
52 "Mailbox: {}/{}\n",
53 info["mailbox_depth"].as_u64().unwrap_or(0),
54 info["mailbox_capacity"].as_u64().unwrap_or(0),
55 ));
56 out.push_str(&format!(
57 "Messages: {}\n",
58 info["message_count"].as_u64().unwrap_or(0)
59 ));
60 out.push_str(&format!(
61 "Restarts: {}\n",
62 info["restart_count"].as_u64().unwrap_or(0)
63 ));
64 out.push_str(&format!(
65 "Compute: {}\n",
66 format_nanos(info["total_compute_time_ns"].as_u64().unwrap_or(0))
67 ));
68 out
69}
70
71fn format_nanos(ns: u64) -> String {
72 if ns < 1000 {
73 format!("{ns}ns")
74 } else if ns < 1_000_000 {
75 format!("{:.2}μs", ns as f64 / 1000.0)
76 } else if ns < 1_000_000_000 {
77 format!("{:.2}ms", ns as f64 / 1_000_000.0)
78 } else {
79 format!("{:.2}s", ns as f64 / 1_000_000_000.0)
80 }
81}
82
83pub fn format_mailbox_info(info: &Value) -> String {
85 let path = info["path"].as_str().unwrap_or("-");
86 let depth = info["mailbox_depth"].as_u64().unwrap_or(0);
87 let cap = info["mailbox_capacity"].as_u64().unwrap_or(0);
88 format!("{path}: mailbox {depth}/{cap}\n")
89}
90
91pub fn format_status(result: &Value) -> String {
93 let mut out = String::new();
94 out.push_str("Status: running\n");
95 let uptime_secs = result["uptime_secs"].as_u64().unwrap_or(0);
96 out.push_str(&format!("Uptime: {}\n", format_uptime(uptime_secs)));
97 out.push_str(&format!(
98 "Cores: {}\n",
99 result["num_cores"].as_u64().unwrap_or(0)
100 ));
101 out.push_str(&format!(
102 "Actors: {}/{} running\n",
103 result["running_actors"].as_u64().unwrap_or(0),
104 result["total_actors"].as_u64().unwrap_or(0),
105 ));
106 out
107}
108
109fn format_uptime(total_secs: u64) -> String {
110 let days = total_secs / 86_400;
111 let hours = (total_secs % 86_400) / 3_600;
112 let minutes = (total_secs % 3_600) / 60;
113 let seconds = total_secs % 60;
114 format!("{days}d {hours:02}h {minutes:02}m {seconds:02}s")
115}
116
117pub fn format_cluster_status(result: &Value) -> String {
119 let mut out = String::new();
120 out.push_str("Cluster: running\n");
121 if let Some(local) = result["local_engine"].as_str() {
122 out.push_str(&format!("Local: {local}\n"));
123 }
124 out.push_str(&format!(
125 "Members: {}\n",
126 result["member_count"].as_u64().unwrap_or(0)
127 ));
128 out.push_str(&format!(
129 "Alive: {}\n",
130 result["alive"].as_u64().unwrap_or(0)
131 ));
132 out.push_str(&format!(
133 "Suspect: {}\n",
134 result["suspect"].as_u64().unwrap_or(0)
135 ));
136 out.push_str(&format!(
137 "Dead: {}\n",
138 result["dead"].as_u64().unwrap_or(0)
139 ));
140 out.push_str(&format!(
141 "Left: {}\n",
142 result["left"].as_u64().unwrap_or(0)
143 ));
144 out
145}
146
147pub fn format_cluster_members(members: &[Value]) -> String {
149 if members.is_empty() {
150 return "No cluster members found.\n".to_string();
151 }
152 let mut out = format!(
153 "{:<22} {:<21} {:<8} {:>5} {:>10} {:>8}\n",
154 "ENGINE", "ADDR", "STATE", "GEN", "LAST_MS", "VER"
155 );
156 out.push_str(&"-".repeat(80));
157 out.push('\n');
158 for m in members {
159 let engine = m["engine_id"].as_str().unwrap_or("-");
160 let addr = m["addr"].as_str().unwrap_or("-");
161 let state = m["state"].as_str().unwrap_or("-");
162 let gen = m["generation"].as_u64().unwrap_or(0);
163 let last = m["last_heartbeat_ms"].as_u64().unwrap_or(0);
164 let ver = m["version"].as_u64().unwrap_or(0);
165 out.push_str(&format!(
166 "{:<22} {:<21} {:<8} {:>5} {:>10} {:>8}\n",
167 engine, addr, state, gen, last, ver
168 ));
169 }
170 out
171}