From 4a154b3cb559336d28a45fb4978a8548044e3408 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Mon, 5 Aug 2024 11:30:32 +0200 Subject: [PATCH] section-config: support a type_key property For when the underlying datatype is supposed to contain the type property and the schema does not mark it as optional. The use case here is to support flat `Remote` type where the "type" of pve/pmg/pbs is a property which is present in the `Remote` struct while being derived from the section type. This will implicitly include and strip the type of the json object after/before de/serializing. Alternatives would be - to mark the type as optional and just fill it out later when loading the data, but that is technically wrong... - have a 2nd version of the struct with the type field removed and From/Into implemented, but that's even more unwieldy. Signed-off-by: Wolfgang Bumiller --- proxmox-section-config/src/lib.rs | 44 ++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/proxmox-section-config/src/lib.rs b/proxmox-section-config/src/lib.rs index 633ca49e..351f53bd 100644 --- a/proxmox-section-config/src/lib.rs +++ b/proxmox-section-config/src/lib.rs @@ -43,6 +43,7 @@ pub struct SectionConfigPlugin { type_name: String, properties: &'static (dyn ObjectSchemaType + Send + Sync + 'static), id_property: Option, + type_key: Option<&'static str>, } impl SectionConfigPlugin { @@ -55,9 +56,16 @@ impl SectionConfigPlugin { type_name, properties, id_property, + type_key: None, } } + /// Override the type key for this plugin. + pub const fn with_type_key(mut self, type_key: &'static str) -> Self { + self.type_key = Some(type_key); + self + } + pub fn type_name(&self) -> &str { &self.type_name } @@ -97,6 +105,7 @@ pub struct SectionConfig { fn(type_name: &str, section_id: &str, key: &str, value: &Value) -> Result, allow_unknown_sections: bool, + type_key: Option<&'static str>, } enum ParseState<'a> { @@ -247,6 +256,7 @@ impl SectionConfig { format_section_header: Self::default_format_section_header, format_section_content: Self::default_format_section_content, allow_unknown_sections: false, + type_key: None, } } @@ -260,6 +270,7 @@ impl SectionConfig { format_section_header: Self::systemd_format_section_header, format_section_content: Self::systemd_format_section_content, allow_unknown_sections: false, + type_key: None, } } @@ -288,6 +299,7 @@ impl SectionConfig { format_section_header, format_section_content, allow_unknown_sections: false, + type_key: None, } } @@ -296,6 +308,12 @@ impl SectionConfig { self } + /// The default type key for all and unknown section types. + pub const fn with_type_key(mut self, type_key: &'static str) -> Self { + self.type_key = Some(type_key); + self + } + /// Register a plugin, which defines the `Schema` for a section type. pub fn register_plugin(&mut self, plugin: SectionConfigPlugin) { self.plugins.insert(plugin.type_name.clone(), plugin); @@ -365,10 +383,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 - } + if plugin.id_property.as_deref() == Some(key) + || plugin.type_key == Some(key) + || (plugin.type_key.is_none() && self.type_key == Some(key)) + { + continue; // id and type are part of the section header } raw += &(self.format_section_content)(type_name, section_id, key, value)?; } @@ -449,7 +468,15 @@ impl SectionConfig { (self.parse_section_header)(line) { //println!("OKLINE: type: {} ID: {}", section_type, section_id); + if let Some(plugin) = self.plugins.get(§ion_type) { + let section_data = + if let Some(type_key) = plugin.type_key.or(self.type_key) { + json!({type_key: section_type}) + } else { + json!({}) + }; + let id_schema = plugin.get_id_schema().unwrap_or(self.id_schema); if let Err(err) = id_schema.parse_simple_value(§ion_id) { @@ -459,12 +486,17 @@ impl SectionConfig { ); } state = - ParseState::InsideSection(plugin, section_id, json!({})); + ParseState::InsideSection(plugin, section_id, section_data); } else if self.allow_unknown_sections { + let section_data = if let Some(type_key) = self.type_key { + json!({type_key: section_type}) + } else { + json!({}) + }; state = ParseState::InsideUnknownSection( section_type, section_id, - json!({}), + section_data, ); } else { bail!("unknown section type '{}'", section_type);