Impl "--regex-cmd=<cmd>,<args...>,<regex>"
The "--regex-cmd=..." allows executing processes and fetching regex'd strings from its stdout to use on swaybar.
This commit is contained in:
parent
90dee55e7a
commit
9305a968a8
6 changed files with 188 additions and 12 deletions
33
Cargo.lock
generated
33
Cargo.lock
generated
|
@ -2,6 +2,15 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -33,6 +42,12 @@ version = "0.2.126"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
|
@ -70,6 +85,23 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.10"
|
||||
|
@ -112,6 +144,7 @@ name = "swaybar_info"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
|
|
@ -9,3 +9,4 @@ edition = "2021"
|
|||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
chrono = "0.4"
|
||||
regex = "1.5"
|
||||
|
|
|
@ -20,10 +20,15 @@ Put the following in your `~/.config/sway/config`:
|
|||
# want to monitor. You can omit --netdev=<device>, but that will also
|
||||
# cause the program to omit network traffic stats.
|
||||
status_command $HOME/.config/sway/swaybar_info --netdev=enp7s0
|
||||
|
||||
# One can use the "--regex-cmd=<cmd>,<args...>,<regex>" option like so:
|
||||
status_command $HOME/.config/sway/swaybar_info --regex-cmd="acpi,-b,[0-9]+%.*"
|
||||
# This example gets battery info into the bar.
|
||||
}
|
||||
|
||||
## Dependencies
|
||||
|
||||
Uses [`serde_json`](https://crates.io/crates/serde_json),
|
||||
[`serde`](https://crates.io/crates/serde),
|
||||
and [`chrono`](https://crates.io/crates/chrono).
|
||||
[`chrono`](https://crates.io/crates/chrono),
|
||||
and [`regex`](https://crates.io/crates/regex).
|
||||
|
|
24
src/args.rs
24
src/args.rs
|
@ -2,8 +2,14 @@ use std::collections::HashMap;
|
|||
use std::io;
|
||||
use std::io::Write;
|
||||
|
||||
pub fn get_args() -> HashMap<String, String> {
|
||||
pub struct ArgsResult {
|
||||
pub map: HashMap<String, String>,
|
||||
pub regex_cmds: Vec<String>,
|
||||
}
|
||||
|
||||
pub fn get_args() -> ArgsResult {
|
||||
let mut map = HashMap::new();
|
||||
let mut regex_cmds = Vec::new();
|
||||
|
||||
let mut first = true;
|
||||
for arg in std::env::args() {
|
||||
|
@ -16,6 +22,9 @@ pub fn get_args() -> HashMap<String, String> {
|
|||
} else if arg.starts_with("--interval-sec=") {
|
||||
let (_, back) = arg.split_at(15);
|
||||
map.insert("interval-sec".into(), back.into());
|
||||
} else if arg.starts_with("--regex-cmd=") {
|
||||
let (_, back) = arg.split_at(12);
|
||||
regex_cmds.push(back.to_owned());
|
||||
} else if arg == "--help" || arg == "-h" {
|
||||
map.insert("help".into(), "".into());
|
||||
} else {
|
||||
|
@ -26,19 +35,24 @@ pub fn get_args() -> HashMap<String, String> {
|
|||
}
|
||||
}
|
||||
|
||||
map
|
||||
ArgsResult { map, regex_cmds }
|
||||
}
|
||||
|
||||
pub fn print_usage() {
|
||||
let mut stderr_handle = io::stderr().lock();
|
||||
stderr_handle.write_all(b"Usage:\n").ok();
|
||||
stderr_handle
|
||||
.write_all(b" -h | --help\t\t\tPrints help\n")
|
||||
.write_all(b" -h | --help\t\t\t\tPrints help\n")
|
||||
.ok();
|
||||
stderr_handle
|
||||
.write_all(b" --netdev=<device_name>\tCheck network traffic on specified device\n")
|
||||
.write_all(b" --netdev=<device_name>\t\tCheck network traffic on specified device\n")
|
||||
.ok();
|
||||
stderr_handle
|
||||
.write_all(b" --interval-sec=<seconds>\tOutput at intervals of <seconds> (default 5)\n")
|
||||
.write_all(b" --interval-sec=<seconds>\t\tOutput at intervals of <seconds> (default 5)\n")
|
||||
.ok();
|
||||
stderr_handle
|
||||
.write_all(
|
||||
b" --regex-cmd=<cmd>,<args...>,<regex>\tUse an output of a command as a metric\n",
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
|
|
64
src/external.rs
Normal file
64
src/external.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use regex::Regex;
|
||||
use std::io;
|
||||
use std::process::Command;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
IO(io::Error),
|
||||
FromUTF8(std::string::FromUtf8Error),
|
||||
Generic(String),
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(io_error: io::Error) -> Self {
|
||||
Self::IO(io_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::string::FromUtf8Error> for Error {
|
||||
fn from(utf8_error: std::string::FromUtf8Error) -> Self {
|
||||
Self::FromUTF8(utf8_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Error {
|
||||
fn from(string: String) -> Self {
|
||||
Self::Generic(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::IO(e) => e.fmt(f),
|
||||
Error::FromUTF8(e) => e.fmt(f),
|
||||
Error::Generic(s) => f.write_str(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Error::IO(e) => e.source(),
|
||||
Error::FromUTF8(e) => e.source(),
|
||||
Error::Generic(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cmd_output(cmd: &str, args: &[&str], regex: &Regex) -> Result<String, Error> {
|
||||
let mut cmd_builder = Command::new(cmd);
|
||||
for arg in args {
|
||||
cmd_builder.arg(arg);
|
||||
}
|
||||
let output = cmd_builder.output()?;
|
||||
let stdout_output: String = String::from_utf8(output.stdout)?;
|
||||
let regex_captures = regex
|
||||
.captures(&stdout_output)
|
||||
.ok_or_else(|| Error::from("Regex returned empty matches".to_owned()))?;
|
||||
let regex_match = regex_captures
|
||||
.get(0)
|
||||
.ok_or_else(|| Error::from("Failed to get regex match".to_owned()))?;
|
||||
Ok(regex_match.as_str().to_owned())
|
||||
}
|
71
src/main.rs
71
src/main.rs
|
@ -1,4 +1,5 @@
|
|||
mod args;
|
||||
mod external;
|
||||
mod proc;
|
||||
mod swaybar_object;
|
||||
|
||||
|
@ -7,21 +8,47 @@ use std::time::Duration;
|
|||
use swaybar_object::*;
|
||||
|
||||
fn main() {
|
||||
let args_map = args::get_args();
|
||||
if args_map.contains_key("help") {
|
||||
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(',');
|
||||
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 interval: Duration = Duration::from_secs(5);
|
||||
if args_map.contains_key("netdev") {
|
||||
if args_result.map.contains_key("netdev") {
|
||||
net_obj = Some(proc::NetInfo::new(
|
||||
args_map.get("netdev").unwrap().to_owned(),
|
||||
args_result.map.get("netdev").unwrap().to_owned(),
|
||||
));
|
||||
}
|
||||
if args_map.contains_key("interval-sec") {
|
||||
let seconds: Result<i64, _> = args_map.get("interval-sec").unwrap().parse();
|
||||
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);
|
||||
|
@ -145,6 +172,38 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
// 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_string) = cmd_result {
|
||||
if is_empty {
|
||||
let cmd_obj =
|
||||
SwaybarObject::from_string(format!("regex_cmd_{}", idx), cmd_string);
|
||||
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_string, None);
|
||||
}
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loadavg
|
||||
{
|
||||
let loadavg_result = proc::get_loadavg();
|
||||
|
|
Loading…
Reference in a new issue