rustybar/src/config.rs

161 lines
4.7 KiB
Rust
Raw Normal View History

2020-06-04 03:05:45 +00:00
use crate::tiles;
use crate::tiles::TileResult;
2020-06-19 20:06:24 +00:00
use dbus::nonblock::SyncConnection;
2020-06-14 05:37:32 +00:00
use futures::{stream::BoxStream, Stream};
use serde::{Deserialize, Deserializer};
2020-06-04 03:05:45 +00:00
use smart_default::SmartDefault;
use std::env::var;
use std::io;
use std::path::PathBuf;
2020-06-19 20:06:24 +00:00
use std::sync::Arc;
2020-06-04 03:05:45 +00:00
use structopt::StructOpt;
use tokio::fs::File;
2021-07-29 04:50:46 +00:00
use tokio::io::AsyncReadExt;
use tokio::time::Duration;
use tokio_stream::StreamExt;
2020-06-04 03:05:45 +00:00
#[derive(Deserialize, Clone, Debug, Default)]
#[serde(default)]
pub struct Config {
pub default: DefaultSection,
pub tile: Box<[TileConfig]>,
}
#[derive(Deserialize, Clone, Debug, Default)]
pub struct DefaultSection {
pub spacing: Option<u32>,
}
2020-06-14 05:37:32 +00:00
fn deserialize_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let number = s.trim_end_matches(char::is_alphabetic);
let suffix = s.trim_start_matches(number);
let number: f64 = number.parse().expect("Not a valid f64");
let duration = match suffix {
"s" | "" => Duration::from_secs_f64(number),
"m" => Duration::from_secs_f64(number * 60f64),
"ms" => Duration::from_secs_f64(number / 1000f64),
_ => unimplemented!(),
};
Ok(Some(duration))
}
#[derive(Deserialize, Clone, Debug)]
pub struct TileConfig {
#[serde(flatten)]
config_type: TileConfigType,
#[serde(deserialize_with = "deserialize_duration", default)]
update: Option<Duration>,
}
2020-06-04 03:05:45 +00:00
#[derive(Deserialize, Clone, Debug)]
#[serde(tag = "type", rename_all = "snake_case")]
2020-06-14 05:37:32 +00:00
pub enum TileConfigType {
Battery(BatteryConfig),
2020-06-04 03:05:45 +00:00
Memory,
Load,
Hostname,
Time(TimeConfig),
2021-07-29 04:50:46 +00:00
Iwd(IwdConfig),
}
#[derive(SmartDefault, Deserialize, Clone, Debug)]
#[serde(default)]
pub struct BatteryConfig {
#[default("BAT0")]
pub battery: Box<str>,
}
2021-07-29 04:50:46 +00:00
#[derive(SmartDefault, Deserialize, Clone, Debug)]
#[serde(default)]
pub struct IwdConfig {
#[default("wlan0")]
pub interface: Box<str>,
2020-06-04 03:05:45 +00:00
}
#[derive(SmartDefault, Deserialize, Clone, Debug)]
#[serde(default)]
pub struct TimeConfig {
#[default("%Y-%m-%d %H:%M:%S")]
pub format: Box<str>,
#[default("%H:%M:%S")]
pub short_format: Box<str>,
}
#[derive(Debug, StructOpt)]
#[structopt(
name = "rustybar",
about = "Something to exec for your swaybar or i3bar"
)]
struct Args {
/// Configuration file, default is $XDG_CONFIG_HOME/rustybar/config.toml
#[structopt(short, long, parse(from_os_str))]
pub config: Option<PathBuf>,
}
pub async fn read_config() -> Result<Config, Box<dyn std::error::Error>> {
let args = Args::from_args();
let config_path = match args.config {
Some(config) => config,
None => {
if let Ok(rustybar_config_env) = var("RUSTYBAR_CONFIG") {
rustybar_config_env.into()
} else if let Ok(xdg_config_home) = var("XDG_CONFIG_HOME") {
[&xdg_config_home, "rustybar", "config.toml"]
.iter()
.collect()
} else if let Ok(home) = var("HOME") {
[&home, ".config", "rustybar", "config.toml"]
.iter()
.collect()
} else {
return Err(Box::new(io::Error::new(
io::ErrorKind::NotFound,
"Could not find RUSTYBAR_CONFIG, XDG_CONFIG_HOME, or HOME environment variables"
)));
}
}
};
let mut config_contents = vec![];
File::open(config_path)
.await?
.read_to_end(&mut config_contents)
.await?;
Ok(toml::from_slice(&config_contents)?)
}
pub fn process_tile<'a>(
tile: &TileConfig,
connection: &'a Arc<SyncConnection>,
) -> BoxStream<'static, TileResult> {
2020-06-14 05:37:32 +00:00
let five_secs = Duration::from_secs(5);
match &tile.config_type {
TileConfigType::Battery(c) => wrap(
tiles::battery_stream(c.clone()),
tile.update.or(Some(five_secs)),
),
TileConfigType::Hostname => wrap(tiles::hostname_stream(connection.as_ref()), tile.update),
2020-06-14 05:37:32 +00:00
TileConfigType::Load => wrap(tiles::load_stream(), tile.update.or(Some(five_secs))),
TileConfigType::Memory => wrap(tiles::memory_stream(), tile.update.or(Some(five_secs))),
TileConfigType::Time(c) => wrap(tiles::time_stream(c.clone()), tile.update),
2021-07-29 04:50:46 +00:00
TileConfigType::Iwd(c) => wrap(
tiles::iwd_stream(connection.clone(), c.clone()),
tile.update.or(Some(five_secs)),
),
2020-06-14 05:37:32 +00:00
}
}
fn wrap<'a, S>(stream: S, duration: Option<Duration>) -> BoxStream<'a, TileResult>
2020-06-14 05:37:32 +00:00
where
S: Stream<Item = TileResult> + Send + 'a,
2020-06-14 05:37:32 +00:00
{
match duration {
2021-07-29 04:50:46 +00:00
Some(duration) => Box::pin(stream.throttle(duration)),
2020-06-14 05:37:32 +00:00
None => Box::pin(stream),
2020-06-04 03:05:45 +00:00
}
}