use super::prelude::*;
use std::env;
use tokio::process::Command;
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct Config {
pub format: FormatConfig,
pub command_on: String,
pub command_off: String,
pub command_state: String,
#[serde(default)]
pub icon_on: Option<String>,
#[serde(default)]
pub icon_off: Option<String>,
#[serde(default)]
pub interval: Option<u64>,
pub state_on: Option<State>,
pub state_off: Option<State>,
}
async fn sleep_opt(dur: Option<Duration>) {
match dur {
Some(dur) => tokio::time::sleep(dur).await,
None => std::future::pending().await,
}
}
pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
let mut actions = api.get_actions()?;
api.set_default_actions(&[(MouseButton::Left, None, "toggle")])?;
let interval = config.interval.map(Duration::from_secs);
let mut widget = Widget::new().with_format(config.format.with_default(" $icon ")?);
let icon_on = config.icon_on.as_deref().unwrap_or("toggle_on");
let icon_off = config.icon_off.as_deref().unwrap_or("toggle_off");
let shell = env::var("SHELL").unwrap_or_else(|_| "sh".to_string());
loop {
let output = Command::new(&shell)
.args(["-c", &config.command_state])
.output()
.await
.error("Failed to run command_state")?;
let is_on = !std::str::from_utf8(&output.stdout)
.error("The output of command_state is invalid UTF-8")?
.trim()
.is_empty();
widget.set_values(map!(
"icon" => Value::icon(
if is_on { icon_on.to_string() } else { icon_off.to_string() }
)
));
if widget.state != State::Critical {
widget.state = if is_on {
config.state_on.unwrap_or(State::Idle)
} else {
config.state_off.unwrap_or(State::Idle)
};
}
api.set_widget(widget.clone())?;
loop {
select! {
_ = sleep_opt(interval) => break,
_ = api.wait_for_update_request() => break,
Some(action) = actions.recv() => match action.as_ref() {
"toggle" => {
let cmd = if is_on {
&config.command_off
} else {
&config.command_on
};
let output = Command::new(&shell)
.args(["-c", cmd])
.output()
.await
.error("Failed to run command")?;
if output.status.success() {
widget.state = State::Idle;
break;
} else {
widget.state = State::Critical;
}
}
_ => (),
}
}
}
}
}