mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-08-16 22:31:03 +02:00
feat(sysinfo): expose values as ironvars
This commit is contained in:
parent
9b67719cac
commit
b83f26cb1a
13 changed files with 410 additions and 152 deletions
|
@ -6,4 +6,19 @@ Any UTF-8 string is a valid value.
|
||||||
|
|
||||||
Reference values using `#my_variable`. These update as soon as the value changes.
|
Reference values using `#my_variable`. These update as soon as the value changes.
|
||||||
|
|
||||||
You can set defaults using the `ironvar_defaults` key in your top-level config.
|
You can set defaults using the `ironvar_defaults` key in your top-level config.
|
||||||
|
|
||||||
|
Some modules (such as `sys_info`) expose their values over the Ironvar interface,
|
||||||
|
allowing you to build custom interfaces and integrate into scripts.
|
||||||
|
These present their values inside read-only namespaces.
|
||||||
|
|
||||||
|
Some examples below:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
ironbar var list
|
||||||
|
ironbar var list sysinfo
|
||||||
|
ironbar var list sysinfo.disk_percent
|
||||||
|
ironbar var get sysinfo.disk_percent./home
|
||||||
|
ironbar var get sysinfo.disk_percent.mean
|
||||||
|
ironbar var get sysinfo.memory_percent
|
||||||
|
```
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::await_sync;
|
use crate::{await_sync, Ironbar};
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -192,7 +192,11 @@ impl Clients {
|
||||||
#[cfg(feature = "sys_info")]
|
#[cfg(feature = "sys_info")]
|
||||||
pub fn sys_info(&mut self) -> Arc<sysinfo::Client> {
|
pub fn sys_info(&mut self) -> Arc<sysinfo::Client> {
|
||||||
self.sys_info
|
self.sys_info
|
||||||
.get_or_insert_with(|| Arc::new(sysinfo::Client::new()))
|
.get_or_insert_with(|| {
|
||||||
|
let client = Arc::new(sysinfo::Client::new());
|
||||||
|
Ironbar::variable_manager().register_namespace("sysinfo", client.clone());
|
||||||
|
client
|
||||||
|
})
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
use crate::ironvar::Namespace;
|
||||||
use crate::modules::sysinfo::Interval;
|
use crate::modules::sysinfo::Interval;
|
||||||
use crate::{lock, register_client};
|
use crate::{lock, register_client};
|
||||||
|
use color_eyre::{Report, Result};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::Mutex;
|
use std::str::FromStr;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use sysinfo::{Components, Disks, LoadAvg, Networks, RefreshKind, System};
|
use sysinfo::{Components, Disks, LoadAvg, Networks, RefreshKind, System};
|
||||||
|
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
|
@ -43,6 +46,21 @@ pub enum Function {
|
||||||
Name(String),
|
Name(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for Function {
|
||||||
|
type Err = ();
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"sum" => Ok(Self::Sum),
|
||||||
|
"min" => Ok(Self::Min),
|
||||||
|
"max" => Ok(Self::Max),
|
||||||
|
"mean" => Ok(Self::Mean),
|
||||||
|
"" => Err(()),
|
||||||
|
_ => Ok(Self::Name(s.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ValueSet {
|
pub struct ValueSet {
|
||||||
values: HashMap<Box<str>, Value>,
|
values: HashMap<Box<str>, Value>,
|
||||||
|
@ -388,3 +406,212 @@ register_client!(Client, sys_info);
|
||||||
const fn c_to_f(c: f64) -> f64 {
|
const fn c_to_f(c: f64) -> f64 {
|
||||||
c / 5.0 * 9.0 + 32.0
|
c / 5.0 * 9.0 + 32.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum TokenType {
|
||||||
|
CpuFrequency,
|
||||||
|
CpuPercent,
|
||||||
|
|
||||||
|
MemoryFree,
|
||||||
|
MemoryAvailable,
|
||||||
|
MemoryTotal,
|
||||||
|
MemoryUsed,
|
||||||
|
MemoryPercent,
|
||||||
|
|
||||||
|
SwapFree,
|
||||||
|
SwapTotal,
|
||||||
|
SwapUsed,
|
||||||
|
SwapPercent,
|
||||||
|
|
||||||
|
TempC,
|
||||||
|
TempF,
|
||||||
|
|
||||||
|
DiskFree,
|
||||||
|
DiskTotal,
|
||||||
|
DiskUsed,
|
||||||
|
DiskPercent,
|
||||||
|
DiskRead,
|
||||||
|
DiskWrite,
|
||||||
|
|
||||||
|
NetDown,
|
||||||
|
NetUp,
|
||||||
|
|
||||||
|
LoadAverage1,
|
||||||
|
LoadAverage5,
|
||||||
|
LoadAverage15,
|
||||||
|
Uptime,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for TokenType {
|
||||||
|
type Err = Report;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
match s {
|
||||||
|
"cpu_frequency" => Ok(Self::CpuFrequency),
|
||||||
|
"cpu_percent" => Ok(Self::CpuPercent),
|
||||||
|
|
||||||
|
"memory_free" => Ok(Self::MemoryFree),
|
||||||
|
"memory_available" => Ok(Self::MemoryAvailable),
|
||||||
|
"memory_total" => Ok(Self::MemoryTotal),
|
||||||
|
"memory_used" => Ok(Self::MemoryUsed),
|
||||||
|
"memory_percent" => Ok(Self::MemoryPercent),
|
||||||
|
|
||||||
|
"swap_free" => Ok(Self::SwapFree),
|
||||||
|
"swap_total" => Ok(Self::SwapTotal),
|
||||||
|
"swap_used" => Ok(Self::SwapUsed),
|
||||||
|
"swap_percent" => Ok(Self::SwapPercent),
|
||||||
|
|
||||||
|
"temp_c" => Ok(Self::TempC),
|
||||||
|
"temp_f" => Ok(Self::TempF),
|
||||||
|
|
||||||
|
"disk_free" => Ok(Self::DiskFree),
|
||||||
|
"disk_total" => Ok(Self::DiskTotal),
|
||||||
|
"disk_used" => Ok(Self::DiskUsed),
|
||||||
|
"disk_percent" => Ok(Self::DiskPercent),
|
||||||
|
"disk_read" => Ok(Self::DiskRead),
|
||||||
|
"disk_write" => Ok(Self::DiskWrite),
|
||||||
|
|
||||||
|
"net_down" => Ok(Self::NetDown),
|
||||||
|
"net_up" => Ok(Self::NetUp),
|
||||||
|
|
||||||
|
"load_average_1" => Ok(Self::LoadAverage1),
|
||||||
|
"load_average_5" => Ok(Self::LoadAverage5),
|
||||||
|
"load_average_15" => Ok(Self::LoadAverage15),
|
||||||
|
"uptime" => Ok(Self::Uptime),
|
||||||
|
_ => Err(Report::msg(format!("invalid token type: '{s}'"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Namespace for Client {
|
||||||
|
fn get(&self, key: &str) -> Option<String> {
|
||||||
|
let get = |value: Value| Some(value.get(Prefix::None).to_string());
|
||||||
|
|
||||||
|
let token = TokenType::from_str(key).ok()?;
|
||||||
|
match token {
|
||||||
|
TokenType::CpuFrequency => None,
|
||||||
|
TokenType::CpuPercent => None,
|
||||||
|
TokenType::MemoryFree => get(self.memory_free()),
|
||||||
|
TokenType::MemoryAvailable => get(self.memory_available()),
|
||||||
|
TokenType::MemoryTotal => get(self.memory_total()),
|
||||||
|
TokenType::MemoryUsed => get(self.memory_used()),
|
||||||
|
TokenType::MemoryPercent => get(self.memory_percent()),
|
||||||
|
TokenType::SwapFree => get(self.swap_free()),
|
||||||
|
TokenType::SwapTotal => get(self.swap_total()),
|
||||||
|
TokenType::SwapUsed => get(self.swap_used()),
|
||||||
|
TokenType::SwapPercent => get(self.swap_percent()),
|
||||||
|
TokenType::TempC => None,
|
||||||
|
TokenType::TempF => None,
|
||||||
|
TokenType::DiskFree => None,
|
||||||
|
TokenType::DiskTotal => None,
|
||||||
|
TokenType::DiskUsed => None,
|
||||||
|
TokenType::DiskPercent => None,
|
||||||
|
TokenType::DiskRead => None,
|
||||||
|
TokenType::DiskWrite => None,
|
||||||
|
TokenType::NetDown => None,
|
||||||
|
TokenType::NetUp => None,
|
||||||
|
TokenType::LoadAverage1 => get(self.load_average_1()),
|
||||||
|
TokenType::LoadAverage5 => get(self.load_average_5()),
|
||||||
|
TokenType::LoadAverage15 => get(self.load_average_15()),
|
||||||
|
TokenType::Uptime => Some(self.uptime()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list(&self) -> Vec<String> {
|
||||||
|
vec![
|
||||||
|
"memory_free",
|
||||||
|
"memory_available",
|
||||||
|
"memory_total",
|
||||||
|
"memory_used",
|
||||||
|
"memory_percent",
|
||||||
|
"swap_free",
|
||||||
|
"swap_total",
|
||||||
|
"swap_used",
|
||||||
|
"swap_percent",
|
||||||
|
"load_average_1",
|
||||||
|
"load_average_5",
|
||||||
|
"load_average_15",
|
||||||
|
"uptime",
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn namespaces(&self) -> Vec<String> {
|
||||||
|
vec![
|
||||||
|
"cpu_frequency",
|
||||||
|
"cpu_percent",
|
||||||
|
"temp_c",
|
||||||
|
"temp_f",
|
||||||
|
"disk_free",
|
||||||
|
"disk_total",
|
||||||
|
"disk_used",
|
||||||
|
"disk_percent",
|
||||||
|
"disk_read",
|
||||||
|
"disk_write",
|
||||||
|
"net_down",
|
||||||
|
"net_up",
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_namespace(&self, key: &str) -> Option<Arc<dyn Namespace + Sync + Send>> {
|
||||||
|
let token = TokenType::from_str(key).ok()?;
|
||||||
|
|
||||||
|
match token {
|
||||||
|
TokenType::CpuFrequency => Some(Arc::new(self.cpu_frequency())),
|
||||||
|
TokenType::CpuPercent => Some(Arc::new(self.cpu_percent())),
|
||||||
|
TokenType::MemoryFree => None,
|
||||||
|
TokenType::MemoryAvailable => None,
|
||||||
|
TokenType::MemoryTotal => None,
|
||||||
|
TokenType::MemoryUsed => None,
|
||||||
|
TokenType::MemoryPercent => None,
|
||||||
|
TokenType::SwapFree => None,
|
||||||
|
TokenType::SwapTotal => None,
|
||||||
|
TokenType::SwapUsed => None,
|
||||||
|
TokenType::SwapPercent => None,
|
||||||
|
TokenType::TempC => Some(Arc::new(self.temp_c())),
|
||||||
|
TokenType::TempF => Some(Arc::new(self.temp_f())),
|
||||||
|
TokenType::DiskFree => Some(Arc::new(self.disk_free())),
|
||||||
|
TokenType::DiskTotal => Some(Arc::new(self.disk_total())),
|
||||||
|
TokenType::DiskUsed => Some(Arc::new(self.disk_used())),
|
||||||
|
TokenType::DiskPercent => Some(Arc::new(self.disk_percent())),
|
||||||
|
TokenType::DiskRead => Some(Arc::new(self.disk_read(Interval::All(1)))),
|
||||||
|
TokenType::DiskWrite => Some(Arc::new(self.disk_write(Interval::All(1)))),
|
||||||
|
TokenType::NetDown => Some(Arc::new(self.net_down(Interval::All(1)))),
|
||||||
|
TokenType::NetUp => Some(Arc::new(self.net_up(Interval::All(1)))),
|
||||||
|
TokenType::LoadAverage1 => None,
|
||||||
|
TokenType::LoadAverage5 => None,
|
||||||
|
TokenType::LoadAverage15 => None,
|
||||||
|
TokenType::Uptime => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Namespace for ValueSet {
|
||||||
|
fn get(&self, key: &str) -> Option<String> {
|
||||||
|
let function = Function::from_str(key).ok()?;
|
||||||
|
Some(self.apply(&function, Prefix::None).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list(&self) -> Vec<String> {
|
||||||
|
let mut vec = vec!["sum", "min", "max", "mean"]
|
||||||
|
.into_iter()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
vec.extend(self.values.keys().map(ToString::to_string));
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
|
||||||
|
fn namespaces(&self) -> Vec<String> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_namespace(&self, _key: &str) -> Option<Arc<dyn Namespace + Sync + Send>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ impl DynamicBool {
|
||||||
let variable_manager = Ironbar::variable_manager();
|
let variable_manager = Ironbar::variable_manager();
|
||||||
|
|
||||||
let variable_name = variable[1..].into(); // remove hash
|
let variable_name = variable[1..].into(); // remove hash
|
||||||
let mut rx = crate::write_lock!(variable_manager).subscribe(variable_name);
|
let mut rx = variable_manager.subscribe(variable_name);
|
||||||
|
|
||||||
while let Ok(value) = rx.recv().await {
|
while let Ok(value) = rx.recv().await {
|
||||||
let has_value = value.is_some_and(|s| is_truthy(&s));
|
let has_value = value.is_some_and(|s| is_truthy(&s));
|
||||||
|
|
|
@ -71,7 +71,7 @@ where
|
||||||
|
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
let variable_manager = Ironbar::variable_manager();
|
let variable_manager = Ironbar::variable_manager();
|
||||||
let mut rx = crate::write_lock!(variable_manager).subscribe(name);
|
let mut rx = variable_manager.subscribe(name);
|
||||||
|
|
||||||
while let Ok(value) = rx.recv().await {
|
while let Ok(value) = rx.recv().await {
|
||||||
if let Some(value) = value {
|
if let Some(value) = value {
|
||||||
|
|
|
@ -52,7 +52,7 @@ pub enum IronvarCommand {
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Gets the current value of all `ironvar`s.
|
/// Gets the current value of all `ironvar`s.
|
||||||
List,
|
List { namespace: Option<Box<str>> },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args, Debug, Serialize, Deserialize)]
|
#[derive(Args, Debug, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,36 +1,73 @@
|
||||||
use crate::ipc::commands::IronvarCommand;
|
use crate::ipc::commands::IronvarCommand;
|
||||||
use crate::ipc::Response;
|
use crate::ipc::Response;
|
||||||
use crate::{read_lock, write_lock, Ironbar};
|
use crate::ironvar::{Namespace, WritableNamespace};
|
||||||
|
use crate::Ironbar;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub fn handle_command(command: IronvarCommand) -> Response {
|
pub fn handle_command(command: IronvarCommand) -> Response {
|
||||||
match command {
|
match command {
|
||||||
IronvarCommand::Set { key, value } => {
|
IronvarCommand::Set { key, value } => {
|
||||||
let variable_manager = Ironbar::variable_manager();
|
let variable_manager = Ironbar::variable_manager();
|
||||||
let mut variable_manager = write_lock!(variable_manager);
|
match variable_manager.set(&key, value) {
|
||||||
match variable_manager.set(key, value) {
|
|
||||||
Ok(()) => Response::Ok,
|
Ok(()) => Response::Ok,
|
||||||
Err(err) => Response::error(&format!("{err}")),
|
Err(err) => Response::error(&format!("{err}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IronvarCommand::Get { key } => {
|
IronvarCommand::Get { mut key } => {
|
||||||
let variable_manager = Ironbar::variable_manager();
|
let variable_manager = Ironbar::variable_manager();
|
||||||
let value = read_lock!(variable_manager).get(&key);
|
let mut ns: Arc<dyn Namespace + Sync + Send> = variable_manager;
|
||||||
|
|
||||||
|
if key.contains('.') {
|
||||||
|
for part in key.split('.') {
|
||||||
|
ns = match ns.get_namespace(part) {
|
||||||
|
Some(ns) => ns.clone(),
|
||||||
|
None => {
|
||||||
|
key = part.into();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = ns.get(&key);
|
||||||
match value {
|
match value {
|
||||||
Some(value) => Response::OkValue { value },
|
Some(value) => Response::OkValue { value },
|
||||||
None => Response::error("Variable not found"),
|
None => Response::error("Variable not found"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IronvarCommand::List => {
|
IronvarCommand::List { namespace } => {
|
||||||
let variable_manager = Ironbar::variable_manager();
|
let variable_manager = Ironbar::variable_manager();
|
||||||
|
let mut ns: Arc<dyn Namespace + Sync + Send> = variable_manager;
|
||||||
|
|
||||||
let mut values = read_lock!(variable_manager)
|
if let Some(namespace) = namespace {
|
||||||
|
for part in namespace.split('.') {
|
||||||
|
ns = match ns.get_namespace(part) {
|
||||||
|
Some(ns) => ns.clone(),
|
||||||
|
None => return Response::error("Namespace not found"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut namespaces = ns
|
||||||
|
.namespaces()
|
||||||
|
.iter()
|
||||||
|
.map(|ns| format!("<{ns}>"))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
namespaces.sort();
|
||||||
|
|
||||||
|
let mut value = namespaces.join("\n");
|
||||||
|
|
||||||
|
let mut values = ns
|
||||||
.get_all()
|
.get_all()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| format!("{k}: {}", v.get().unwrap_or_default()))
|
.map(|(k, v)| format!("{k}: {v}"))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
values.sort();
|
values.sort();
|
||||||
let value = values.join("\n");
|
|
||||||
|
value.push('\n');
|
||||||
|
value.push_str(&values.join("\n"));
|
||||||
|
|
||||||
Response::OkValue { value }
|
Response::OkValue { value }
|
||||||
}
|
}
|
||||||
|
|
131
src/ironvar.rs
131
src/ironvar.rs
|
@ -1,13 +1,36 @@
|
||||||
#![doc = include_str!("../docs/Ironvars.md")]
|
#![doc = include_str!("../docs/Ironvars.md")]
|
||||||
|
|
||||||
use crate::send;
|
use crate::{arc_rw, read_lock, send, write_lock};
|
||||||
use color_eyre::{Report, Result};
|
use color_eyre::{Report, Result};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
|
type NamespaceTrait = Arc<dyn Namespace + Sync + Send>;
|
||||||
|
|
||||||
|
pub trait Namespace {
|
||||||
|
fn get(&self, key: &str) -> Option<String>;
|
||||||
|
fn list(&self) -> Vec<String>;
|
||||||
|
|
||||||
|
fn get_all(&self) -> HashMap<Box<str>, String> {
|
||||||
|
self.list()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|name| self.get(&name).map(|value| (name.into(), value)))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn namespaces(&self) -> Vec<String>;
|
||||||
|
fn get_namespace(&self, key: &str) -> Option<NamespaceTrait>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WritableNamespace: Namespace {
|
||||||
|
fn set(&self, key: &str, value: String) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Global singleton manager for `IronVar` variables.
|
/// Global singleton manager for `IronVar` variables.
|
||||||
pub struct VariableManager {
|
pub struct VariableManager {
|
||||||
variables: HashMap<Box<str>, IronVar>,
|
variables: Arc<RwLock<HashMap<Box<str>, IronVar>>>,
|
||||||
|
namespaces: Arc<RwLock<HashMap<Box<str>, NamespaceTrait>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for VariableManager {
|
impl Default for VariableManager {
|
||||||
|
@ -19,41 +42,15 @@ impl Default for VariableManager {
|
||||||
impl VariableManager {
|
impl VariableManager {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
variables: HashMap::new(),
|
variables: arc_rw!(HashMap::new()),
|
||||||
|
namespaces: arc_rw!(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the value for a variable,
|
|
||||||
/// creating it if it does not exist.
|
|
||||||
pub fn set(&mut self, key: Box<str>, value: String) -> Result<()> {
|
|
||||||
if Self::key_is_valid(&key) {
|
|
||||||
if let Some(var) = self.variables.get_mut(&key) {
|
|
||||||
var.set(Some(value));
|
|
||||||
} else {
|
|
||||||
let var = IronVar::new(Some(value));
|
|
||||||
self.variables.insert(key, var);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(Report::msg("Invalid key"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the current value of an `ironvar`.
|
|
||||||
/// Prefer to use `subscribe` where possible.
|
|
||||||
pub fn get(&self, key: &str) -> Option<String> {
|
|
||||||
self.variables.get(key).and_then(IronVar::get)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_all(&self) -> &HashMap<Box<str>, IronVar> {
|
|
||||||
&self.variables
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Subscribes to an `ironvar`, creating it if it does not exist.
|
/// Subscribes to an `ironvar`, creating it if it does not exist.
|
||||||
/// Any time the var is set, its value is sent on the channel.
|
/// Any time the var is set, its value is sent on the channel.
|
||||||
pub fn subscribe(&mut self, key: Box<str>) -> broadcast::Receiver<Option<String>> {
|
pub fn subscribe(&self, key: Box<str>) -> broadcast::Receiver<Option<String>> {
|
||||||
self.variables
|
write_lock!(self.variables)
|
||||||
.entry(key)
|
.entry(key)
|
||||||
.or_insert_with(|| IronVar::new(None))
|
.or_insert_with(|| IronVar::new(None))
|
||||||
.subscribe()
|
.subscribe()
|
||||||
|
@ -65,6 +62,76 @@ impl VariableManager {
|
||||||
.chars()
|
.chars()
|
||||||
.all(|char| char.is_alphanumeric() || char == '_' || char == '-')
|
.all(|char| char.is_alphanumeric() || char == '_' || char == '-')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn register_namespace<N>(&self, name: &str, namespace: Arc<N>)
|
||||||
|
where
|
||||||
|
N: Namespace + Sync + Send + 'static,
|
||||||
|
{
|
||||||
|
write_lock!(self.namespaces).insert(name.into(), namespace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Namespace for VariableManager {
|
||||||
|
fn get(&self, key: &str) -> Option<String> {
|
||||||
|
if key.contains('.') {
|
||||||
|
let Some((ns, key)) = key.split_once('.') else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let namespaces = read_lock!(self.namespaces);
|
||||||
|
let Some(ns) = namespaces.get(ns) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.get(key).map(|v| v.to_owned())
|
||||||
|
} else {
|
||||||
|
read_lock!(self.variables).get(key).and_then(IronVar::get)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn list(&self) -> Vec<String> {
|
||||||
|
read_lock!(self.variables)
|
||||||
|
.keys()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all(&self) -> HashMap<Box<str>, String> {
|
||||||
|
read_lock!(self.variables)
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(k, v)| v.get().map(|value| (k.clone(), value)))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn namespaces(&self) -> Vec<String> {
|
||||||
|
read_lock!(self.namespaces)
|
||||||
|
.keys()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_namespace(&self, key: &str) -> Option<NamespaceTrait> {
|
||||||
|
read_lock!(self.namespaces).get(key).cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WritableNamespace for VariableManager {
|
||||||
|
/// Sets the value for a variable,
|
||||||
|
/// creating it if it does not exist.
|
||||||
|
fn set(&self, key: &str, value: String) -> Result<()> {
|
||||||
|
if Self::key_is_valid(key) {
|
||||||
|
if let Some(var) = write_lock!(self.variables).get_mut(&Box::from(key)) {
|
||||||
|
var.set(Some(value));
|
||||||
|
} else {
|
||||||
|
let var = IronVar::new(Some(value));
|
||||||
|
write_lock!(self.variables).insert(key.into(), var);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Report::msg("Invalid key"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ironbar dynamic variable representation.
|
/// Ironbar dynamic variable representation.
|
||||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -7,8 +7,6 @@ use std::path::PathBuf;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||||
#[cfg(feature = "ipc")]
|
|
||||||
use std::sync::RwLock;
|
|
||||||
use std::sync::{mpsc, Arc, Mutex, OnceLock};
|
use std::sync::{mpsc, Arc, Mutex, OnceLock};
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
|
@ -32,7 +30,7 @@ use crate::clients::Clients;
|
||||||
use crate::config::{Config, MonitorConfig};
|
use crate::config::{Config, MonitorConfig};
|
||||||
use crate::error::ExitCode;
|
use crate::error::ExitCode;
|
||||||
#[cfg(feature = "ipc")]
|
#[cfg(feature = "ipc")]
|
||||||
use crate::ironvar::VariableManager;
|
use crate::ironvar::{VariableManager, WritableNamespace};
|
||||||
use crate::style::load_css;
|
use crate::style::load_css;
|
||||||
|
|
||||||
mod bar;
|
mod bar;
|
||||||
|
@ -263,10 +261,10 @@ impl Ironbar {
|
||||||
/// Gets the `Ironvar` manager singleton.
|
/// Gets the `Ironvar` manager singleton.
|
||||||
#[cfg(feature = "ipc")]
|
#[cfg(feature = "ipc")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn variable_manager() -> Arc<RwLock<VariableManager>> {
|
pub fn variable_manager() -> Arc<VariableManager> {
|
||||||
static VARIABLE_MANAGER: OnceLock<Arc<RwLock<VariableManager>>> = OnceLock::new();
|
static VARIABLE_MANAGER: OnceLock<Arc<VariableManager>> = OnceLock::new();
|
||||||
VARIABLE_MANAGER
|
VARIABLE_MANAGER
|
||||||
.get_or_init(|| arc_rw!(VariableManager::new()))
|
.get_or_init(|| Arc::new(VariableManager::new()))
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,7 +334,7 @@ fn load_config() -> (Config, PathBuf) {
|
||||||
if let Some(ironvars) = config.ironvar_defaults.take() {
|
if let Some(ironvars) = config.ironvar_defaults.take() {
|
||||||
let variable_manager = Ironbar::variable_manager();
|
let variable_manager = Ironbar::variable_manager();
|
||||||
for (k, v) in ironvars {
|
for (k, v) in ironvars {
|
||||||
if write_lock!(variable_manager).set(k.clone(), v).is_err() {
|
if variable_manager.set(&k, v).is_err() {
|
||||||
warn!("Ignoring invalid ironvar: '{k}'");
|
warn!("Ignoring invalid ironvar: '{k}'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@ mod parser;
|
||||||
mod renderer;
|
mod renderer;
|
||||||
mod token;
|
mod token;
|
||||||
|
|
||||||
|
use crate::clients::sysinfo::TokenType;
|
||||||
use crate::config::{CommonConfig, ModuleOrientation};
|
use crate::config::{CommonConfig, ModuleOrientation};
|
||||||
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
|
use crate::gtk_helpers::{IronbarGtkExt, IronbarLabelExt};
|
||||||
use crate::modules::sysinfo::token::{Part, TokenType};
|
use crate::modules::sysinfo::token::Part;
|
||||||
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext};
|
||||||
use crate::{clients, glib_recv, module_impl, send_async, spawn, try_send};
|
use crate::{clients, glib_recv, module_impl, send_async, spawn, try_send};
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
|
|
|
@ -1,65 +1,9 @@
|
||||||
use crate::clients::sysinfo::{Function, Prefix};
|
use crate::clients::sysinfo::{Function, Prefix, TokenType};
|
||||||
use crate::modules::sysinfo::token::{Alignment, Formatting, Part, Token, TokenType};
|
use crate::modules::sysinfo::token::{Alignment, Formatting, Part, Token};
|
||||||
use color_eyre::{Report, Result};
|
use color_eyre::{Report, Result};
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
use std::str::{Chars, FromStr};
|
use std::str::{Chars, FromStr};
|
||||||
|
|
||||||
impl FromStr for TokenType {
|
|
||||||
type Err = Report;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
|
||||||
match s {
|
|
||||||
"cpu_frequency" => Ok(Self::CpuFrequency),
|
|
||||||
"cpu_percent" => Ok(Self::CpuPercent),
|
|
||||||
|
|
||||||
"memory_free" => Ok(Self::MemoryFree),
|
|
||||||
"memory_available" => Ok(Self::MemoryAvailable),
|
|
||||||
"memory_total" => Ok(Self::MemoryTotal),
|
|
||||||
"memory_used" => Ok(Self::MemoryUsed),
|
|
||||||
"memory_percent" => Ok(Self::MemoryPercent),
|
|
||||||
|
|
||||||
"swap_free" => Ok(Self::SwapFree),
|
|
||||||
"swap_total" => Ok(Self::SwapTotal),
|
|
||||||
"swap_used" => Ok(Self::SwapUsed),
|
|
||||||
"swap_percent" => Ok(Self::SwapPercent),
|
|
||||||
|
|
||||||
"temp_c" => Ok(Self::TempC),
|
|
||||||
"temp_f" => Ok(Self::TempF),
|
|
||||||
|
|
||||||
"disk_free" => Ok(Self::DiskFree),
|
|
||||||
"disk_total" => Ok(Self::DiskTotal),
|
|
||||||
"disk_used" => Ok(Self::DiskUsed),
|
|
||||||
"disk_percent" => Ok(Self::DiskPercent),
|
|
||||||
"disk_read" => Ok(Self::DiskRead),
|
|
||||||
"disk_write" => Ok(Self::DiskWrite),
|
|
||||||
|
|
||||||
"net_down" => Ok(Self::NetDown),
|
|
||||||
"net_up" => Ok(Self::NetUp),
|
|
||||||
|
|
||||||
"load_average_1" => Ok(Self::LoadAverage1),
|
|
||||||
"load_average_5" => Ok(Self::LoadAverage5),
|
|
||||||
"load_average_15" => Ok(Self::LoadAverage15),
|
|
||||||
"uptime" => Ok(Self::Uptime),
|
|
||||||
_ => Err(Report::msg(format!("invalid token type: '{s}'"))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Function {
|
|
||||||
type Err = ();
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
match s.to_lowercase().as_str() {
|
|
||||||
"sum" => Ok(Self::Sum),
|
|
||||||
"min" => Ok(Self::Min),
|
|
||||||
"max" => Ok(Self::Max),
|
|
||||||
"mean" => Ok(Self::Mean),
|
|
||||||
"" => Err(()),
|
|
||||||
_ => Ok(Self::Name(s.to_string())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub(crate) fn default_for(token_type: TokenType) -> Self {
|
pub(crate) fn default_for(token_type: TokenType) -> Self {
|
||||||
match token_type {
|
match token_type {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::token::{Alignment, Part, Token, TokenType};
|
use super::token::{Alignment, Part, Token};
|
||||||
use super::Interval;
|
use super::Interval;
|
||||||
use crate::clients;
|
use crate::clients;
|
||||||
use crate::clients::sysinfo::{Value, ValueSet};
|
use crate::clients::sysinfo::{TokenType, Value, ValueSet};
|
||||||
|
|
||||||
pub enum TokenValue {
|
pub enum TokenValue {
|
||||||
Number(f64),
|
Number(f64),
|
||||||
|
|
|
@ -1,39 +1,4 @@
|
||||||
use crate::clients::sysinfo::{Function, Prefix};
|
use crate::clients::sysinfo::{Function, Prefix, TokenType};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum TokenType {
|
|
||||||
CpuFrequency,
|
|
||||||
CpuPercent,
|
|
||||||
|
|
||||||
MemoryFree,
|
|
||||||
MemoryAvailable,
|
|
||||||
MemoryTotal,
|
|
||||||
MemoryUsed,
|
|
||||||
MemoryPercent,
|
|
||||||
|
|
||||||
SwapFree,
|
|
||||||
SwapTotal,
|
|
||||||
SwapUsed,
|
|
||||||
SwapPercent,
|
|
||||||
|
|
||||||
TempC,
|
|
||||||
TempF,
|
|
||||||
|
|
||||||
DiskFree,
|
|
||||||
DiskTotal,
|
|
||||||
DiskUsed,
|
|
||||||
DiskPercent,
|
|
||||||
DiskRead,
|
|
||||||
DiskWrite,
|
|
||||||
|
|
||||||
NetDown,
|
|
||||||
NetUp,
|
|
||||||
|
|
||||||
LoadAverage1,
|
|
||||||
LoadAverage5,
|
|
||||||
LoadAverage15,
|
|
||||||
Uptime,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Token {
|
pub struct Token {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue