Skip to main content

proc_cli/ui/
format.rs

1//! Shared formatting utilities used across commands and output
2//!
3//! Provides common string formatting, truncation, and colorization.
4
5use crate::core::ProcessStatus;
6use colored::*;
7
8/// Format a duration in seconds to a human-readable string.
9///
10/// Examples: "45s", "3m 12s", "2h 15m", "1d 6h"
11pub fn format_duration(secs: u64) -> String {
12    if secs < 60 {
13        format!("{}s", secs)
14    } else if secs < 3600 {
15        format!("{}m {}s", secs / 60, secs % 60)
16    } else if secs < 86400 {
17        format!("{}h {}m", secs / 3600, (secs % 3600) / 60)
18    } else {
19        format!("{}d {}h", secs / 86400, (secs % 86400) / 3600)
20    }
21}
22
23/// Uptime as a trailing label for one-line process views (tree, why):
24/// `"  up 3m 12s"`, or empty when unknown.
25pub fn uptime_label(uptime_secs: Option<u64>) -> String {
26    uptime_secs
27        .map(|secs| format!("  up {}", format_duration(secs)))
28        .unwrap_or_default()
29}
30
31/// Return "es" for plural process counts, "" for singular.
32///
33/// Usage: `format!("{} process{}", n, plural(n))` → "1 process" or "3 processes"
34pub fn plural(n: usize) -> &'static str {
35    if n == 1 {
36        ""
37    } else {
38        "es"
39    }
40}
41
42/// Truncate a string to a maximum length, appending "..." if truncated.
43pub fn truncate_string(s: &str, max_len: usize) -> String {
44    if s.len() <= max_len {
45        s.to_string()
46    } else {
47        format!("{}...", &s[..max_len.saturating_sub(3)])
48    }
49}
50
51/// Truncate a path intelligently — shows the end (most relevant part).
52pub fn truncate_path(path: &str, max_len: usize) -> String {
53    if path.len() <= max_len {
54        path.to_string()
55    } else {
56        let start = path.len().saturating_sub(max_len.saturating_sub(3));
57        format!("...{}", &path[start..])
58    }
59}
60
61/// Format memory in MB to a human-readable string with adaptive units.
62///
63/// Examples: "512KB", "6.0MB", "3.0GB"
64pub fn format_memory(memory_mb: f64) -> String {
65    if memory_mb < 1.0 {
66        format!("{:.0}KB", memory_mb * 1000.0)
67    } else if memory_mb < 1000.0 {
68        format!("{:.1}MB", memory_mb)
69    } else {
70        format!("{:.1}GB", memory_mb / 1000.0)
71    }
72}
73
74/// Colorize a process status string based on the status variant.
75pub fn colorize_status(status: &ProcessStatus, status_str: &str) -> ColoredString {
76    match status {
77        ProcessStatus::Running => status_str.green(),
78        ProcessStatus::Sleeping => status_str.blue(),
79        ProcessStatus::Stopped => status_str.yellow(),
80        ProcessStatus::Zombie => status_str.red(),
81        _ => status_str.white(),
82    }
83}