From 24baa3a6a66fec67bf71fd18b8035b2a491b126b Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Fri, 23 Sep 2022 22:27:13 -0400 Subject: [PATCH] Load default configuration overrides from files --- Cargo.lock | 1 + Cargo.toml | 3 +- src/conf.rs | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 27 +++++++++++- 4 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 src/conf.rs diff --git a/Cargo.lock b/Cargo.lock index 7d356f8..a727777 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -396,6 +396,7 @@ name = "haunter" version = "0.1.0" dependencies = [ "scraper", + "tempfile", "thirtyfour_sync", ] diff --git a/Cargo.toml b/Cargo.toml index 2684971..594ebda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" [dependencies] thirtyfour_sync = "0.27" -scraper = "0.13" \ No newline at end of file +scraper = "0.13" +tempfile = "3" \ No newline at end of file diff --git a/src/conf.rs b/src/conf.rs new file mode 100644 index 0000000..4b7202c --- /dev/null +++ b/src/conf.rs @@ -0,0 +1,125 @@ +use std::io::Read; +use std::str::FromStr; +use std::time::Duration; + +pub struct Conf { + pub job_dir: String, + pub output_dir: String, + pub check_interval: Duration, +} + +impl Conf { + pub fn update_from_file(&mut self, f: &str) { + let path = std::path::Path::new(f); + let mut file = match std::fs::File::open(&path) { + Err(why) => { + println!("Could not open file '{}': {}", path.display(), why); return; + }, + Ok(file) => file, + }; + let mut content = String::new(); + match file.read_to_string(&mut content) { + Err(why) => println!("Could not read from file '{}': {}", path.display(), why), + Ok(_) => (), + } + + let lines = content.lines(); + for line in lines { + if line.starts_with('#') { + continue; + } + let result = line.split_once('='); + if result.is_none() { + println!("Skipping configuration line '{}', no key-value delimiter (=) found", line); + continue; + } + + let key = result.unwrap().0.trim(); + let value = result.unwrap().1.trim(); + if key.eq("") || value.eq("") { + println!("Skipping configuration line '{}', no key or value side is empty", line); + continue; + } + + match key { + "job_dir" => { + println!("{} changed from '{}' to '{}' by line in '{}'", key, self.job_dir, value, path.display()); + self.job_dir = String::from_str(value).unwrap(); + }, + "output_dir" => { + println!("{} changed from '{}' to '{}' by line in '{}'", key, self.output_dir, value, path.display()); + self.output_dir = String::from_str(value).unwrap(); + }, + "check_interval" => { + let converted_value = match value.parse::() { + Err(why) => { println!("Failed to convert '{}' to u64: {}", value, why); continue;}, + Ok(v) => v, + }; + println!("{} changed from '{}' to '{}' by line in '{}'", key, self.check_interval.as_secs(), value, path.display()); + self.check_interval = Duration::new(converted_value, 0); + }, + _ => { + println!("Unknown key '{}' in file '{}'", key, path.display()); + } + } + } + } +} + +#[cfg(test)] +mod tests { + + use std::io::Write; + use std::str::FromStr; + use tempfile::NamedTempFile; + use std::time::Duration; + + use super::*; + + fn get_default_conf() -> Conf { + return Conf { + job_dir: String::from_str("jobs.d").unwrap(), + output_dir: String::from_str("results.d").unwrap(), + check_interval: Duration::new(15*60, 0), + }; + } + + fn write_conf_to_temp_file(content: &str) -> NamedTempFile { + let mut f = NamedTempFile::new().unwrap(); + f.write_all(content.as_bytes()); + return f; + } + + #[test] + fn test_not_existing() { + let mut conf = get_default_conf(); + conf.update_from_file("/not/a/real/file"); + } + + #[test] + fn test_set_all_values() { + let mut conf = get_default_conf(); + let tf = write_conf_to_temp_file(r#" +job_dir = /value +output_dir=/var/lib/output +check_interval=3600 + "#); + let f = tf.path().to_str().unwrap(); + conf.update_from_file(f); + assert!(String::from_str("/value").unwrap().eq(&conf.job_dir)); + assert!(String::from_str("/var/lib/output").unwrap().eq(&conf.output_dir)); + assert_eq!(3600, conf.check_interval.as_secs()); + } + + #[test] + fn test_non_numeric_check_interval_fails() { + let mut conf = get_default_conf(); + let tf = write_conf_to_temp_file(r#" +check_interval=d3600 + "#); + let f = tf.path().to_str().unwrap(); + conf.update_from_file(f); + assert_eq!(15*60, conf.check_interval.as_secs()); + + } +} diff --git a/src/main.rs b/src/main.rs index 3f6f926..2e3f42f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,13 @@ -use std::option; +use std::env; +use std::str::FromStr; use std::thread; use std::time::Duration; use std::time::Instant; use thirtyfour_sync::WebDriverCommands; +mod conf; +use conf::Conf; + struct Job<'a> { url: &'a str, selector: &'a str, @@ -20,7 +24,26 @@ struct ThreadJob<'a> { >, last_result: Option, } + fn main() { + let mut conf = Conf { + job_dir: String::from_str("jobs.d").unwrap(), + output_dir: String::from_str("results.d").unwrap(), + check_interval: Duration::new(15*60, 0), + }; + + conf.update_from_file("/etc/haunter/haunter.conf"); + + let mut args = env::args(); + while let Some(arg) = args.next() { + if arg.eq("-f") { + match args.next() { + Some(file) => conf.update_from_file(file.as_str()), + _ => {println!("Missing argument after '-f'"); std::process::exit(1);}, + }; + } + } + let driver = "http://localhost:4444"; let mut jobs = Vec::new(); let some_job = Job { @@ -106,3 +129,5 @@ fn get_source(driver: &str, url: &str) -> Result { driver.quit().expect("failed to close session"); return Ok(source); } + +