diff --git a/Cargo.lock b/Cargo.lock index a4b0270..873b128 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -59,6 +68,7 @@ version = "0.1.0" dependencies = [ "chrono", "dirs", + "regex", "reqwest", "serde", "serde_derive", @@ -546,6 +556,23 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + [[package]] name = "reqwest" version = "0.11.13" diff --git a/Cargo.toml b/Cargo.toml index b563c3c..1b00ea5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] chrono = { version = "*", features = ["serde"] } dirs = "4" +regex = "1.7" reqwest = { version = "*", features = ["json", "rustls-tls"], default-features = false } serde = { version = "1", features = ["derive"] } serde_json = "*" diff --git a/README.adoc b/README.adoc index 8397da3..f9d13d4 100644 --- a/README.adoc +++ b/README.adoc @@ -31,6 +31,14 @@ url = "tcp://app.example.com:12345" check_type = "Tcp" ---- +The value for `interval` can be set by using plain seconds or using units of `h`, `m` and `s`. Unparseable values will +default to 60 seconds. + +---- +# Update interval using value with units. Default value if not set is 60 sec. +interval = 2m30s +---- + Each host or application to be checked consists of `name` and `url`. You can optionally specify `check_type`: @@ -42,7 +50,7 @@ You can optionally specify `check_type`: ** `Down`: No response. * `Tcp`: Checks if TCP connection to given host and port can be established -To use more than one configuration, pass the config file location as first argument to the application. +To use more than one configuration, pass the config file location to be used as first argument to the application. ---- $ checkbar /etc/checkbar_example.toml diff --git a/src/config.rs b/src/config.rs index 6d9c760..71efd47 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,9 +1,11 @@ +use regex::Regex; use serde::Deserialize; +use std::time::Duration; use std::{env, fs}; #[derive(Deserialize)] pub struct Config { - pub interval: Option, + pub interval: Option, pub colors: Option, pub checks: Vec, } @@ -57,3 +59,79 @@ pub fn get_config() -> Config { }, } } + +pub fn parse_duration(value: Option) -> Duration { + let mut result = 0; + let value = match &value { + Some(value) => { + let result = value; + result.as_str() + } + _ => return Duration::from_secs(60), + }; + if let Ok(re) = Regex::new(r"^((?P\d+)h)?((?P\d+)m)?((?P\d+)s?)?$") { + if re.is_match(value) { + let parts = re.captures_iter(value).nth(0).unwrap(); + if let Some(hours) = parts.name("hours") { + result += match u64::from_str_radix(hours.as_str(), 10) { + Ok(value) => value * 60 * 60, + _ => 0, + }; + } + if let Some(minutes) = parts.name("minutes") { + result += match u64::from_str_radix(minutes.as_str(), 10) { + Ok(value) => value * 60, + _ => 0, + }; + } + if let Some(seconds) = parts.name("seconds") { + result += match u64::from_str_radix(seconds.as_str(), 10) { + Ok(value) => value, + _ => 0, + }; + } + } else { + return Duration::from_secs(60); + } + } + Duration::from_secs(result) +} + +#[cfg(test)] +mod tests { + use crate::config::parse_duration; + use std::time::Duration; + + #[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) + ); + } + + #[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) + ); + } +} diff --git a/src/main.rs b/src/main.rs index 330deac..180c70f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,14 +2,13 @@ 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, CheckConfig, CheckType}; +use crate::config::{get_config, parse_duration, CheckConfig, CheckType}; #[derive(Deserialize)] struct ClickEvent { @@ -102,7 +101,7 @@ async fn main() { loop { let config = get_config(); print_states(&config.checks).await; - std::thread::sleep(Duration::from_secs(config.interval.unwrap_or(60))); + std::thread::sleep(parse_duration(config.interval)); } });