Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/rafaelrcamargo/ds
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelrcamargo committed Oct 3, 2023
2 parents f862ce2 + 28d8ff8 commit eff9aaa
Showing 1 changed file with 169 additions and 119 deletions.
288 changes: 169 additions & 119 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
mod cli;

mod utils;
use utils::*;

mod data;
use data::DockerStats;
mod utils;

use byte_unit::Byte;
use colored::Colorize;
use data::DockerStats;
use std::{
collections::HashMap,
io::{BufRead, BufReader},
process::{Command, Stdio}
process::{Command, Stdio},
sync::{
mpsc::{self, Receiver},
Arc, Mutex
},
thread,
time::Instant
};
use utils::*;

fn main() {
let matches = cli::args().get_matches();
Expand All @@ -22,9 +25,14 @@ fn main() {
cli::has_flag(&matches, "full")
);

let mut containers: HashMap<String, DockerStats> = HashMap::new();
let containers: Arc<Mutex<Vec<DockerStats>>> = Arc::new(Mutex::new(Vec::new()));
let width = get_terminal_width();

let (sender, receiver) = mpsc::channel::<()>();

let t_containers = containers.clone();
let purger = thread::spawn(move || purge(receiver, t_containers, compact, full, width));

let mut cmd = Command::new("docker")
.args(build_command(matches))
.stdout(Stdio::piped())
Expand All @@ -35,137 +43,179 @@ fn main() {
let stdout_reader = BufReader::new(stdout);
let stdout_lines = stdout_reader.lines();

// print!("\x1B[s"); // Save cursor position

// Clear line and print
// fn cap(string: String) { print!("\x1B[K{}\n", string) }

println!("Fetching container stats...");

for line in stdout_lines {
print!("\x1B[2J\x1B[1;1H"); // Clear screen
let mut line = line.unwrap();
// dbg!(line.clone());

sender.send(()).unwrap();

let containers = containers.clone();
if line.starts_with("\u{1b}[2J\u{1b}[H") {
// println!("{}", "\x1B[u"); // Restore cursor position
print(containers.clone(), compact, full, width); // Print the charts
containers.lock().unwrap().clear(); // Reset the containers

let line = line
.unwrap()
.replace("\u{1b}[2J\u{1b}[H", "");
line = line.replace("\u{1b}[2J\u{1b}[H", "")
}

let stats: DockerStats = serde_json::from_str(&line).unwrap();
containers.insert(stats.id.clone(), stats);
containers.lock().unwrap().push(stats);
}

let mut max = 100f32;
purger.join().unwrap();
let status = cmd.wait();
println!("Exited with status {:?}", status);
}

for (i, (_id, stats)) in containers.iter().enumerate() {
// LAYOUT
{
if !compact || i == 0 {
println!("┌─ {} {}┐", stats.name, filler("─", width, stats.name.len() + 5));
} else {
println!("├─ {} {}┤", stats.name, fill_on_even("─", width, stats.name.len() + 5));
}
fn purge(receiver: Receiver<()>, containers: Arc<Mutex<Vec<DockerStats>>>, compact: bool, full: bool, width: usize) {
let mut last_message: Instant = Instant::now();

loop {
if receiver.try_recv().is_ok() {
last_message = Instant::now()
}

if last_message.elapsed().as_secs() > 2 {
containers.lock().unwrap().clear(); // Clear the containers list
print(containers.clone(), compact, full, width); // Print the charts
last_message = Instant::now()
}
}
}

fn print(containers: Arc<Mutex<Vec<DockerStats>>>, compact: bool, full: bool, width: usize) {
print!("\x1B[2J\x1B[1;1H"); // Clear screen

let containers = containers.lock().unwrap();
let mut max = 100f32;

if containers.len() == 0 {
println!("Waiting for container stats...");
return;
}

for (i, stats) in containers.iter().enumerate() {
// LAYOUT
{
if !compact || i == 0 {
println!("┌─ {} {}┐", stats.name, filler("─", width, stats.name.len() + 5));
} else {
println!("├─ {} {}┤", stats.name, fill_on_even("─", width, stats.name.len() + 5));
}
}

// CONSTANTS
let mem_perc: f32 = perc_to_float(stats.mem_perc.clone());
let cpu_perc: f32 = perc_to_float(stats.cpu_perc.clone());

// GLOBAL SCALE
max = max.max(mem_perc);
max = max.max(cpu_perc);

// CPU
{
let scale_factor = (width - 18) as f32 / max;
let cpu_perc = (cpu_perc * scale_factor) as usize;
println!(
"│ CPU | {}{} {}{} │",
filler(" ", 7, stats.cpu_perc.len()),
stats.cpu_perc,
usize_to_status(cpu_perc, width),
filler("░", width, cpu_perc + 18).dimmed()
);
}

// CONSTANTS
let mem_perc: f32 = perc_to_float(stats.mem_perc.clone());
let cpu_perc: f32 = perc_to_float(stats.cpu_perc.clone());
// RAM
{
let mem_usage_len = stats.mem_usage.len() + 1;
let scale_factor = (width - (18 + mem_usage_len)) as f32 / max;
let mem_perc = (mem_perc * scale_factor) as usize;
println!(
"│ RAM | {}{} {}{}{} {} │",
filler(" ", 7, stats.mem_perc.len()),
stats.mem_perc,
usize_to_status(mem_perc, width - (18 + mem_usage_len)),
filler("░", width, mem_perc + (18 + mem_usage_len)).dimmed(),
filler(" ", mem_usage_len, mem_usage_len),
stats.mem_usage
);
}

// GLOBAL SCALE
max = max.max(mem_perc);
max = max.max(cpu_perc);
if full {
println!("│{}│", fill_on_even("─", width, 2).dimmed());

// CPU
// NET
{
let scale_factor = (width - 18) as f32 / max;
let cpu_perc = (cpu_perc * scale_factor) as usize;
let net: Vec<usize> = {
let net = stats
.net_io
.split(" / ")
.collect::<Vec<&str>>();

let bytes = vec![
Byte::from_str(net[0])
.expect("Failed to parse Byte")
.get_bytes(),
Byte::from_str(net[1])
.expect("Failed to parse Byte")
.get_bytes(),
];

match scale_between(bytes, 1, width - 12) {
None => balanced_split(width - 11),
Some(scaled_net) => scaled_net
}
};

println!(
"│ CPU | {}{} {}{} │",
filler(" ", 7, stats.cpu_perc.len()),
stats.cpu_perc,
usize_to_status(cpu_perc, width),
filler("░", width, cpu_perc + 18).dimmed()
"│ NET | {}{}{} │",
filler("▒", width - 11, net[0]).green(),
"░".dimmed(),
filler("▒", width - 11, net[1]).red()
);
}

// RAM
// IO
{
let mem_usage_len = stats.mem_usage.len() + 1;
let scale_factor = (width - (18 + mem_usage_len)) as f32 / max;
let mem_perc = (mem_perc * scale_factor) as usize;
let io = {
let blocks = stats
.block_io
.split(" / ")
.collect::<Vec<&str>>();

let bytes = vec![
Byte::from_str(blocks[0])
.expect("Failed to parse Byte")
.get_bytes(),
Byte::from_str(blocks[1])
.expect("Failed to parse Byte")
.get_bytes(),
];

match scale_between(bytes, 1, width - 12) {
None => balanced_split(width - 11),
Some(scaled_io) => scaled_io
}
};

println!(
"│ RAM | {}{} {}{}{} {} │",
filler(" ", 7, stats.mem_perc.len()),
stats.mem_perc,
usize_to_status(mem_perc, width - (18 + mem_usage_len)),
filler("░", width, mem_perc + (18 + mem_usage_len)).dimmed(),
filler(" ", mem_usage_len, mem_usage_len),
stats.mem_usage
"│ IO | {}{}{} │",
filler("▒", io[0], 0).white(),
"░".dimmed(),
filler("▒", io[1], 0).black()
);
}
}

if full {
println!("│{}│", fill_on_even("─", width, 2).dimmed());

// NET
{
let net: Vec<usize> = {
let net = stats
.net_io
.split(" / ")
.collect::<Vec<&str>>();

let bytes = vec![
Byte::from_str(net[0])
.expect("Failed to parse Byte")
.get_bytes(),
Byte::from_str(net[1])
.expect("Failed to parse Byte")
.get_bytes(),
];

match scale_between(bytes, 1, width - 12) {
None => balanced_split(width - 11),
Some(scaled_net) => scaled_net
}
};

println!(
"│ NET | {}{}{} │",
filler("▒", net[0], width - 12).green(),
"░".dimmed(),
filler("▒", net[1], width - 12).red()
);
}

// IO
{
let io = {
let blocks = stats
.block_io
.split(" / ")
.collect::<Vec<&str>>();

let bytes = vec![
Byte::from_str(blocks[0])
.expect("Failed to parse Byte")
.get_bytes(),
Byte::from_str(blocks[1])
.expect("Failed to parse Byte")
.get_bytes(),
];

match scale_between(bytes, 1, width - 12) {
None => balanced_split(width - 11),
Some(scaled_io) => scaled_io
}
};

println!(
"│ IO | {}{}{} │",
filler("▒", io[0], 0).white(),
"░".dimmed(),
filler("▒", io[1], 0).black()
);
}
}

if !compact || i == containers.len() - 1 {
println!("└{}┘", filler("─", width, 2));
}
if !compact || i == containers.len() - 1 {
println!("└{}┘", filler("─", width, 2));
}
}

let status = cmd.wait();
println!("Exited with status {:?}", status);
}

0 comments on commit eff9aaa

Please sign in to comment.