oxur_cli/repl/
info.rs

1//! System information display for the REPL
2//!
3//! Provides display functions for system metadata using OxurTable formatting.
4//! Core data capture is in oxur-repl::metadata; this module handles presentation.
5
6use crate::table::{OxurTable, Tabled};
7use oxur_repl::metadata::SystemMetadata;
8
9/// Show system information in a formatted table
10///
11/// Displays system metadata captured at startup including versions,
12/// platform info, process details, and uptime.
13pub fn show_system_info(metadata: &SystemMetadata, _color_enabled: bool) -> String {
14    let mut output = String::new();
15
16    output.push_str(&header("System Information", _color_enabled));
17    output.push('\n');
18
19    #[derive(Tabled)]
20    struct InfoRow {
21        #[tabled(rename = "Property")]
22        property: String,
23        #[tabled(rename = "Value")]
24        value: String,
25    }
26
27    // Build the info rows
28    let rows = vec![
29        InfoRow { property: "Oxur Version".to_string(), value: metadata.oxur_version.clone() },
30        InfoRow { property: "Rust Version".to_string(), value: metadata.rust_version.clone() },
31        InfoRow { property: "Cargo Version".to_string(), value: metadata.cargo_version.clone() },
32        InfoRow {
33            property: "Operating System".to_string(),
34            value: format!("{} {}", metadata.os_name, metadata.os_version),
35        },
36        InfoRow { property: "Architecture".to_string(), value: metadata.arch.clone() },
37        InfoRow { property: "Hostname".to_string(), value: metadata.hostname.clone() },
38        InfoRow { property: "Process ID".to_string(), value: metadata.pid.to_string() },
39        InfoRow {
40            property: "Working Directory".to_string(),
41            value: metadata.cwd.display().to_string(),
42        },
43        InfoRow { property: "Uptime".to_string(), value: format_uptime(metadata.uptime_seconds()) },
44    ];
45
46    output.push_str(&OxurTable::new(rows).with_title("SYSTEM INFO").with_footer().render());
47    output.push('\n');
48
49    output
50}
51
52/// Format header with optional color
53fn header(text: &str, color_enabled: bool) -> String {
54    if color_enabled {
55        format!("\x1b[1;36m{}\x1b[0m\n{}\n", text, "═".repeat(text.len()))
56    } else {
57        format!("{}\n{}\n", text, "=".repeat(text.len()))
58    }
59}
60
61/// Format uptime in human-readable form
62fn format_uptime(seconds: f64) -> String {
63    let secs = seconds as u64;
64    const MINUTE: u64 = 60;
65    const HOUR: u64 = MINUTE * 60;
66    const DAY: u64 = HOUR * 24;
67
68    if secs >= DAY {
69        let days = secs / DAY;
70        let hours = (secs % DAY) / HOUR;
71        format!("{}d {}h", days, hours)
72    } else if secs >= HOUR {
73        let hours = secs / HOUR;
74        let mins = (secs % HOUR) / MINUTE;
75        format!("{}h {}m", hours, mins)
76    } else if secs >= MINUTE {
77        let mins = secs / MINUTE;
78        let s = secs % MINUTE;
79        format!("{}m {}s", mins, s)
80    } else {
81        format!("{:.1}s", seconds)
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_format_uptime_seconds() {
91        assert_eq!(format_uptime(0.0), "0.0s");
92        assert_eq!(format_uptime(5.5), "5.5s");
93        assert_eq!(format_uptime(30.0), "30.0s");
94    }
95
96    #[test]
97    fn test_format_uptime_minutes() {
98        assert_eq!(format_uptime(60.0), "1m 0s");
99        assert_eq!(format_uptime(90.0), "1m 30s");
100        assert_eq!(format_uptime(3599.0), "59m 59s");
101    }
102
103    #[test]
104    fn test_format_uptime_hours() {
105        assert_eq!(format_uptime(3600.0), "1h 0m");
106        assert_eq!(format_uptime(7200.0), "2h 0m");
107        assert_eq!(format_uptime(5400.0), "1h 30m");
108    }
109
110    #[test]
111    fn test_format_uptime_days() {
112        assert_eq!(format_uptime(86400.0), "1d 0h");
113        assert_eq!(format_uptime(172800.0), "2d 0h");
114        assert_eq!(format_uptime(90000.0), "1d 1h");
115    }
116
117    #[test]
118    fn test_show_system_info() {
119        let metadata = SystemMetadata::capture();
120        let output = show_system_info(&metadata, false);
121
122        // Should contain expected sections
123        assert!(output.contains("System Information"));
124        assert!(output.contains("SYSTEM INFO"));
125        assert!(output.contains("Oxur Version"));
126        assert!(output.contains("Rust Version"));
127        assert!(output.contains("Process ID"));
128        assert!(output.contains("Working Directory"));
129        assert!(output.contains("Uptime"));
130    }
131
132    #[test]
133    fn test_show_system_info_with_color() {
134        let metadata = SystemMetadata::capture();
135        let output = show_system_info(&metadata, true);
136
137        // Should contain ANSI escape codes
138        assert!(output.contains("\x1b["));
139    }
140
141    #[test]
142    fn test_header_with_color() {
143        let header = header("Test", true);
144        assert!(header.contains("\x1b[1;36m"));
145        assert!(header.contains("Test"));
146    }
147
148    #[test]
149    fn test_header_without_color() {
150        let header = header("Test", false);
151        assert!(!header.contains("\x1b["));
152        assert!(header.contains("Test"));
153        assert!(header.contains("===="));
154    }
155}