1use std::fmt::Write;
2
3use crate::protocol::Response;
4
5#[must_use]
7pub fn format(response: &Response) -> String {
8 if let Some(error) = &response.error {
9 let mut out = format!("Error: {}\nCode: {}", error.message, error.code);
10 if let Some(advice) = &error.advice {
11 let _ = write!(out, "\n\nAdvice: {advice}");
12 }
13 return out;
14 }
15
16 let Some(data) = &response.data else {
17 return String::new();
18 };
19
20 if let Some(daemon) = data.get("daemon") {
22 let pid = daemon
23 .get("pid")
24 .and_then(serde_json::Value::as_u64)
25 .unwrap_or(0);
26 let uptime = daemon
27 .get("uptime_secs")
28 .and_then(serde_json::Value::as_u64)
29 .unwrap_or(0);
30 let mut out = format!(
31 "Daemon Status\n PID: {pid}\n Uptime: {}",
32 format_duration_human(uptime)
33 );
34
35 if let Some(lsp) = data.get("lsp") {
36 if !lsp.is_null() {
37 let lang = lsp.get("language").and_then(|v| v.as_str()).unwrap_or("?");
38 let status = lsp.get("status").and_then(|v| v.as_str()).unwrap_or("?");
39 let server = lsp.get("server").and_then(|v| v.as_str()).unwrap_or("?");
40 let _ = write!(out, "\n\nLSP Server\n Language: {lang}\n Status: {status}\n Server: {server}");
41 }
42 }
43
44 if let Some(project) = data.get("project") {
45 if let Some(root) = project.get("root").and_then(|v| v.as_str()) {
46 let _ = write!(out, "\n\nProject\n Root: {root}");
47 }
48 if let Some(langs) = project.get("languages").and_then(|v| v.as_array()) {
49 let names: Vec<&str> = langs.iter().filter_map(|v| v.as_str()).collect();
50 if !names.is_empty() {
51 let _ = write!(out, "\n Languages: {}", names.join(", "));
52 }
53 }
54 }
55
56 return out;
57 }
58
59 serde_json::to_string_pretty(data).unwrap_or_default()
61}
62
63fn format_duration_human(secs: u64) -> String {
64 if secs < 60 {
65 format!("{secs} seconds")
66 } else if secs < 3600 {
67 let m = secs / 60;
68 if m == 1 {
69 "1 minute".into()
70 } else {
71 format!("{m} minutes")
72 }
73 } else {
74 let h = secs / 3600;
75 let m = (secs % 3600) / 60;
76 let h_str = if h == 1 {
77 "1 hour".into()
78 } else {
79 format!("{h} hours")
80 };
81 if m == 0 {
82 h_str
83 } else {
84 format!("{h_str} {m} minutes")
85 }
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use serde_json::json;
92
93 use super::*;
94
95 #[test]
96 fn human_status_output() {
97 let resp = Response::ok(json!({"daemon": {"pid": 12345, "uptime_secs": 300}}));
98 let out = format(&resp);
99 assert!(out.contains("Daemon Status"));
100 assert!(out.contains("PID: 12345"));
101 assert!(out.contains("Uptime: 5 minutes"));
102 }
103
104 #[test]
105 fn human_error_with_advice() {
106 let resp = Response::err_with_advice("lsp_not_found", "LSP not detected", "Install it");
107 let out = format(&resp);
108 assert!(out.contains("Error: LSP not detected"));
109 assert!(out.contains("Advice: Install it"));
110 }
111}