swaybar_info/src/main.rs
Stephen Seo 7bba60e323 Impl 0.1.5 of swaybar_info
Implemented `--netdev_width=<width>` which sets the minimum width of the netdev
byte/KiB/MiB text displays.

Implemented `--netgraph_max_bytes=<bytes>` which displays a graph in text using
Unicode "Block Elements" symbols. The `<bytes>` argument determines the maximum
amount of bytes that will determine which block-character is printed on the
interval. The graph is always 10 characters wide, and the right side is the
most-recent side. Note that this always checks against the maximum of either
download or upload rates. For example, if `<bytes>` is set to 1024, and 128
bytes were downloaded and 512 bytes were uploaded in an interval, the "Lower
Half Block" Unicode symbol will be emitted (exactly half).

SwaybarObject was changed to use an `Option<String>` instead of an `Option<u16>`
for `min_width`.
2022-10-18 21:04:28 +09:00

355 lines
13 KiB
Rust

mod args;
mod builtin;
mod external;
mod proc;
mod swaybar_object;
use std::io::{self, Write};
use std::time::Duration;
use swaybar_object::*;
const DEFAULT_FMT_STRING: &str = "%F %r";
fn main() {
let args_result = args::get_args();
if args_result.map.contains_key("help") {
args::print_usage();
return;
}
let mut cmds: Vec<(&str, Vec<&str>, regex::Regex)> = Vec::new();
for regex_cmd in &args_result.regex_cmds {
let mut split_strs = regex_cmd.split_terminator("[SPLIT]");
let cmd: &str = split_strs.next().expect("Should have cmd in option");
let mut args: Vec<&str> = Vec::new();
let mut next: Option<&str>;
loop {
next = split_strs.next();
if let Some(str) = next {
args.push(str);
} else {
break;
}
}
if args.is_empty() {
panic!("Missing regex for --regex-cmd=<cmd>,<args...>,<regex>");
}
let regex_str: &str = args[args.len() - 1];
args.pop();
let regex = regex::Regex::new(regex_str).expect("Should be able to compile regex");
cmds.push((cmd, args, regex));
}
let mut net_obj: Option<proc::NetInfo> = None;
let mut net_width: Option<u16> = Some(10);
let mut net_graph_max: Option<f64> = None;
let mut interval: Duration = Duration::from_secs(5);
if args_result.map.contains_key("netdev") {
net_obj = Some(proc::NetInfo::new(
args_result.map.get("netdev").unwrap().to_owned(),
));
}
if args_result.map.contains_key("netdevwidth") {
let width_result: Result<u16, _> = args_result.map.get("netdevwidth").unwrap().parse();
if let Ok(width) = width_result {
net_width = Some(width);
} else {
let mut stderr_handle = io::stderr().lock();
stderr_handle
.write_all(
format!("WARNING: Invalid value passed to --netdev_width=..., ignoring...\n")
.as_bytes(),
)
.ok();
}
}
if args_result.map.contains_key("netgraph") {
let graph_max_result: Result<f64, _> = args_result.map.get("netgraph").unwrap().parse();
if let Ok(graph_max) = graph_max_result {
net_graph_max = Some(graph_max);
} else {
let mut stderr_handle = io::stderr().lock();
stderr_handle
.write_all(
format!(
"WARNING: Invalid value passed to --netgraph_max_bytes=..., ignoring...\n"
)
.as_bytes(),
)
.ok();
}
}
if args_result.map.contains_key("interval-sec") {
let seconds: Result<i64, _> = args_result.map.get("interval-sec").unwrap().parse();
if let Ok(seconds_value) = seconds {
if seconds_value > 0 {
interval = Duration::from_secs(seconds_value as u64);
} else {
let mut stderr_handle = io::stderr().lock();
stderr_handle
.write_all(
format!(
"WARNING: Invalid --interval-sec=\"{}\", defaulting to 5!\n",
seconds_value
)
.as_bytes(),
)
.ok();
}
} else {
let mut stderr_handle = io::stderr().lock();
stderr_handle
.write_all(b"WARNING: Failed to parse --interval-sec=?, defaulting to 5!\n")
.ok();
}
}
let mut batt_info: builtin::BattInfo = Default::default();
let batt_info_enabled: bool = args_result.map.contains_key("acpi-builtin");
let mut batt_info_error: bool = false;
let mut time_fmt_str = DEFAULT_FMT_STRING;
if let Some(s) = args_result.map.get("time-format") {
time_fmt_str = s;
}
println!(
"{}",
serde_json::to_string(&swaybar_object::SwaybarHeader::new())
.expect("Should be able to serialize SwaybarHeader")
);
println!("[");
let mut array = SwaybarArray::new();
let set_net_error = |is_empty: bool, array: &mut SwaybarArray, graph_max_opt: &Option<f64>| {
if is_empty {
if graph_max_opt.is_some() {
array.push_object(SwaybarObject::from_error_string(
"net_graph".to_owned(),
"net ERROR".into(),
));
}
let down_obj =
SwaybarObject::from_error_string("net_down".to_owned(), "Net ERROR".into());
array.push_object(down_obj);
let up_obj = SwaybarObject::from_error_string("net_up".to_owned(), "Net ERROR".into());
array.push_object(up_obj);
} else {
if graph_max_opt.is_some() {
if let Some(graph_ref) = array.get_by_name_mut("net_graph") {
graph_ref.update_as_error("Net ERROR".to_owned());
}
}
let down_ref_opt = array.get_by_name_mut("net_down");
if let Some(down_ref) = down_ref_opt {
down_ref.update_as_error("Net ERROR".to_owned());
}
let up_ref_opt = array.get_by_name_mut("net_up");
if let Some(up_ref) = up_ref_opt {
up_ref.update_as_error("Net ERROR".to_owned());
}
}
};
let handle_net = |is_empty: bool,
net: &mut proc::NetInfo,
array: &mut SwaybarArray,
graph_opt: &Option<f64>,
width: &Option<u16>|
-> Result<(), proc::Error> {
net.update()?;
let (netinfo_string, graph_string) = net.get_netstring(*graph_opt)?;
let netinfo_parts: Vec<&str> = netinfo_string.split_whitespace().collect();
if is_empty {
if graph_opt.is_some() {
let mut graph_obj =
SwaybarObject::from_string("net_graph".to_owned(), graph_string);
graph_obj.color = Some("#ffff88".into());
array.push_object(graph_obj);
}
let mut width_string: Option<String> = None;
if let Some(width) = *width {
let mut string = String::with_capacity(width.into());
for _ in 0..width {
string.push('0');
}
width_string = Some(string);
}
{
let mut down_object = SwaybarObject::from_string(
"net_down".to_owned(),
format!("{} {}", netinfo_parts[0], netinfo_parts[1]),
);
down_object.color = Some("#ff8888ff".into());
down_object.min_width = width_string.clone();
down_object.align = Some(String::from("right"));
array.push_object(down_object);
}
{
let mut up_object = SwaybarObject::from_string(
"net_up".to_owned(),
format!("{} {}", netinfo_parts[2], netinfo_parts[3]),
);
up_object.color = Some("#88ff88ff".into());
up_object.min_width = width_string;
up_object.align = Some(String::from("right"));
array.push_object(up_object);
}
} else {
if graph_opt.is_some() {
if let Some(graph_obj) = array.get_by_name_mut("net_graph") {
graph_obj.full_text = graph_string;
}
}
if let Some(down_object) = array.get_by_name_mut("net_down") {
down_object
.update_as_net_down(format!("{} {}", netinfo_parts[0], netinfo_parts[1]));
}
if let Some(up_object) = array.get_by_name_mut("net_up") {
up_object.update_as_net_up(format!("{} {}", netinfo_parts[2], netinfo_parts[3]));
}
}
Ok(())
};
loop {
let is_empty = array.is_empty();
// network traffic
if let Some(net) = net_obj.as_mut() {
if let Err(e) = handle_net(is_empty, net, &mut array, &net_graph_max, &net_width) {
let mut stderr_handle = io::stderr().lock();
stderr_handle.write_all(format!("{}\n", e).as_bytes()).ok();
net_obj = None;
set_net_error(is_empty, &mut array, &net_graph_max);
}
}
// meminfo
{
let meminfo_result = proc::get_meminfo();
let meminfo_string: String = if let Err(e) = meminfo_result {
let mut stderr_handle = io::stderr().lock();
stderr_handle.write_all(format!("{}\n", e).as_bytes()).ok();
String::from("MEMINFO ERROR")
} else {
meminfo_result.unwrap()
};
if is_empty {
let meminfo_obj = SwaybarObject::from_string("meminfo".to_owned(), meminfo_string);
array.push_object(meminfo_obj);
} else if let Some(meminfo_obj) = array.get_by_name_mut("meminfo") {
meminfo_obj.update_as_generic(meminfo_string, None);
}
}
// regex_cmds
{
for (idx, (cmd, args, regex)) in cmds.iter().enumerate() {
let cmd_result = external::get_cmd_output(cmd, args, regex);
if let Ok(cmd_struct) = cmd_result {
if is_empty {
let mut cmd_obj = SwaybarObject::from_string(
format!("regex_cmd_{}", idx),
cmd_struct.matched,
);
cmd_obj.color = cmd_struct.color;
array.push_object(cmd_obj);
} else if let Some(cmd_obj) =
array.get_by_name_mut(&format!("regex_cmd_{}", idx))
{
cmd_obj.update_as_generic(cmd_struct.matched, cmd_struct.color);
}
} else if let Err(e) = cmd_result {
let mut stderr_handle = io::stderr().lock();
stderr_handle.write_all(format!("{}\n", e).as_bytes()).ok();
if is_empty {
let cmd_obj = SwaybarObject::from_error_string(
format!("regex_cmd_{}", idx),
"REGEX_CMD ERROR".into(),
);
array.push_object(cmd_obj);
} else if let Some(cmd_obj) =
array.get_by_name_mut(&format!("regex_cmd_{}", idx))
{
cmd_obj.update_as_error("REGEX_CMD ERROR".into());
}
}
}
}
// batt_info
if batt_info_enabled {
if is_empty {
let mut new_object = SwaybarObject::new("battinfo".to_owned());
let result = batt_info.update(&mut new_object);
if result.is_ok() {
array.push_object(new_object);
} else {
new_object.update_as_error("BATTINFO ERROR".to_owned());
array.push_object(new_object);
batt_info_error = true;
let mut stderr_handle = io::stderr().lock();
stderr_handle
.write_all(format!("{}\n", result.unwrap_err()).as_bytes())
.ok();
}
} else if let Some(obj) = array.get_by_name_mut("battinfo") {
if !batt_info_error {
let result = batt_info.update(obj);
if let Err(e) = result {
obj.update_as_error("BATTINFO ERROR".to_owned());
batt_info_error = true;
let mut stderr_handle = io::stderr().lock();
stderr_handle.write_all(format!("{}\n", e).as_bytes()).ok();
}
}
}
}
// loadavg
{
let loadavg_result = proc::get_loadavg();
let loadavg_string: String = if let Err(e) = loadavg_result {
let mut stderr_handle = io::stderr().lock();
stderr_handle.write_all(format!("{}\n", e).as_bytes()).ok();
String::from("LOADAVG ERROR")
} else {
loadavg_result.unwrap()
};
if is_empty {
let loadavg_obj = SwaybarObject::from_string("loadavg".to_owned(), loadavg_string);
array.push_object(loadavg_obj);
} else if let Some(loadavg_obj) = array.get_by_name_mut("loadavg") {
loadavg_obj.update_as_generic(loadavg_string, None);
}
}
// time
if is_empty {
let mut time_obj = SwaybarObject::new("current_time".to_owned());
time_obj.update_as_date(time_fmt_str);
array.push_object(time_obj);
} else if let Some(time_obj) = array.get_by_name_mut("current_time") {
time_obj.update_as_date(time_fmt_str);
}
println!("{}", array);
std::thread::sleep(interval);
}
}