mirror of
				https://github.com/pcvolkmer/checkbar.git
				synced 2025-10-30 19:46:11 +00:00 
			
		
		
		
	Use numbers or parseable string for config property interval
This commit is contained in:
		| @@ -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" | ||||||
| ---- | ---- | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										147
									
								
								src/config.rs
									
									
									
									
									
								
							
							
						
						
									
										147
									
								
								src/config.rs
									
									
									
									
									
								
							| @@ -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) |  | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/main.rs
									
									
									
									
									
								
							| @@ -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); | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user