From e7a5768e04bb291fb1fa4d89f686f6c19e65209f Mon Sep 17 00:00:00 2001 From: Dominik Csapak Date: Mon, 18 May 2020 14:18:33 +0200 Subject: [PATCH] proxmox: section_config: add id_property to SectionConfigPlugin if set in the SectionConfigPlugin, we set the given field to the section_id for each entry this way we can have e.g. the userid in the Struct and ObjectSchema, without having to write it twice to the config but we are still getting it when parsing the config when a plugin does not specify an id_property, then the id_schema of the SectionConfig will be used as fallback also adds a test for this with multiple plugins Signed-off-by: Dominik Csapak --- proxmox/src/api/section_config.rs | 108 +++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 11 deletions(-) diff --git a/proxmox/src/api/section_config.rs b/proxmox/src/api/section_config.rs index 527a2d3a..3a42a435 100644 --- a/proxmox/src/api/section_config.rs +++ b/proxmox/src/api/section_config.rs @@ -35,12 +35,26 @@ use crate::try_block; pub struct SectionConfigPlugin { type_name: String, properties: &'static ObjectSchema, + id_property: Option, } impl SectionConfigPlugin { - pub fn new(type_name: String, properties: &'static ObjectSchema) -> Self { - Self { type_name, properties } + pub fn new(type_name: String, id_property: Option, properties: &'static ObjectSchema) -> Self { + Self { type_name, properties, id_property } + } + + pub fn get_id_schema(&self) -> Option<&Schema> { + match &self.id_property { + Some(id_prop) => { + if let Some((_, schema)) = self.properties.lookup(&id_prop) { + Some(schema) + } else { + None + } + }, + None => None, + } } } @@ -228,7 +242,11 @@ impl SectionConfig { let (type_name, section_config) = config.sections.get(section_id).unwrap(); let plugin = self.plugins.get(type_name).unwrap(); - if let Err(err) = parse_simple_value(§ion_id, &self.id_schema) { + let id_schema = match plugin.get_id_schema() { + Some(schema) => schema, + None => &self.id_schema, + }; + if let Err(err) = parse_simple_value(§ion_id, &id_schema) { bail!("syntax error in section identifier: {}", err.to_string()); } if section_id.chars().any(|c| c.is_control()) { @@ -243,6 +261,11 @@ impl SectionConfig { raw += &(self.format_section_header)(type_name, section_id, section_config)?; for (key, value) in section_config.as_object().unwrap() { + if let Some(id_property) = &plugin.id_property { + if id_property == key { + continue; // skip writing out id properties, they are in the section header + } + } raw += &(self.format_section_content)(type_name, section_id, key, value)?; } } @@ -260,8 +283,14 @@ impl SectionConfig { let mut state = ParseState::BeforeHeader; - let test_required_properties = |value: &Value, schema: &ObjectSchema| -> Result<(), Error> { + let test_required_properties = |value: &Value, schema: &ObjectSchema, id_property: &Option| -> Result<(), Error> { for (name, optional, _prop_schema) in schema.properties { + if let Some(id_property) = id_property { + if name == id_property { + // the id_property is the section header, skip for requirement check + continue; + } + } if *optional == false && value[name] == Value::Null { return Err(format_err!("property '{}' is missing and it is not optional.", name)); } @@ -288,8 +317,10 @@ impl SectionConfig { if let Some((section_type, section_id)) = (self.parse_section_header)(line) { //println!("OKLINE: type: {} ID: {}", section_type, section_id); if let Some(ref plugin) = self.plugins.get(§ion_type) { - if let Err(err) = parse_simple_value(§ion_id, &self.id_schema) { - bail!("syntax error in section identifier: {}", err.to_string()); + if let Some(id_schema) = plugin.get_id_schema() { + if let Err(err) = parse_simple_value(§ion_id, id_schema) { + bail!("syntax error in section identifier: {}", err.to_string()); + } } state = ParseState::InsideSection(plugin, section_id, json!({})); } else { @@ -303,7 +334,10 @@ impl SectionConfig { if line.trim().is_empty() { // finish section - test_required_properties(config, &plugin.properties)?; + test_required_properties(config, &plugin.properties, &plugin.id_property)?; + if let Some(id_property) = &plugin.id_property { + config[id_property] = Value::from(section_id.clone()); + } result.set_data(section_id, &plugin.type_name, config.take())?; result.record_order(section_id); @@ -347,9 +381,12 @@ impl SectionConfig { } } - if let ParseState::InsideSection(plugin, section_id, config) = state { + if let ParseState::InsideSection(plugin, ref mut section_id, ref mut config) = state { // finish section - test_required_properties(&config, &plugin.properties)?; + test_required_properties(&config, &plugin.properties, &plugin.id_property)?; + if let Some(id_property) = &plugin.id_property { + config[id_property] = Value::from(section_id.clone()); + } result.set_data(§ion_id, &plugin.type_name, config)?; result.record_order(§ion_id); } @@ -541,12 +578,11 @@ fn test_section_config1() { ], ); - let plugin = SectionConfigPlugin::new("lvmthin".to_string(), &PROPERTIES); + let plugin = SectionConfigPlugin::new("lvmthin".to_string(), None, &PROPERTIES); const ID_SCHEMA: Schema = StringSchema::new("Storage ID schema.") .min_length(3) .schema(); - let mut config = SectionConfig::new(&ID_SCHEMA); config.register_plugin(plugin); @@ -570,3 +606,53 @@ lvmthin: local-lvm2 } + +// cargo test test_section_config2 -- --nocapture +#[test] +fn test_section_config2() { + + let filename = "user.cfg"; + + const ID_SCHEMA: Schema = StringSchema::new("default id schema.") + .min_length(3) + .schema(); + let mut config = SectionConfig::new(&ID_SCHEMA); + + const USER_PROPERTIES: ObjectSchema = ObjectSchema::new( + "user properties", + &[ + ("email", false, &StringSchema::new("The e-mail of the user").schema()), + ("userid", true, &StringSchema::new("The id of the user (name@realm).").min_length(3).schema()), + ], + ); + + const GROUP_PROPERTIES: ObjectSchema = ObjectSchema::new( + "group properties", + &[ + ("comment", false, &StringSchema::new("Comment").schema()), + ("groupid", true, &StringSchema::new("The id of the group.").min_length(3).schema()), + ], + ); + + let plugin = SectionConfigPlugin::new("user".to_string(), Some("userid".to_string()), &USER_PROPERTIES); + config.register_plugin(plugin); + + let plugin = SectionConfigPlugin::new("group".to_string(), Some("groupid".to_string()), &GROUP_PROPERTIES); + config.register_plugin(plugin); + + let raw = r" + +user: root@pam + email root@example.com + +group: mygroup + comment a very important group +"; + + let res = config.parse(filename, &raw); + println!("RES: {:?}", res); + let raw = config.write(filename, &res.unwrap()); + println!("CONFIG:\n{}", raw.unwrap()); + + +}