mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-07-01 10:41:03 +02:00
Merge pull request #111 from JakeStanger/fix/script-parsing
fix(script): parser incorrectly handling colons
This commit is contained in:
commit
246313136f
1 changed files with 62 additions and 28 deletions
|
@ -2,6 +2,7 @@ use crate::send_async;
|
||||||
use color_eyre::eyre::WrapErr;
|
use color_eyre::eyre::WrapErr;
|
||||||
use color_eyre::{Report, Result};
|
use color_eyre::{Report, Result};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::cmp::min;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||||
|
@ -110,7 +111,13 @@ enum ScriptInputToken {
|
||||||
Mode(ScriptMode),
|
Mode(ScriptMode),
|
||||||
Interval(u64),
|
Interval(u64),
|
||||||
Cmd(String),
|
Cmd(String),
|
||||||
Colon,
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
enum CurrentToken {
|
||||||
|
Mode,
|
||||||
|
Interval,
|
||||||
|
Cmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for Script {
|
impl From<&str> for Script {
|
||||||
|
@ -118,46 +125,53 @@ impl From<&str> for Script {
|
||||||
let mut script = Self::default();
|
let mut script = Self::default();
|
||||||
let mut tokens = vec![];
|
let mut tokens = vec![];
|
||||||
|
|
||||||
|
let mut current_state = CurrentToken::Mode;
|
||||||
|
|
||||||
let mut chars = str.chars().collect::<Vec<_>>();
|
let mut chars = str.chars().collect::<Vec<_>>();
|
||||||
while !chars.is_empty() {
|
while !chars.is_empty() {
|
||||||
let char = chars[0];
|
let char = chars[0];
|
||||||
|
|
||||||
let (token, skip) = match char {
|
let parse_res = match current_state {
|
||||||
':' => (ScriptInputToken::Colon, 1),
|
CurrentToken::Mode => {
|
||||||
// interval
|
current_state = CurrentToken::Interval;
|
||||||
'0'..='9' => {
|
|
||||||
|
if matches!(char, 'p' | 'w') {
|
||||||
|
let mode_str = chars.iter().take_while(|&c| c != &':').collect::<String>();
|
||||||
|
let len = mode_str.len();
|
||||||
|
|
||||||
|
let token = ScriptMode::try_parse(&mode_str).ok();
|
||||||
|
token.map(|token| (ScriptInputToken::Mode(token), len))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CurrentToken::Interval => {
|
||||||
|
current_state = CurrentToken::Cmd;
|
||||||
|
|
||||||
|
if char.is_ascii_digit() {
|
||||||
let interval_str = chars
|
let interval_str = chars
|
||||||
.iter()
|
.iter()
|
||||||
.take_while(|c| c.is_ascii_digit())
|
.take_while(|c| c.is_ascii_digit())
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
let len = interval_str.len();
|
||||||
|
|
||||||
let interval = interval_str.parse::<u64>().unwrap_or_else(|_| {
|
let token = interval_str.parse::<u64>().ok();
|
||||||
warn!("Received invalid interval in script string. Falling back to default `5000ms`.");
|
token.map(|token| (ScriptInputToken::Interval(token), len))
|
||||||
5000
|
} else {
|
||||||
});
|
None
|
||||||
(ScriptInputToken::Interval(interval), interval_str.len())
|
|
||||||
}
|
}
|
||||||
// watching or polling
|
|
||||||
'w' | 'p' => {
|
|
||||||
let mode_str = chars.iter().take_while(|&c| c != &':').collect::<String>();
|
|
||||||
let len = mode_str.len();
|
|
||||||
|
|
||||||
let token = ScriptMode::try_parse(&mode_str)
|
|
||||||
.map_or(ScriptInputToken::Cmd(mode_str), |mode| {
|
|
||||||
ScriptInputToken::Mode(mode)
|
|
||||||
});
|
|
||||||
|
|
||||||
(token, len)
|
|
||||||
}
|
}
|
||||||
_ => {
|
CurrentToken::Cmd => {
|
||||||
let cmd_str = chars.iter().take_while(|_| true).collect::<String>();
|
let cmd_str = chars.iter().take_while(|_| true).collect::<String>();
|
||||||
let len = cmd_str.len();
|
let len = cmd_str.len();
|
||||||
(ScriptInputToken::Cmd(cmd_str), len)
|
Some((ScriptInputToken::Cmd(cmd_str), len))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some((token, skip)) = parse_res {
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
chars.drain(..skip);
|
chars.drain(..min(skip + 1, chars.len())); // skip 1 extra for colon
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for token in tokens {
|
for token in tokens {
|
||||||
|
@ -165,7 +179,6 @@ impl From<&str> for Script {
|
||||||
ScriptInputToken::Mode(mode) => script.mode = mode,
|
ScriptInputToken::Mode(mode) => script.mode = mode,
|
||||||
ScriptInputToken::Interval(interval) => script.interval = interval,
|
ScriptInputToken::Interval(interval) => script.interval = interval,
|
||||||
ScriptInputToken::Cmd(cmd) => script.cmd = cmd,
|
ScriptInputToken::Cmd(cmd) => script.cmd = cmd,
|
||||||
ScriptInputToken::Colon => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,6 +311,27 @@ impl Script {
|
||||||
|
|
||||||
Ok(rx)
|
Ok(rx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes the script in oneshot mode,
|
||||||
|
/// meaning it is not awaited and output cannot be captured.
|
||||||
|
///
|
||||||
|
/// If the script errors, this is logged.
|
||||||
|
///
|
||||||
|
/// This has some overhead,
|
||||||
|
/// as the script has to be cloned to the thread.
|
||||||
|
///
|
||||||
|
pub fn run_as_oneshot(&self, args: Option<&[String]>) {
|
||||||
|
let script = self.clone();
|
||||||
|
let args = args.map(|args| args.to_vec());
|
||||||
|
|
||||||
|
spawn(async move {
|
||||||
|
match script.get_output(args.as_deref()).await {
|
||||||
|
Ok((OutputStream::Stderr(out), _)) => error!("{out}"),
|
||||||
|
Err(err) => error!("{err:?}"),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue