use std::collections::hash_map::HashMap; use std::fs::File; use std::io::Read; use std::fmt::{Display, Formatter, Error}; #[derive(Clone)] pub enum ConfigValue { Str(String), UInt(usize) } impl Display for ConfigValue { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { match self { ConfigValue::Str(s) => write!(f, "{}", s), ConfigValue::UInt(u) => write!(f, "{}", u) } } } struct ConfigItem { default_value: ConfigValue, parse_func: Box Option>, } pub struct Config { v: HashMap, } fn stripped_str(s: String) -> Option { Some(ConfigValue::Str(String::from(s.trim()))) } fn parse_usize(s: String) -> Option { if let Ok(r) = usize::from_str_radix(&s, 10) { Some(ConfigValue::UInt(r)) } else { None } } pub fn split_at_first<'a, 'b>(s: &'a str, sp: &'b str) -> (&'a str, &'a str) { if let Some(p) = s.find(sp) { (&s[.. p], &s[p + sp.len()..]) } else { (s, "") } } impl Config { fn items() -> HashMap<&'static str, ConfigItem> { HashMap::from([ ("SERVED_DATA_ROOT", ConfigItem{default_value: ConfigValue::Str(String::from("")), parse_func: Box::new(stripped_str)}), ("POSTS_PER_PAGE", ConfigItem{default_value: ConfigValue::UInt(20), parse_func: Box::new(parse_usize)}), ("DISPLAY_TIMEZONE", ConfigItem{default_value: ConfigValue::Str(String::from("UTC")), parse_func: Box::new(stripped_str)}), ("VERSION_STRING", ConfigItem{default_value: ConfigValue::Str(String::from("1.0")), parse_func: Box::new(stripped_str)}), ]) } pub fn parse_config(filename: &str) -> Config { let mut v = HashMap::from_iter( Config::items().iter() .map(|(k, v)| {(String::from(*k), v.default_value.clone())}) ); let mut f = File::open(filename).unwrap(); let mut s = String::new(); if let Ok(_) = f.read_to_string(&mut s) { for l in s.split("\n") { let (sname, svalue) = split_at_first(l, "="); if let Some(ConfigItem{default_value: _, parse_func}) = Config::items().get(sname) { if let Some(value) = parse_func(String::from(svalue)) { v.insert(String::from(sname), value); } } } } Config { v } } pub fn get(&self, key: &str) -> ConfigValue { self.v.get(&String::from(key)).unwrap().clone() } pub fn get_str(&self, key: &str) -> String { if let ConfigValue::Str(s) = self.get(key) { s } else { String::from("") } } }