use std::thread;
use std::time::{Duration, Instant};
use gilt::console::Console;
use gilt::live::Live;
use gilt::style::Style;
use gilt::text::Text;
struct SimpleRng {
state: u64,
}
impl SimpleRng {
fn new(seed: u64) -> Self {
SimpleRng { state: seed }
}
fn next_u64(&mut self) -> u64 {
self.state = self
.state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
self.state >> 33
}
fn next_f64(&mut self) -> f64 {
self.next_u64() as f64 / u32::MAX as f64
}
fn next_range(&mut self, min: u64, max: u64) -> u64 {
min + self.next_u64() % (max - min + 1)
}
}
#[derive(Clone)]
struct Process {
pid: u32,
command: String,
cpu_percent: f64,
memory: u64,
start_secs_ago: u64,
thread_count: u32,
state: &'static str,
}
impl Process {
fn memory_str(&self) -> String {
if self.memory > 1_000_000 {
format!("{}M", self.memory / 1_000_000)
} else if self.memory > 1_000 {
format!("{}K", self.memory / 1_000)
} else {
format!("{}B", self.memory)
}
}
fn time_str(&self) -> String {
let secs = self.start_secs_ago;
let hours = secs / 3600;
let mins = (secs % 3600) / 60;
let s = secs % 60;
format!("{:02}:{:02}:{:02}", hours, mins, s)
}
}
static COMMANDS: &[&str] = &[
"systemd",
"kworker/0:1",
"sshd",
"bash",
"cargo",
"rustc",
"node",
"python3",
"nginx",
"postgres",
"redis-server",
"dockerd",
"containerd",
"Xorg",
"pulseaudio",
"dbus-daemon",
"NetworkManager",
"vim",
"top",
"htop",
"git",
"rsync",
"tar",
"wget",
"curl",
"journald",
"cron",
"syslog-ng",
"snapd",
"code-server",
];
fn generate_processes(rng: &mut SimpleRng, count: usize) -> Vec<Process> {
let mut processes: Vec<Process> = Vec::with_capacity(count);
for i in 0..count {
let cmd_idx = rng.next_range(0, COMMANDS.len() as u64 - 1) as usize;
let cpu = rng.next_f64() * 25.0;
let mem_base = rng.next_range(10, 200);
let memory = mem_base * mem_base * mem_base; let start_secs = rng.next_range(0, 500) * rng.next_range(0, 500);
let threads = rng.next_range(1, 32) as u32;
let state = if rng.next_range(0, 10) < 8 {
"running"
} else {
"sleeping"
};
processes.push(Process {
pid: (i as u32 + 1) * 100 + rng.next_range(1, 99) as u32,
command: COMMANDS[cmd_idx].to_string(),
cpu_percent: cpu,
memory,
start_secs_ago: start_secs,
thread_count: threads,
state,
});
}
processes.sort_by(|a, b| b.cpu_percent.partial_cmp(&a.cpu_percent).unwrap());
processes
}
fn build_process_display(processes: &[Process]) -> Text {
let header_style = Style::parse("bold white on blue").unwrap_or_else(|_| Style::null());
let dim_style = Style::parse("dim").unwrap_or_else(|_| Style::null());
let mut text = Text::empty();
let header = format!(
" {:>6} {:<18} {:>6} {:>8} {:>10} {:>4} {:<10}\n",
"PID", "Command", "CPU %", "Memory", "Time", "THR", "State"
);
text.append_str(&header, Some(header_style));
text.append_str(&format!(" {}\n", "-".repeat(72)), Some(dim_style.clone()));
for (i, proc) in processes.iter().enumerate() {
let cpu_style = if proc.cpu_percent > 15.0 {
Style::parse("bold red").unwrap_or_else(|_| Style::null())
} else if proc.cpu_percent > 8.0 {
Style::parse("yellow").unwrap_or_else(|_| Style::null())
} else {
Style::parse("green").unwrap_or_else(|_| Style::null())
};
let state_style = if proc.state == "running" {
Style::parse("bold green").unwrap_or_else(|_| Style::null())
} else {
Style::parse("dim").unwrap_or_else(|_| Style::null())
};
let row_base = if i % 2 == 1 {
Style::parse("dim").ok()
} else {
None
};
let pid_str = format!(" {:>6} ", proc.pid);
text.append_str(&pid_str, row_base.clone());
let cmd_str = format!("{:<18} ", proc.command);
text.append_str(&cmd_str, row_base.clone());
let cpu_str = format!("{:>5.1}% ", proc.cpu_percent);
text.append_str(&cpu_str, Some(cpu_style));
let mem_str = format!("{:>8} ", proc.memory_str());
text.append_str(&mem_str, row_base.clone());
let time_str = format!("{:>10} ", proc.time_str());
text.append_str(&time_str, row_base.clone());
let thr_str = format!("{:>4} ", proc.thread_count);
text.append_str(&thr_str, row_base);
let state_str = format!("{:<10}\n", proc.state);
text.append_str(&state_str, Some(state_style));
}
text.append_str(
"\n Press Ctrl+C to exit (auto-exits after ~10 seconds)",
Some(dim_style),
);
text
}
fn main() {
let console = Console::builder()
.force_terminal(true)
.no_color(false)
.build();
let mut rng = SimpleRng::new(42);
let process_count = 20;
let initial_processes = generate_processes(&mut rng, process_count);
let initial_display = build_process_display(&initial_processes);
let mut live = Live::new(initial_display)
.with_console(console)
.with_auto_refresh(false)
.with_transient(true);
live.start();
let start_time = Instant::now();
let duration = Duration::from_secs(10);
while start_time.elapsed() < duration {
let processes = generate_processes(&mut rng, process_count);
let display = build_process_display(&processes);
live.update(display, true);
thread::sleep(Duration::from_millis(500));
}
live.stop();
println!("\nProcess monitor ended.");
}