1
0
mirror of https://github.com/pcvolkmer/checkbar.git synced 2025-04-19 11:06: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.
----
# 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"
----

View File

@ -1,11 +1,15 @@
use regex::Regex;
use serde::Deserialize;
use std::fmt::Formatter;
use std::time::Duration;
use std::{env, fs};
use regex::Regex;
use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer};
#[derive(Deserialize)]
pub struct Config {
pub interval: Option<String>,
#[serde(default, deserialize_with = "deserialize_duration")]
pub interval: Option<Duration>,
pub colors: Option<ColorConfig>,
pub checks: Vec<CheckConfig>,
}
@ -60,15 +64,42 @@ pub fn get_config() -> Config {
}
}
pub fn parse_duration(value: Option<String>) -> Duration {
let mut result = 0;
let value = match &value {
Some(value) => {
let result = value;
result.as_str()
fn deserialize_duration<'de, D>(d: D) -> Result<Option<Duration>, D::Error>
where
D: Deserializer<'de>,
{
struct StringVisitor;
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) =
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 {
return Duration::from_secs(60);
return None;
}
}
Duration::from_secs(result)
Some(Duration::from_secs(result))
}
#[cfg(test)]
mod tests {
use crate::config::parse_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]
fn test_should_parse_durations() {
assert_eq!(
parse_duration(Some("1m30s".to_string())),
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)
);
assert_eq!(parse_duration("1m30s"), Some(Duration::from_secs(90)));
assert_eq!(parse_duration("2m"), Some(Duration::from_secs(120)));
assert_eq!(parse_duration("1h1m1s"), Some(Duration::from_secs(3661)));
assert_eq!(parse_duration("90"), Some(Duration::from_secs(90)));
}
#[test]
fn test_should_parse_durations_with_whitespaces() {
assert_eq!(
parse_duration(Some("1m 30s".to_string())),
Duration::from_secs(90)
);
assert_eq!(
parse_duration(Some("1h 1m 1s".to_string())),
Duration::from_secs(3661)
);
assert_eq!(parse_duration("1m 30s"), Some(Duration::from_secs(90)));
assert_eq!(parse_duration("1h 1m 1s"), Some(Duration::from_secs(3661)));
}
#[test]
fn test_should_return_default_for_unparseable_durations() {
assert_eq!(parse_duration(None), Duration::from_secs(60));
assert_eq!(
parse_duration(Some("invalid".to_string())),
Duration::from_secs(60)
);
assert_eq!(
parse_duration(Some("1x30m10q".to_string())),
Duration::from_secs(60)
);
assert_eq!(parse_duration("invalid"), None);
assert_eq!(parse_duration("1x30m10q"), None);
}
}

View File

@ -1,14 +1,15 @@
mod checker;
mod config;
use std::process;
use std::time::Duration;
use serde::Deserialize;
use serde_json::json;
use tokio::task;
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)]
struct ClickEvent {
@ -101,7 +102,11 @@ async fn main() {
loop {
let config = get_config();
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);
}
});