Mercurial > lbo > hg > syslog
changeset 20:4914bd1e162f draft
Finish implementation of Config
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sat, 03 Dec 2016 15:45:27 +0100 |
parents | c4be6edfffec |
children | eae478565a9f |
files | src/config.rs |
diffstat | 1 files changed, 208 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/src/config.rs Sat Dec 03 14:41:35 2016 +0100 +++ b/src/config.rs Sat Dec 03 15:45:27 2016 +0100 @@ -5,12 +5,62 @@ use std::collections::btree_map::Entry; use std::default::Default; use std::net::SocketAddr; +use std::str::FromStr; use dns_lookup; use toml; use priority; +/// Parses strings like 0/1s/2m/3h/4d/5w and returns seconds. If no unit is there, seconds is +/// assumed. +fn parse_duration(s: &str) -> Option<u64> { + let s = s.trim(); + let suffix = &s[s.len() - 1..]; + let num_part = &s[0..s.len() - 1]; + + if suffix == "s" { + if let Ok(s) = u64::from_str(num_part) { + Some(s) + } else { + None + } + } else if suffix == "m" { + if let Ok(m) = u64::from_str(num_part) { + Some(m * 60) + } else { + None + } + } else if suffix == "h" { + if let Ok(h) = u64::from_str(num_part) { + Some(h * 60 * 60) + } else { + None + } + } else if suffix == "d" { + if let Ok(d) = u64::from_str(num_part) { + Some(d * 24 * 60 * 60) + } else { + None + } + } else if suffix == "w" { + if let Ok(w) = u64::from_str(num_part) { + Some(w * 7 * 24 * 60 * 60) + } else { + None + } + } else if let Ok(_) = u64::from_str(suffix) { + // no explicit unit + if let Ok(s) = u64::from_str(s) { + Some(s) + } else { + None + } + } else { + None + } +} + // helper functions for extracting values from TOML values fn get_string(tbl: &toml::Table, key: &str, default: String) -> String { if let Some(e) = tbl.get(key) { @@ -30,6 +80,53 @@ default } +fn get_bool(tbl: &toml::Table, key: &str, default: bool) -> bool { + if let Some(e) = tbl.get(key) { + if let &toml::Value::Boolean(b) = e { + return b; + } + } + default +} + +fn get_tbl<'a>(tbl: &'a toml::Table, key: &str) -> Option<&'a toml::Table> { + if let Some(e) = tbl.get(key) { + if let &toml::Value::Table(ref t) = e { + return Some(t); + } + } + None +} + +/// Extract an array of tables. +fn get_tbl_arr(tbl: &toml::Table, key: &str) -> Option<Vec<toml::Table>> { + if let Some(e) = tbl.get(key) { + if let &toml::Value::Array(ref a) = e { + let mut v = Vec::with_capacity(a.len()); + + for elem in a { + match elem { + &toml::Value::Table(ref t) => v.push(t.clone()), + _ => (), + } + } + } + } + None +} + +fn get_tbls(tbl: &toml::Table) -> Option<Vec<(String, toml::Table)>> { + let mut res = Vec::with_capacity(tbl.len()); + + for (k, t) in tbl.iter() { + if let &toml::Value::Table(ref t) = t { + res.push((k.clone(), t.clone())); + } + } + + Some(res) +} + fn tomlerr<T>(s: String) -> Result<T, toml::Error> { Err(toml::Error::Custom(s)) } @@ -42,6 +139,7 @@ pub max_msg_len: usize, } +/// Takes a "general" table fn assemble_general(t: &toml::Table) -> Result<General, toml::Error> { let default_bind_path = "/dev/log".to_string(); let default_max_msg_len = 8192; @@ -59,7 +157,8 @@ addr: SocketAddr, } -/// Assemble a Remote from a `remote` entry, and resolve the address if needed. +/// Assemble a Remote from a `remote` entry, and resolve the address if needed; takes a "remote" +/// table. fn assemble_remote(t: &toml::Table, name: &str) -> Result<Remote, toml::Error> { let addr = get_string(t, "addr", String::new()); let port = get_int(t, "port", 0); @@ -84,7 +183,7 @@ fn assemble_compress_type(v: &toml::Value) -> Result<CompressType, toml::Error> { if let &toml::Value::String(ref s) = v { - if s == "none" { + if s == "none" || s == "" { return Ok(CompressType::NoCompression); } else if s == "gzip" { return Ok(CompressType::Gzip); @@ -113,7 +212,19 @@ compress: CompressType, } - +fn assemble_file(tbl: &toml::Table, name: &str) -> Result<File, toml::Error> { + let f = File { + name: name.to_string(), + location: get_string(tbl, "file", "".to_string()), + max_size: get_int(tbl, "max_size", 4 * 1024 * 1024) as usize, + max_age: parse_duration(&get_string(tbl, "max_age", "".to_string())).unwrap_or(0), + history: get_int(tbl, "history", 10) as i32, + compress: assemble_compress_type(tbl.get("compress") + .unwrap_or(&toml::Value::String("".to_string()))) + .unwrap_or(CompressType::NoCompression), + }; + Ok(f) +} #[derive(Clone, Debug)] pub enum FacPattern { @@ -165,6 +276,10 @@ let mut matcher = Vec::with_capacity(3); for filter in s.trim().split(';') { + if filter.len() == 0 { + return Ok(matcher); + } + match parse_filter(filter) { Ok(f) => matcher.push(f), Err(e) => return Err(e), @@ -262,6 +377,24 @@ stop: bool, } +/// Takes a "rule" table +fn assemble_rule(t: &toml::Table) -> Result<Rule, toml::Error> { + let pattern = get_string(t, "pattern", "".to_string()); + let matcher = try!(parse_matcher(&pattern)); + + let file = get_string(t, "dest", "null".to_string()); + let remote = get_string(t, "remote_dest", "".to_string()); + + let stop = get_bool(t, "stop", false); + + Ok(Rule { + pattern: matcher, + file: file, + remote_dest: remote, + stop: stop, + }) +} + #[derive(Clone,Default)] pub struct Config { pub general: General, @@ -271,8 +404,54 @@ } fn decode_config(text: &String) -> Result<Config, toml::Error> { + let mut cfg = Config::default(); + if let Some(tbl) = toml::Parser::new(&text).parse() { - let general = try!(assemble_general(&tbl)); + if let Some(gen) = get_tbl(&tbl, "general") { + cfg.general = try!(assemble_general(&tbl)); + } else { + return tomlerr("Could not find 'general' section".to_string()); + } + + // remotes: { remote1: { addr: xy, port: 12 }, remote2: { addr: xz, port: 34 } } + if let Some(remotes) = get_tbl(&tbl, "remotes") { + if let Some(remotes) = get_tbls(&remotes) { + for (name, remote) in remotes { + match assemble_remote(&remote, &name) { + Ok(remote) => { + cfg.remotes.insert(name, remote); + } + Err(e) => return tomlerr(format!("Error setting up remotes: {}", e)), + } + } + } + } + + // files: { file1: { file: "/a/b/c" history: "1w" }, file2: { file: "/d/e/f", compress: + // "none" } } + if let Some(files) = get_tbl(&tbl, "files") { + if let Some(files) = get_tbls(&files) { + for (name, file) in files { + match assemble_file(&file, &name) { + Ok(file) => { + cfg.files.insert(name, file); + } + Err(e) => return tomlerr(format!("Error setting up files: {}", e)), + } + } + } + } + + if let Some(rules) = get_tbl_arr(&tbl, "rules") { + for r in rules { + match assemble_rule(&r) { + Ok(r) => { + cfg.rules.push(r); + } + Err(e) => return tomlerr(format!("Error setting up rules: {}", e)), + } + } + } unimplemented!() } else { @@ -294,6 +473,30 @@ let m = "mail, news.info; kern.warn"; - assert_eq!(format!("{:?}", super::parse_matcher(m)), "Ok([(MultiFacility([NEWS, LOCAL0]), Level(INFO)), (Facility(KERN), Level(WARNING))])"); + assert_eq!(format!("{:?}", super::parse_matcher(m)), + "Ok([(MultiFacility([NEWS, LOCAL0]), Level(INFO)), (Facility(KERN), \ + Level(WARNING))])"); + + assert_eq!(format!("{:?}", super::parse_matcher("")), "Ok([])"); + } + + #[test] + fn test_config_duration_parse() { + use super::parse_duration; + + let cases = vec![("1", 1), + ("10", 10), + ("10s", 10), + ("1m", 60), + ("30m", 30 * 60), + ("60m", 60 * 60), + ("1h", 60 * 60), + ("24h", 24 * 60 * 60), + ("1d", 24 * 60 * 60), + ("4w", 4 * 7 * 24 * 60 * 60)]; + + for (test, result) in cases { + assert_eq!(parse_duration(test), Some(result)); + } } }