diff --git a/src/section_config.rs b/src/section_config.rs index c1c8b7ab..6138b521 100644 --- a/src/section_config.rs +++ b/src/section_config.rs @@ -3,6 +3,9 @@ use failure::*; use std::fs::File; use std::io::Read; use std::collections::HashMap; +use std::collections::HashSet; +use std::collections::LinkedList; + use serde_json::{json, Value}; use std::sync::Arc; @@ -27,6 +30,7 @@ pub struct SectionConfig { id_schema: Arc, parse_section_header: fn(&str) -> Option<(String, String)>, parse_section_content: fn(&str) -> Option<(String, String)>, + format_section_header: fn(type_name: &str, section_id: &str, data: &Value) -> String, } enum ParseState<'a> { @@ -36,22 +40,23 @@ enum ParseState<'a> { #[derive(Debug)] pub struct SectionConfigData { - sections: HashMap, - order: HashMap, + sections: HashMap, + order: LinkedList, } impl SectionConfigData { pub fn new() -> Self { - Self { sections: HashMap::new(), order: HashMap::new() } + Self { sections: HashMap::new(), order: LinkedList::new() } } - pub fn set_data(&mut self, section_id: &str, config: Value) { - self.sections.insert(section_id.to_string(), config); + pub fn set_data(&mut self, section_id: &str, type_name: &str, config: Value) { + // fixme: verify section_id schema here?? + self.sections.insert(section_id.to_string(), (type_name.to_string(), config)); } - fn set_order(&mut self, section_id: &str, pri: usize) { - self.order.insert(section_id.to_string(), pri); + fn record_order(&mut self, section_id: &str) { + self.order.push_back(section_id.to_string()); } } @@ -63,6 +68,7 @@ impl SectionConfig { id_schema: id_schema, parse_section_header: SectionConfig::default_parse_section_header, parse_section_content: SectionConfig::default_parse_section_content, + format_section_header: SectionConfig::default_format_section_header, } } @@ -70,6 +76,61 @@ impl SectionConfig { self.plugins.insert(plugin.type_name.clone(), plugin); } + pub fn write(&self, filename: &str, config: &SectionConfigData) -> Result<(), Error> { + + let mut list = LinkedList::new(); + + let mut done = HashSet::new(); + + for id in &config.order { + if config.sections.get(id) == None { continue }; + list.push_back(id); + done.insert(id); + } + + for (id, _) in &config.sections { + if done.contains(id) { continue }; + list.push_back(id); + } + + let mut raw = String::new(); + + for id in list { + let (type_name, section_config) = config.sections.get(id).unwrap(); + let plugin = self.plugins.get(type_name).unwrap(); + + // fixme: verify json data + println!("REAL WRITE {} {} {:?}\n", id, type_name, section_config); + + let head = (self.format_section_header)(type_name, id, section_config); + + if !raw.is_empty() { raw += "\n" } + + raw += &head; + + for (key, value) in section_config.as_object().unwrap() { + let text = match value { + Value::Null => { continue; }, // do nothing ? + Value::Bool(bv) => bv.to_string(), + Value::String(str) => str.to_string(), + Value::Number(num) => num.to_string(), + _ => { + bail!("file {}: got unsupported type in section {} key {}", filename, id, key); + }, + }; + raw += "\t"; + raw += &key; + raw += " "; + raw += &text; + raw += "\n"; + } + println!("CONFIG:\n{}", raw); + + } + + Ok(()) + } + pub fn parse(&self, filename: &str, raw: &str) -> Result { let mut line_no = 0; @@ -87,18 +148,14 @@ impl SectionConfig { let mut result = SectionConfigData::new(); - let mut pri = 1; - let mut create_section = |section_id: &str, config| { - result.set_data(section_id, config); - result.set_order(section_id, pri); - pri += 1; + let mut create_section = |section_id: &str, type_name: &str, config| { + result.set_data(section_id, type_name, config); + result.record_order(section_id); }; for line in raw.lines() { line_no += 1; - if line.trim().is_empty() { continue; } - match state { ParseState::BeforeHeader => { @@ -128,7 +185,7 @@ impl SectionConfig { if let Err(err) = test_required_properties(config, &plugin.properties) { bail!("file '{}' line {} - {}", filename, line_no, err.to_string()); } - create_section(section_id, config.take()); + create_section(section_id, &plugin.type_name, config.take()); state = ParseState::BeforeHeader; continue; } @@ -163,15 +220,20 @@ impl SectionConfig { if let ParseState::InsideSection(plugin, section_id, config) = state { // finish section + if let Err(err) = test_required_properties(&config, &plugin.properties) { bail!("file '{}' line {} - {}", filename, line_no, err.to_string()); } - create_section(§ion_id, config); + create_section(§ion_id, &plugin.type_name, config); } Ok(result) } + pub fn default_format_section_header(type_name: &str, section_id: &str, data: &Value) -> String { + return format!("{}: {}\n", type_name, section_id); + } + pub fn default_parse_section_content(line: &str) -> Option<(String, String)> { if line.is_empty() { return None; } @@ -258,9 +320,15 @@ lvmthin: local-lvm thinpool data vgname pve5 content rootdir,images + +lvmthin: local-lvm2 + thinpool data + vgname pve5 + content rootdir,images "; let res = config.parse(filename, &raw); println!("RES: {:?}", res); + config.write(filename, &res.unwrap()); }