1
0
mirror of https://github.com/pcvolkmer/checkbar.git synced 2025-04-19 19:16:50 +00:00

Use numbers or parseable string for config property interval

This commit is contained in:
Paul-Christian Volkmer 2022-12-31 00:44:35 +01:00
parent 813ebc7a24
commit 5cc33c983e
3 changed files with 116 additions and 53 deletions

View File

@ -35,7 +35,12 @@ The value for `interval` can be set by using plain seconds or using units of `h`
default to 60 seconds. default to 60 seconds.
---- ----
# Update interval using value with units. Default value if not set is 60 sec. # Update interval using seconds as number.
interval = 60
# or ...
# Update interval using value with units.
interval = "2m 30s" interval = "2m 30s"
---- ----

View File

@ -1,11 +1,15 @@
use regex::Regex; use std::fmt::Formatter;
use serde::Deserialize;
use std::time::Duration; use std::time::Duration;
use std::{env, fs}; use std::{env, fs};
use regex::Regex;
use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer};
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct Config { pub struct Config {
pub interval: Option<String>, #[serde(default, deserialize_with = "deserialize_duration")]
pub interval: Option<Duration>,
pub colors: Option<ColorConfig>, pub colors: Option<ColorConfig>,
pub checks: Vec<CheckConfig>, pub checks: Vec<CheckConfig>,
} }
@ -60,15 +64,42 @@ pub fn get_config() -> Config {
} }
} }
pub fn parse_duration(value: Option<String>) -> Duration { fn deserialize_duration<'de, D>(d: D) -> Result<Option<Duration>, D::Error>
let mut result = 0; where
let value = match &value { D: Deserializer<'de>,
Some(value) => { {
let result = value; struct StringVisitor;
result.as_str()
impl<'de> Visitor<'de> for StringVisitor {
type Value = String;
fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
f.write_str("a number or string with parsable duration")
} }
_ => return Duration::from_secs(60),
}; fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(format!("{}", v))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
Ok(v.to_string())
}
}
match d.deserialize_string(StringVisitor) {
Ok(value) => Ok(parse_duration(value.as_str())),
Err(err) => Err(err),
}
}
fn parse_duration(value: &str) -> Option<Duration> {
let mut result = 0;
if let Ok(re) = if let Ok(re) =
Regex::new(r"^((?P<hours>\d+)h\s*)?((?P<minutes>\d+)m\s*)?((?P<seconds>\d+)s?\s*)?$") Regex::new(r"^((?P<hours>\d+)h\s*)?((?P<minutes>\d+)m\s*)?((?P<seconds>\d+)s?\s*)?$")
{ {
@ -93,59 +124,81 @@ pub fn parse_duration(value: Option<String>) -> Duration {
}; };
} }
} else { } else {
return Duration::from_secs(60); return None;
} }
} }
Duration::from_secs(result) Some(Duration::from_secs(result))
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::config::parse_duration;
use std::time::Duration; use std::time::Duration;
use crate::config::{parse_duration, Config};
#[test]
fn test_should_parse_config_with_number_interval() {
let config: Config = toml::from_str(
r#"
interval = 123
[[checks]]
name = "example"
url = "https://example.com"
"#,
)
.unwrap();
assert_eq!(config.interval, Some(Duration::from_secs(123)));
}
#[test]
fn test_should_parse_config_with_parsed_interval() {
let config: Config = toml::from_str(
r#"
interval = "2m 3s"
[[checks]]
name = "example"
url = "https://example.com"
"#,
)
.unwrap();
assert_eq!(config.interval, Some(Duration::from_secs(123)));
}
#[test]
fn test_should_parse_config_without_interval() {
let config: Config = toml::from_str(
r#"
[[checks]]
name = "example"
url = "https://example.com"
"#,
)
.unwrap();
assert_eq!(config.interval, None);
}
#[test] #[test]
fn test_should_parse_durations() { fn test_should_parse_durations() {
assert_eq!( assert_eq!(parse_duration("1m30s"), Some(Duration::from_secs(90)));
parse_duration(Some("1m30s".to_string())), assert_eq!(parse_duration("2m"), Some(Duration::from_secs(120)));
Duration::from_secs(90) assert_eq!(parse_duration("1h1m1s"), Some(Duration::from_secs(3661)));
); assert_eq!(parse_duration("90"), Some(Duration::from_secs(90)));
assert_eq!(
parse_duration(Some("2m".to_string())),
Duration::from_secs(120)
);
assert_eq!(
parse_duration(Some("1h1m1s".to_string())),
Duration::from_secs(3661)
);
assert_eq!(
parse_duration(Some("90".to_string())),
Duration::from_secs(90)
);
} }
#[test] #[test]
fn test_should_parse_durations_with_whitespaces() { fn test_should_parse_durations_with_whitespaces() {
assert_eq!( assert_eq!(parse_duration("1m 30s"), Some(Duration::from_secs(90)));
parse_duration(Some("1m 30s".to_string())), assert_eq!(parse_duration("1h 1m 1s"), Some(Duration::from_secs(3661)));
Duration::from_secs(90)
);
assert_eq!(
parse_duration(Some("1h 1m 1s".to_string())),
Duration::from_secs(3661)
);
} }
#[test] #[test]
fn test_should_return_default_for_unparseable_durations() { fn test_should_return_default_for_unparseable_durations() {
assert_eq!(parse_duration(None), Duration::from_secs(60)); assert_eq!(parse_duration("invalid"), None);
assert_eq!( assert_eq!(parse_duration("1x30m10q"), None);
parse_duration(Some("invalid".to_string())),
Duration::from_secs(60)
);
assert_eq!(
parse_duration(Some("1x30m10q".to_string())),
Duration::from_secs(60)
);
} }
} }

View File

@ -1,14 +1,15 @@
mod checker;
mod config;
use std::process; use std::process;
use std::time::Duration;
use serde::Deserialize; use serde::Deserialize;
use serde_json::json; use serde_json::json;
use tokio::task; use tokio::task;
use crate::checker::{ActuatorChecker, CheckResult, HttpChecker, TcpChecker}; use crate::checker::{ActuatorChecker, CheckResult, HttpChecker, TcpChecker};
use crate::config::{get_config, parse_duration, CheckConfig, CheckType}; use crate::config::{get_config, CheckConfig, CheckType};
mod checker;
mod config;
#[derive(Deserialize)] #[derive(Deserialize)]
struct ClickEvent { struct ClickEvent {
@ -101,7 +102,11 @@ async fn main() {
loop { loop {
let config = get_config(); let config = get_config();
print_states(&config.checks).await; print_states(&config.checks).await;
std::thread::sleep(parse_duration(config.interval)); let interval = match config.interval {
Some(value) => value,
_ => Duration::from_secs(60),
};
std::thread::sleep(interval);
} }
}); });