From c6ce326d20b5dd564c0765cfa181a577c451f741 Mon Sep 17 00:00:00 2001 From: licunlong Date: Fri, 3 Nov 2023 16:42:27 +0800 Subject: [PATCH] fix: refactor unit_parser --- core/coms/service/src/config.rs | 11 +- core/coms/service/src/mng.rs | 2 +- core/coms/service/src/rentry.rs | 44 ++-- core/coms/service/src/unit.rs | 3 +- core/coms/socket/src/config.rs | 4 +- core/coms/socket/src/rentry.rs | 30 +-- core/sysmaster/src/manager/pre_install.rs | 13 +- core/sysmaster/src/unit/entry/config.rs | 6 +- core/sysmaster/src/unit/rentry.rs | 28 +- core/sysmaster/src/unit/util/unit_file.rs | 65 +++-- libs/macros/src/unit_parser/attribute.rs | 110 +------- libs/macros/src/unit_parser/entry.rs | 204 ++++----------- libs/macros/src/unit_parser/section.rs | 240 +++--------------- .../src/unit_parser/transform_default.rs | 1 + libs/macros/src/unit_parser/unit.rs | 64 ++--- libs/unit_parser/examples/dropins.rs | 36 --- libs/unit_parser/examples/example.rs | 81 ------ libs/unit_parser/examples/specifiers.rs | 100 -------- libs/unit_parser/examples/subdir.rs | 27 -- libs/unit_parser/examples/template.rs | 30 --- libs/unit_parser/src/config.rs | 155 ++--------- libs/unit_parser/src/parser.rs | 33 +-- libs/unit_parser/src/specifiers.rs | 67 ++--- 23 files changed, 275 insertions(+), 1079 deletions(-) delete mode 100644 libs/unit_parser/examples/dropins.rs delete mode 100644 libs/unit_parser/examples/example.rs delete mode 100644 libs/unit_parser/examples/specifiers.rs delete mode 100644 libs/unit_parser/examples/subdir.rs delete mode 100644 libs/unit_parser/examples/template.rs diff --git a/core/coms/service/src/config.rs b/core/coms/service/src/config.rs index 69da1600..e69ba089 100644 --- a/core/coms/service/src/config.rs +++ b/core/coms/service/src/config.rs @@ -66,11 +66,9 @@ impl ServiceConfig { } } - pub(super) fn load(&self, paths: Vec, update: bool) -> Result<()> { - let name = paths[0].file_name().unwrap().to_string_lossy().to_string(); - let paths = paths.iter().map(|x| x.parent().unwrap()).collect(); + pub(super) fn load(&self, paths: Vec, name: &str, update: bool) -> Result<()> { log::debug!("Loading {} config from: {:?}", name, paths); - let service_config = match ServiceConfigData::load_named(paths, name, true) { + let service_config = match ServiceConfigData::load_config(paths, name) { Ok(v) => v, Err(e) => { log::error!("Invalid Configuration: {}", e); @@ -158,7 +156,6 @@ fn specifier_escape_exec_command( #[derive(UnitConfig, Default, Debug)] pub(super) struct ServiceConfigData { - #[section(must)] pub Service: SectionService, } @@ -239,7 +236,7 @@ mod tests { let comm = Rc::new(ServiceUnitComm::new()); let config = ServiceConfig::new(&comm); - let result = config.load(paths, false); + let result = config.load(paths, "config.service", false); println!("service data: {:?}", config.config_data()); @@ -255,7 +252,7 @@ mod tests { let comm = Rc::new(ServiceUnitComm::new()); let config = ServiceConfig::new(&comm); - assert!(config.load(paths, false).is_ok()); + assert!(config.load(paths, "config.service", false).is_ok()); assert_eq!(config.service_type(), ServiceType::Simple) } diff --git a/core/coms/service/src/mng.rs b/core/coms/service/src/mng.rs index fb2432d9..47c2cf00 100644 --- a/core/coms/service/src/mng.rs +++ b/core/coms/service/src/mng.rs @@ -2645,7 +2645,7 @@ mod tests { let config = Rc::new(ServiceConfig::new(&comm)); let context = Rc::new(ExecContext::new()); - let result = config.load(paths, false); + let result = config.load(paths, "config.service", false); assert!(result.is_ok()); let rt = Rc::new(RunningData::new(&comm)); diff --git a/core/coms/service/src/rentry.rs b/core/coms/service/src/rentry.rs index e35e7cd4..b4f18e58 100644 --- a/core/coms/service/src/rentry.rs +++ b/core/coms/service/src/rentry.rs @@ -508,51 +508,51 @@ fn parse_environment(s: &str) -> Result> { pub struct SectionService { #[entry(default=ServiceType::Simple)] pub Type: ServiceType, - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecStart: Vec, - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecStartPre: Vec, - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecStartPost: Vec, - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecStop: Vec, - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecStopPost: Vec, - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecReload: Vec, - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecCondition: Vec, - #[entry(multiple)] + #[entry(append)] pub Sockets: Vec, #[entry(default = 0)] pub WatchdogSec: u64, - #[entry(myparser = deserialize_pidfile)] + #[entry(parser = deserialize_pidfile)] pub PIDFile: Option, #[entry(default = false)] pub RemainAfterExit: bool, pub NotifyAccess: Option, #[entry(default = false)] pub NonBlocking: bool, - #[entry(myparser = parse_environment)] + #[entry(parser = parse_environment)] pub Environment: Option>, #[entry(default = KillMode::ControlGroup)] pub KillMode: KillMode, pub SELinuxContext: Option, - #[entry(myparser = deserialize_pathbuf)] + #[entry(parser = deserialize_pathbuf)] pub RootDirectory: Option, - #[entry(default = WorkingDirectory::default(), myparser = parse_working_directory)] + #[entry(default = WorkingDirectory::default(), parser = parse_working_directory)] pub WorkingDirectory: WorkingDirectory, - #[entry(default = StateDirectory::default(), myparser = deserialize_state_directory)] + #[entry(default = StateDirectory::default(), parser = deserialize_state_directory)] pub StateDirectory: StateDirectory, - #[entry(default = RuntimeDirectory::default(), myparser = deserialize_runtime_directory)] + #[entry(default = RuntimeDirectory::default(), parser = deserialize_runtime_directory)] pub RuntimeDirectory: RuntimeDirectory, #[entry(default = PreserveMode::No)] pub RuntimeDirectoryPreserve: PreserveMode, - #[entry(default = "")] + #[entry(default = String::new())] pub User: String, - #[entry(default = "")] + #[entry(default = String::new())] pub Group: String, - #[entry(default = "0022")] + #[entry(default = String::from("0022"))] pub UMask: String, #[entry(default = ServiceRestart::No)] pub Restart: ServiceRestart, @@ -560,15 +560,15 @@ pub struct SectionService { pub RestartPreventExitStatus: ExitStatusSet, #[entry(default = 1)] pub RestartSec: u64, - #[entry(multiple)] + #[entry(append)] pub EnvironmentFile: Vec, - #[entry(default = "SIGTERM")] + #[entry(default = String::from("SIGTERM"))] pub KillSignal: String, - #[entry(default = 10000000, myparser = deserialize_timeout)] + #[entry(default = 10000000, parser = deserialize_timeout)] pub TimeoutSec: u64, - #[entry(default = 10000000, myparser = deserialize_timeout)] + #[entry(default = 10000000, parser = deserialize_timeout)] pub TimeoutStartSec: u64, - #[entry(default = 10000000, myparser = deserialize_timeout)] + #[entry(default = 10000000, parser = deserialize_timeout)] pub TimeoutStopSec: u64, pub LimitCORE: Option, pub LimitNOFILE: Option, diff --git a/core/coms/service/src/unit.rs b/core/coms/service/src/unit.rs index 09d50753..4c7db89c 100644 --- a/core/coms/service/src/unit.rs +++ b/core/coms/service/src/unit.rs @@ -85,7 +85,8 @@ impl SubUnit for ServiceUnit { } fn load(&self, paths: Vec) -> Result<()> { - self.config.load(paths, true)?; + let unit_name = self.comm.get_owner_id(); + self.config.load(paths, &unit_name, true)?; self.parse()?; diff --git a/core/coms/socket/src/config.rs b/core/coms/socket/src/config.rs index 9585d5f9..c53e275a 100644 --- a/core/coms/socket/src/config.rs +++ b/core/coms/socket/src/config.rs @@ -134,8 +134,7 @@ impl SocketConfig { pub(super) fn load(&self, paths: Vec, update: bool) -> Result<()> { let name = paths[0].file_name().unwrap().to_string_lossy().to_string(); - let paths = paths.iter().map(|x| x.parent().unwrap()).collect(); - let data = match SocketConfigData::load_named(paths, name, true) { + let data = match SocketConfigData::load_config(paths, &name) { Ok(v) => v, Err(e) => { log::error!("Invalid Configuration: {}", e); @@ -300,7 +299,6 @@ enum ListenItem { #[derive(UnitConfig, Default, Debug)] pub(crate) struct SocketConfigData { - #[section(must)] pub Socket: SectionSocket, } diff --git a/core/coms/socket/src/rentry.rs b/core/coms/socket/src/rentry.rs index 9f9bcb54..baa7c6df 100644 --- a/core/coms/socket/src/rentry.rs +++ b/core/coms/socket/src/rentry.rs @@ -45,28 +45,28 @@ fn deserialize_pathbuf_vec(s: &str) -> Result, core::error::Error> #[derive(UnitSection, Default, Clone, Debug, Serialize, Deserialize)] #[allow(dead_code)] pub(super) struct SectionSocket { - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecStartPre: Vec, - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecStartChown: Vec, - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecStartPost: Vec, - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecStopPre: Vec, - #[entry(multiple, myparser = core::exec::deserialize_exec_command)] + #[entry(append, parser = core::exec::deserialize_exec_command)] pub ExecStopPost: Vec, - #[entry(multiple)] + #[entry(append)] pub ListenStream: Vec, - #[entry(multiple)] + #[entry(append)] pub ListenDatagram: Vec, - #[entry(multiple)] + #[entry(append)] pub ListenNetlink: Vec, - #[entry(multiple)] + #[entry(append)] pub ListenSequentialPacket: Vec, - #[entry(multiple)] + #[entry(append)] pub ListenFIFO: Vec, - #[entry(multiple)] + #[entry(append)] pub ListenSpecial: Vec, #[entry(default = false)] @@ -85,18 +85,18 @@ pub(super) struct SectionSocket { pub Broadcast: Option, #[entry(default = false)] pub RemoveOnStop: bool, - #[entry(multiple, myparser = deserialize_pathbuf_vec)] + #[entry(append, parser = deserialize_pathbuf_vec)] pub Symlinks: Vec, pub PassSecurity: Option, #[entry(default = 0o666)] pub SocketMode: u32, - #[entry(default = "")] + #[entry(default = String::new())] pub SocketUser: String, - #[entry(default = "")] + #[entry(default = String::new())] pub SocketGroup: String, #[entry(default = KillMode::ControlGroup)] pub KillMode: KillMode, - #[entry(default = "SIGTERM")] + #[entry(default = String::from("SIGTERM"))] pub KillSignal: String, } diff --git a/core/sysmaster/src/manager/pre_install.rs b/core/sysmaster/src/manager/pre_install.rs index f69e42d2..18cadb90 100644 --- a/core/sysmaster/src/manager/pre_install.rs +++ b/core/sysmaster/src/manager/pre_install.rs @@ -541,11 +541,14 @@ impl Install { unit_install.set_u_type(UnitFileType::Symlink); } - let configer = match UeConfigData::load_named( - self.lookup_path.search_path.clone(), - unit_install.name(), - true, - ) { + let mut paths: Vec = Vec::new(); + for p in &self.lookup_path.search_path { + let mut path = String::new(); + path = path + p + &unit_install.name(); + paths.push(PathBuf::from(path)); + } + + let configer = match UeConfigData::load_config(paths, &unit_install.name()) { Err(unit_parser::error::Error::LoadTemplateError { name: _ }) => { return Err(Error::LoadError { msg: format!( diff --git a/core/sysmaster/src/unit/entry/config.rs b/core/sysmaster/src/unit/entry/config.rs index 2d37c944..d336d68b 100644 --- a/core/sysmaster/src/unit/entry/config.rs +++ b/core/sysmaster/src/unit/entry/config.rs @@ -161,9 +161,7 @@ impl UeConfig { return Err(format!("{} doesn't have corresponding config file", name).into()); } - // fragment - let unit_conf_frag = unit_conf_frag.iter().map(|x| x.parent().unwrap()).collect(); - let mut configer = match UeConfigData::load_named(unit_conf_frag, name, true) { + let mut configer = match UeConfigData::load_config(unit_conf_frag, name) { Ok(v) => v, Err(e) => { log::error!("Invalid Configuration: {}", e); @@ -269,7 +267,7 @@ mod tests { let mut file_path = get_project_root().unwrap(); file_path.push("tests/test_units"); println!("{:?}", file_path); - let result = UeConfigData::load_named(vec![file_path], "config.service", true).unwrap(); + let result = UeConfigData::load_config(vec![file_path], "config.service").unwrap(); println!("{:?}", result); } diff --git a/core/sysmaster/src/unit/rentry.rs b/core/sysmaster/src/unit/rentry.rs index 21eddd28..351ed815 100644 --- a/core/sysmaster/src/unit/rentry.rs +++ b/core/sysmaster/src/unit/rentry.rs @@ -155,25 +155,25 @@ pub(crate) struct UeConfigUnit { pub OnFailureJobMode: JobMode, #[entry(default = JobMode::Replace)] pub OnSuccessJobMode: JobMode, - #[entry(multiple)] + #[entry(append)] pub Wants: Vec, - #[entry(multiple)] + #[entry(append)] pub Requires: Vec, - #[entry(multiple)] + #[entry(append)] pub BindsTo: Vec, - #[entry(multiple)] + #[entry(append)] pub Requisite: Vec, - #[entry(multiple)] + #[entry(append)] pub PartOf: Vec, - #[entry(multiple)] + #[entry(append)] pub OnFailure: Vec, - #[entry(multiple)] + #[entry(append)] pub OnSuccess: Vec, - #[entry(multiple)] + #[entry(append)] pub Before: Vec, - #[entry(multiple)] + #[entry(append)] pub After: Vec, - #[entry(multiple)] + #[entry(append)] pub Conflicts: Vec, /* Conditions */ @@ -233,13 +233,13 @@ pub(crate) struct UeConfigUnit { #[derive(UnitSection, Default, Clone, Debug, Serialize, Deserialize)] pub struct UeConfigInstall { - #[entry(multiple)] + #[entry(append)] pub Alias: Vec, - #[entry(multiple)] + #[entry(append)] pub WantedBy: Vec, - #[entry(multiple)] + #[entry(append)] pub RequiredBy: Vec, - #[entry(multiple)] + #[entry(append)] pub Also: Vec, } diff --git a/core/sysmaster/src/unit/util/unit_file.rs b/core/sysmaster/src/unit/util/unit_file.rs index 5fc4b1e6..ef623edc 100644 --- a/core/sysmaster/src/unit/util/unit_file.rs +++ b/core/sysmaster/src/unit/util/unit_file.rs @@ -133,27 +133,32 @@ impl UnitFileData { } } - fn build_id_fragment_by_name(&mut self, path: &str, name: &str) -> Option> { + fn search_dropin_fragment(&mut self, path: &str, name: &str) -> Vec { let mut res: Vec = Vec::new(); - if fs::metadata(path).is_err() { - return None; - } - /* {/etc/sysmaster/system, /usr/lib/sysmaster/system}/foo.service.d */ let pathd_str = format!("{}/{}.d", path, name); let dir = Path::new(&pathd_str); - if dir.is_dir() { - for entry in dir.read_dir().unwrap() { - let fragment = entry.unwrap().path(); - if !fragment.is_file() { - continue; - } - let file_name = String::from(fragment.file_name().unwrap().to_str().unwrap()); - if file_name.starts_with('.') || file_name.ends_with(".toml") { - continue; - } + if !dir.is_dir() { + return res; + } + for entry in dir.read_dir().unwrap() { + let fragment = entry.unwrap().path(); + if !fragment.is_file() { + continue; + } + let file_name = String::from(fragment.file_name().unwrap().to_str().unwrap()); + if file_name.ends_with(".conf") { res.push(fragment); } } + res + } + + fn build_id_fragment_by_name(&mut self, path: &str, name: &str) -> Option> { + let mut res: Vec = Vec::new(); + if fs::metadata(path).is_err() { + return None; + } + /* {/etc/sysmater/system, /usr/lib/sysmaster/system}/foo.service */ let config_path = Path::new(path).join(name); if !config_path.exists() { @@ -198,6 +203,8 @@ impl UnitFileData { /* We are processing an alias service. */ if file_name == name { if !unit_name_is_valid(&target_name, UnitNameFlags::ANY) { + /* So this symlink is pointing an invalid unit, mark the vector as empty and + * we will treat it as masked. */ return Some(Vec::new()); } self.real_name = target_name; @@ -227,9 +234,20 @@ impl UnitFileData { return; } pathbuf_fragment.append(&mut v); + /* One is enough. */ + break; } if !pathbuf_fragment.is_empty() || !name.contains('@') { + for search_path in &search_path_list { + let mut v = self.search_dropin_fragment(search_path, name); + if v.is_empty() { + continue; + } + pathbuf_fragment.append(&mut v); + break; + } + self.unit_id_fragment .insert(name.to_string(), pathbuf_fragment); return; @@ -239,7 +257,7 @@ impl UnitFileData { * load the template configuration file. */ let template_name = name.split_once('@').unwrap().0.to_string() + "@.service"; for search_path in &search_path_list { - let v = match self.build_id_fragment_by_name(search_path, &template_name) { + let mut v = match self.build_id_fragment_by_name(search_path, &template_name) { None => continue, Some(v) => v, }; @@ -247,12 +265,19 @@ impl UnitFileData { /* unit is masked */ return; } - /* FIXME: this changes the template_name to full_name, it's a temporary solution. */ - for p in v { - let path = p.parent().unwrap().join(name); - pathbuf_fragment.push(path); + pathbuf_fragment.append(&mut v); + break; + } + + for search_path in &search_path_list { + let mut v = self.search_dropin_fragment(search_path, &template_name); + if v.is_empty() { + continue; } + pathbuf_fragment.append(&mut v); + break; } + self.unit_id_fragment .insert(name.to_string(), pathbuf_fragment); } diff --git a/libs/macros/src/unit_parser/attribute.rs b/libs/macros/src/unit_parser/attribute.rs index 9b760d2f..221b825e 100644 --- a/libs/macros/src/unit_parser/attribute.rs +++ b/libs/macros/src/unit_parser/attribute.rs @@ -4,64 +4,6 @@ use proc_macro2::TokenStream; use quote::ToTokens; use syn::{Attribute, Error, Expr, Field, LitStr, Token, Type}; -/// Attributes valid for [UnitSection]s. -#[derive(Default)] -pub(crate) struct SectionAttributes { - /// Whether fallback to [std::default::Default] is enabled - pub(crate) default: bool, - /// Whether alternative key is specified - pub(crate) key: Option, - /// Whether must-present is specified - pub(crate) must: bool, -} - -impl SectionAttributes { - /// Parses [SectionAttributes] from [syn] tokens. - /// Pass in [syn::Type] to do type check, or pass in [None] to prevent errors from showing up multiple times - pub(crate) fn parse_vec(input: &Field, ty: Option<&Type>) -> syn::Result { - let mut result = SectionAttributes::default(); - for attribute in input.attrs.iter() { - if !attribute.path().is_ident("section") { - continue; - } - attribute.parse_nested_meta(|nested| { - if nested.path.is_ident("default") { - result.default = true; - Ok(()) - } else if nested.path.is_ident("key") { - nested.input.parse::()?; - let value: LitStr = nested.input.parse()?; - result.key = Some(value.into_token_stream()); - Ok(()) - } else if nested.path.is_ident("must") { - result.must = true; - Ok(()) - } else { - Err(Error::new_spanned( - attribute, - "section, Not a valid attribute.", - )) - } - })?; - } - if result.default & result.must { - return Err(Error::new_spanned( - input, - "`default` and `must` cannot co-exist.", - )); - } - if let Some(ty) = ty { - if (!result.must) & (!result.default) & (!is_option(ty)) { - return Err(Error::new_spanned( - input, - "Optional fields should be `Option`s.", - )); - } - } - Ok(result) - } -} - /// Attributes valid for [UnitEntry]s. #[derive(Default)] pub(crate) struct EntryAttributes { @@ -69,14 +11,10 @@ pub(crate) struct EntryAttributes { pub(crate) default: Option, /// Whether alternative key is specified pub(crate) key: Option, - /// Whether multiple-present is specified - pub(crate) multiple: bool, - /// Whether must-present is specified - pub(crate) must: bool, - /// Whether systemd subdir resolve is specified - pub(crate) subdir: Option, + /// Whether append-present is specified + pub(crate) append: bool, /// User's own parser - pub(crate) myparser: Option, + pub(crate) parser: Option, } impl EntryAttributes { @@ -99,59 +37,39 @@ impl EntryAttributes { let value: LitStr = nested.input.parse()?; result.key = Some(value.into_token_stream()); Ok(()) - } else if nested.path.is_ident("multiple") { - result.multiple = true; + } else if nested.path.is_ident("append") { + result.append = true; Ok(()) - } else if nested.path.is_ident("must") { - result.must = true; - Ok(()) - } else if nested.path.is_ident("subdir") { - nested.input.parse::()?; - let value: LitStr = nested.input.parse()?; - result.subdir = Some(value.into_token_stream()); - Ok(()) - } else if nested.path.is_ident("myparser") { + } else if nested.path.is_ident("parser") { nested.input.parse::()?; let value: syn::Path = nested.input.parse()?; - result.myparser = Some(value); + result.parser = Some(value); Ok(()) } else { Err(Error::new_spanned( attribute, - "entry, Not a valid attribute.", + "Not a valid entry attribute.", )) } })?; } - if result.must & result.default.is_some() { - return Err(Error::new_spanned( - input, - "`must` and `default` cannot co-exist.", - )); - } - if result.multiple & result.must { - return Err(Error::new_spanned( - input, - "`must` and `multiple` cannot co-exist.", - )); - } - if (!result.multiple) & result.subdir.is_some() { + if result.append & result.default.is_some() { return Err(Error::new_spanned( input, - "`subdir` attributed fields must be `multiple`.", + "`append` and `default` cannot co-exist.", )); } if let Some(ty) = ty { - if (!result.must) & (result.default.is_none()) & (!result.multiple) & (!is_option(ty)) { + if result.default.is_none() && !result.append && !is_option(ty) { return Err(Error::new_spanned( input, - "Optional fields should be `Option`s.", + "Type must be `Option` if neither default nor append is configured.", )); } - if result.multiple & (!is_vec(ty)) { + if result.append && (!is_vec(ty)) { return Err(Error::new_spanned( input, - "`multiple` attributed fields should be `Vec`s.", + "`append` attributed fields should be `Vec`s.", )); } } diff --git a/libs/macros/src/unit_parser/entry.rs b/libs/macros/src/unit_parser/entry.rs index 7c146c1f..aa8bd58d 100644 --- a/libs/macros/src/unit_parser/entry.rs +++ b/libs/macros/src/unit_parser/entry.rs @@ -1,7 +1,7 @@ //! Functions for generating entry parsing expressions. use crate::{ unit_conf_parse::{get_option_inner_type, get_vec_inner_type}, - unit_parser::{attribute::EntryAttributes, transform_default::transform_default}, + unit_parser::attribute::EntryAttributes, }; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; @@ -13,12 +13,12 @@ pub(crate) fn gen_entry_ensure(field: &Field) -> Result { let mut ty = &field.ty; let attribute = EntryAttributes::parse_attributes(field, None)?; /* No need to check when user has defined his own parser */ - if attribute.myparser.is_some() { + if attribute.parser.is_some() { return Ok(quote! {}); } - if attribute.multiple { + if attribute.append { ty = get_vec_inner_type(ty).unwrap(); - } else if (!attribute.must) & (attribute.default.is_none()) { + } else if attribute.default.is_none() { ty = get_option_inner_type(ty).unwrap(); } Ok(quote! { @@ -34,20 +34,26 @@ pub(crate) fn gen_entry_ensure(field: &Field) -> Result { /// ``` /// let mut Field1 = None; /// ``` -pub(crate) fn gen_entry_init(field: &Field) -> Result { +pub(crate) fn gen_entry_default(field: &Field) -> Result { let name = field .ident .as_ref() .ok_or_else(|| Error::new_spanned(field, "Tuple structs are not supported."))?; + let _ty = &field.ty; let attributes = EntryAttributes::parse_attributes(field, None)?; - Ok(match attributes.multiple { - false => quote! { - let mut #name = None; - }, - true => quote! { - let mut #name = Vec::new(); - }, - }) + if attributes.append { + return Ok(quote! { + __res.#name = Vec::new(); + }); + } + match attributes.default { + None => Ok(quote! { + __res.#name = None; + }), + Some(v) => Ok(quote! { + __res.#name = #v; + }), + } } /// Generate entry parsing statements, in the form of an arm in a `match` statement. @@ -74,40 +80,19 @@ pub(crate) fn gen_entry_parse(field: &Field) -> Result { .key .unwrap_or_else(|| (format!("{}", name)).into_token_stream()); - if !attributes.multiple && attributes.subdir.is_some() { - return Err(Error::new_spanned( - field, - "\'subdir\' can only be used when \'multiple\' is true.", - )); - } - - if attributes.multiple && attributes.must { - return Err(Error::new_spanned( - field, - "\'must\' should be false when \'multiple\' is true.", - )); - } - - if (attributes.must || attributes.multiple) && attributes.default.is_some() { - return Err(Error::new_spanned( - field, - "\'default\' can only be used when \'must\' and \'multiple\' are false.", - )); - } - - if attributes.multiple { + if attributes.append { let clear_when_empty = quote! { if __pair.1.as_str().is_empty() { - #name.clear(); + __res.#name.clear(); continue; } }; - /* myparser */ - let parser = match attributes.myparser { + /* parser */ + let parser = match attributes.parser { Some(myparser) => quote! { match #myparser(__pair.1.as_str()) { Ok(__inner) => { - #name.extend(__inner); + __res.#name.extend(__inner); } Err(_) => { log::warn!("Failed to parse {} for key {}, ignoring.", __pair.0, __pair.1); @@ -118,7 +103,7 @@ pub(crate) fn gen_entry_parse(field: &Field) -> Result { for __part in __pair.1.split_ascii_whitespace(){ match unit_parser::internal::UnitEntry::parse_from_str(__part){ Ok(__inner) => { - #name.push(__inner); + __res.#name.push(__inner); } Err(_) => { log::warn!("Failed to parse {} for key {}, ignoring.", __pair.0, __pair.1); @@ -127,47 +112,29 @@ pub(crate) fn gen_entry_parse(field: &Field) -> Result { } }, }; - /* subdir */ - match attributes.subdir { - None => { - return Ok(quote! { - #key => { - #clear_when_empty - #parser - } - }) + return Ok(quote! { + #key => { + #clear_when_empty + #parser } - Some(v) => { - return Ok(quote! { - #key => { - #clear_when_empty - #parser - let __subdirs = __source.__parse_subdir(#v); - #name.extend_from_slice(&__subdirs); - } - }) - } - } + }); } - - let parser = match (attributes.must, attributes.myparser) { - (true, Some(entry_parser)) => quote! { - let __value = #entry_parser(__pair.1.as_str()).unwrap(); - #name = Some(__value); + let apply_value = match attributes.default { + None => quote! { + __res.#name = Some(__value); }, - (true, None) => quote! { - let __value = unit_parser::internal::UnitEntry::parse_from_str(__pair.1.as_str()).unwrap(); - #name = Some(__value); + Some(_) => quote! { + __res.#name = __value; }, - (false, Some(entry_parser)) => quote! { - if let Ok(__value) = #entry_parser(__pair.1.as_str()) { - #name = Some(__value); - } + }; + let parser = match attributes.parser { + Some(entry_parser) => quote! { + let __value = #entry_parser(__pair.1.as_str()).unwrap(); + #apply_value; }, - (false, None) => quote! { - if let Ok(__value) = unit_parser::internal::UnitEntry::parse_from_str(__pair.1.as_str()) { - #name = Some(__value) - } + None => quote! { + let __value = unit_parser::internal::UnitEntry::parse_from_str(__pair.1.as_str()).unwrap(); + #apply_value; }, }; @@ -178,51 +145,6 @@ pub(crate) fn gen_entry_parse(field: &Field) -> Result { }) } -/// Generate finalization statements which are in charge of processing [Option] and [Result]s during parsing. -pub(crate) fn gen_entry_finalize(field: &Field) -> Result { - let name = field - .ident - .as_ref() - .ok_or_else(|| Error::new_spanned(field, "Tuple structs are not supported."))?; - let ty = &field.ty; - let attributes = EntryAttributes::parse_attributes(field, None)?; - let key = attributes - .key - .unwrap_or_else(|| (format!("{}", name)).into_token_stream()); - - let result = match (attributes.default, attributes.multiple, attributes.must) { - // invalid - (Some(_), _, true) | (_, true, true) => unreachable!(), - // apply default if empty - (Some(default), true, false) => { - quote! { - if #name.is_empty() { - #name = #default; - } - } - } - // leave unchanged (`Vec` and `Option`) - (None, true, false) | (None, false, false) => { - quote! {} - } - // unwrap to default - (Some(default), false, false) => { - let default = transform_default(ty, &default)?; - quote! { - let #name = #name.unwrap_or(#default); - } - } - // throw Error - (None, false, true) => { - quote! { - let #name = #name.ok_or_else(|| { - unit_parser::internal::Error::EntryMissingError { key: #key.to_string()}})?; - } - } - }; - Ok(result) -} - /// Generate implementation statements for custom enums. /// Uses each variant's name as their value. pub(crate) fn gen_entry_derives(input: DeriveInput) -> Result { @@ -258,43 +180,3 @@ pub(crate) fn gen_entry_derives(input: DeriveInput) -> Result { )) } } - -/// Generate patching statements that sets each field to a new value, if present. -/// Append the new value to the [Vec] if possible. -pub(crate) fn gen_entry_patch(field: &Field) -> Result { - let name = field - .ident - .as_ref() - .ok_or_else(|| Error::new_spanned(field, "Tuple structs are not supported."))?; - let attributes = EntryAttributes::parse_attributes(field, None)?; - - let result = match (attributes.must, attributes.multiple, attributes.default) { - // invalid - (true, _, Some(_)) | (true, true, _) => unreachable!(), - // append - // TODO: or should it overwrite? - (false, true, _) => { - quote! { - __from.#name.extend_from_slice(&#name); - } - } - // set (as is) if not None - (false, false, None) => { - quote! { - if #name.is_some() { - __from.#name = #name; - } - } - } - // set if not None - (_, false, _) => { - quote! { - if let Some(__inner) = #name { - __from.#name = __inner; - } - } - } - }; - - Ok(result) -} diff --git a/libs/macros/src/unit_parser/section.rs b/libs/macros/src/unit_parser/section.rs index f50ded6e..012f1060 100644 --- a/libs/macros/src/unit_parser/section.rs +++ b/libs/macros/src/unit_parser/section.rs @@ -1,13 +1,5 @@ //! Functions for generating section parsing expressions. -use crate::{ - unit_conf_parse::get_option_inner_type, - unit_parser::{ - attribute::SectionAttributes, - entry::{ - gen_entry_ensure, gen_entry_finalize, gen_entry_init, gen_entry_parse, gen_entry_patch, - }, - }, -}; +use crate::unit_parser::entry::{gen_entry_default, gen_entry_ensure, gen_entry_parse}; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{Data, DeriveInput, Error, Field, Result}; @@ -15,19 +7,15 @@ use syn::{Data, DeriveInput, Error, Field, Result}; /// Generate [UnitSection] implementations when using `#[derive(UnitSection)]` pub fn gen_section_derives(input: DeriveInput) -> Result { let mut entry_ensures = Vec::new(); - let mut entry_inits = Vec::new(); + let mut entry_defaults = Vec::new(); let mut entry_parsers = Vec::new(); - let mut entry_finalizes = Vec::new(); let mut entries = Vec::new(); - let mut entry_patches = Vec::new(); if let Data::Struct(data_struct) = &input.data { for entry in &data_struct.fields { entry_ensures.push(gen_entry_ensure(entry)?); - entry_inits.push(gen_entry_init(entry)?); + entry_defaults.push(gen_entry_default(entry)?); entry_parsers.push(gen_entry_parse(entry)?); - entry_finalizes.push(gen_entry_finalize(entry)?); - entry_patches.push(gen_entry_patch(entry)?); let ident = entry.ident.as_ref().ok_or_else(|| { Error::new_spanned(&entry, "An entry must have an explicit name.") @@ -45,66 +33,33 @@ pub fn gen_section_derives(input: DeriveInput) -> Result { let result = quote! { impl unit_parser::internal::UnitSection for #ident { - fn __parse_section(__source: &mut unit_parser::internal::SectionParser) -> unit_parser::internal::Result> { + fn __parse_section(__source: &mut unit_parser::internal::SectionParser, __res: &mut Self) -> unit_parser::internal::Result<()> { # ( #entry_ensures )* - # ( #entry_inits )* loop { - if let Some(__pair) = __source.next() { - match __pair.0 { - #( #entry_parsers ),* - _ => { - log::warn!("{} is not a valid key.", __pair.0); - } - } - } else { - break; - } - } - #( #entry_finalizes )* - Ok(Some(Self { - #( #entries ),* - })) - } + let __pair = match __source.next() { + None => break, + Some(v) => v, + }; - fn __patch_section(__source: &mut unit_parser::internal::SectionParser, __from: &mut Self) -> unit_parser::internal::Result<()> { - # ( #entry_ensures )* - # ( #entry_inits )* - loop { - if let Some(__pair) = __source.next() { - match __pair.0 { - #( #entry_parsers ),* - _ => { - log::warn!("{} is not a valid key.", __pair.0); - } + match __pair.0 { + #( #entry_parsers ),* + _ => { + log::warn!("{} is not a valid key.", __pair.0); } - } else { - break; } } - #( #entry_patches )* Ok(()) } + + fn __load_default(__res: &mut Self) { + #( #entry_defaults )* + } } }; Ok(result) } -/// Generate variable initialization statements. -/// example: -/// ``` -/// let mut Section1 = None; -/// ``` -pub(crate) fn gen_section_init(field: &Field) -> Result { - let name = field - .ident - .as_ref() - .ok_or_else(|| Error::new_spanned(field, "Tuple structs are not supported."))?; - Ok(quote! { - let mut #name = None; - }) -} - /// Generate section parsing statements, in the form of an arm in a `match` statement. /// example: /// ``` @@ -118,88 +73,23 @@ pub(crate) fn gen_section_init(field: &Field) -> Result { /// } /// } /// ``` -pub(crate) fn gen_section_parse(field: &Field) -> Result<(TokenStream, TokenStream)> { +pub(crate) fn gen_section_parse(field: &Field) -> Result { let name = field .ident .as_ref() .ok_or_else(|| Error::new_spanned(field, "Tuple structs are not supported."))?; let ty = &field.ty; - let attributes = SectionAttributes::parse_vec(field, Some(ty))?; - let key = attributes - .key - .unwrap_or_else(|| (format!("{}", name)).into_token_stream()); - let result = match (attributes.default, attributes.must) { - // invalid - (true, true) => unreachable!(), - // convert Error to Option - (true, false) => ( - // ensure the struct implements `Default` by calling an function with generic constraints - quote! { - #key => { - const _: fn() = || { - fn assert_impl() {} - assert_impl::<#ty>(); - }; - if let Ok(__value) = unit_parser::internal::UnitSection::__parse_section(&mut __section) { - if __value.is_some() { - #name = __value; - } - } else { - log::warn!("Failed to parse section {}.", #key); - } - } - }, - quote! { - #key => { - let __section_partial = &mut __from.#name; - if let Err(_)= unit_parser::internal::UnitSection::__patch_section(&mut __section, __section_partial) { - log::warn!("Failed to parse section {}, skipping.", #key) - } - } - }, - ), - // convert Error to Option - (false, false) => ( - quote! { - #key => { - if let Ok(__value) = unit_parser::internal::UnitSection::__parse_section(&mut __section) { - #name = __value; - } else { - log::warn!("Failed to parse section {}, skipping.", #key); - } - } - }, - quote! { - #key => { - if let Some(__section_partial) = &mut __from.#name { - if let Err(_) = unit_parser::internal::UnitSection::__patch_section(&mut __section, __section_partial) { - log::warn!("Failed to patch section {}, skipping.", #key); - } - } else { - if let Ok(__inner) = unit_parser::internal::UnitSection::__parse_section(&mut __section) { - __from.#name = __inner; - } - } - } - }, - ), - // throw Error - (false, true) => ( - quote! { - #key => { - let __value = unit_parser::internal::UnitSection::__parse_section(&mut __section)? - .ok_or_else(||unit_parser::internal::Error::SectionParsingError{ key: #key.to_string() })?; - #name = Some(__value); - } - }, - quote! { - #key => { - let __section_partial = &mut __from.#name; - unit_parser::internal::UnitSection::__patch_section(&mut __section, __section_partial) - .map_err(|_| unit_parser::internal::Error::SectionParsingError{ key: #key.to_string() })?; } - }, - ), + let key = format!("{}", name).into_token_stream(); + + let result = quote! { + #key => { + const _: fn() = || { + fn assert_impl() {} + assert_impl::<#ty>(); + }; + let _ = unit_parser::internal::UnitSection::__parse_section(&mut __section, &mut __res.#name); + } }; Ok(result) @@ -208,11 +98,7 @@ pub(crate) fn gen_section_parse(field: &Field) -> Result<(TokenStream, TokenStre /// Generate statements that ensure the given struct implements [UnitSection] /// by calling a function with generic constraints. pub(crate) fn gen_section_ensure(field: &Field) -> Result { - let mut ty = &field.ty; - let attribute = SectionAttributes::parse_vec(field, None)?; - if (!attribute.must) & (!attribute.default) { - ty = get_option_inner_type(ty).unwrap(); - } + let ty = &field.ty; Ok(quote! { const _: fn() = || { fn assert_impl() {} @@ -221,69 +107,9 @@ pub(crate) fn gen_section_ensure(field: &Field) -> Result { }) } -/// Generate finalization statements which are in charge of processing [Option] and [Result]s during parsing. -pub(crate) fn gen_section_finalize(field: &Field) -> Result { - let name = field - .ident - .as_ref() - .ok_or_else(|| Error::new_spanned(field, "Tuple structs are not supported."))?; - let ty = &field.ty; - let attributes = SectionAttributes::parse_vec(field, None)?; - let key = attributes - .key - .unwrap_or_else(|| (format!("{}", name)).into_token_stream()); - - let result = match (attributes.default, attributes.must) { - (true, true) => unreachable!(), - // fallback to default - (true, false) => { - quote! { - let #name: #ty = #name.unwrap_or(Default::default()); - } - } - // throw Error - (false, true) => { - quote! { - let #name = #name.ok_or_else(||unit_parser::internal::Error::SectionMissingError { key: #key.to_string()})?; - } - } - // leave unchanged - (false, false) => { - quote! {} - } - }; - - Ok(result) -} - -/// Generate patching statements that sets each field to a new value, if present. -pub(crate) fn gen_section_patches(field: &Field) -> Result { - let name = field - .ident - .as_ref() - .ok_or_else(|| Error::new_spanned(field, "Tuple structs are not supported."))?; - let attributes = SectionAttributes::parse_vec(field, None)?; - - let result = match (attributes.must, attributes.default) { - // invalid - (true, true) => unreachable!(), - // unwrap inner if new value is Some - (true, false) | (false, true) => { - quote! { - if let Some(__inner) = #name { - __from.#name = __inner; - } - } - } - // set to new value if new value is Some - (false, false) => { - quote! { - if #name.is_some() { - __from.#name = #name; - } - } - } - }; - - Ok(result) +pub(crate) fn gen_section_default(field: &Field) -> Result { + let name = &field.ident.as_ref().unwrap(); + Ok(quote! { + let _ = unit_parser::internal::UnitSection::__load_default(&mut __res.#name); + }) } diff --git a/libs/macros/src/unit_parser/transform_default.rs b/libs/macros/src/unit_parser/transform_default.rs index 6460feff..c356c926 100644 --- a/libs/macros/src/unit_parser/transform_default.rs +++ b/libs/macros/src/unit_parser/transform_default.rs @@ -5,6 +5,7 @@ use syn::{Error, Expr, Lit, Type}; /// If a field is defined to be a [String] and the given default expression is a [str], /// this functions adds `.to_string()` to the end of it. +#[allow(unused)] pub(crate) fn transform_default(ty: &Type, default: &Expr) -> Result { // add `to_string()` suffix if ty is String if let Type::Path(inner) = ty { diff --git a/libs/macros/src/unit_parser/unit.rs b/libs/macros/src/unit_parser/unit.rs index 3ea00e95..effb2e86 100644 --- a/libs/macros/src/unit_parser/unit.rs +++ b/libs/macros/src/unit_parser/unit.rs @@ -1,10 +1,7 @@ //! Functions for generating unit parsing expressions. use crate::unit_parser::{ attribute::UnitAttributes, - section::{ - gen_section_ensure, gen_section_finalize, gen_section_init, gen_section_parse, - gen_section_patches, - }, + section::{gen_section_default, gen_section_ensure, gen_section_parse}, }; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; @@ -15,18 +12,14 @@ pub fn gen_unit_derives(input: DeriveInput) -> syn::Result { let attributes = UnitAttributes::parse_vec(&input.attrs)?; let mut sections = Vec::new(); let mut section_ensures = Vec::new(); - let mut section_inits = Vec::new(); let mut section_parsers = Vec::new(); - let mut section_finalizes = Vec::new(); - let mut section_patches = Vec::new(); + let mut section_defaults = Vec::new(); if let Data::Struct(data_struct) = &input.data { for entry in &data_struct.fields { section_ensures.push(gen_section_ensure(entry)?); - section_inits.push(gen_section_init(entry)?); section_parsers.push(gen_section_parse(entry)?); - section_finalizes.push(gen_section_finalize(entry)?); - section_patches.push(gen_section_patches(entry)?); + section_defaults.push(gen_section_default(entry)?); let ident = entry.ident.as_ref().ok_or_else(|| { Error::new_spanned(&entry, "An entry must have an explicit name.") })?; @@ -39,8 +32,7 @@ pub fn gen_unit_derives(input: DeriveInput) -> syn::Result { )); } - let parse_parsers = section_parsers.iter().map(|x| &x.0); - let patch_parsers = section_parsers.iter().map(|x| &x.1); + let parse_parsers = section_parsers.iter(); let ident = &input.ident; @@ -51,48 +43,28 @@ pub fn gen_unit_derives(input: DeriveInput) -> syn::Result { let result = quote! { impl unit_parser::internal::UnitConfig for #ident { const SUFFIX: &'static str = #suffix; - fn __parse_unit(__source: unit_parser::internal::UnitParser) -> unit_parser::internal::Result { + fn __parse_unit(__source: unit_parser::internal::UnitParser, __res: &mut Self) -> unit_parser::internal::Result<()> { let mut __source = __source; #( #section_ensures )* - #( #section_inits )* loop { - if let Some(mut __section) = __source.next() { - match __section.name { - #( #parse_parsers ),* - _ => { - log::debug!("{} is not a valid section.", __section.name); - } + let mut __section = match __source.next() { + None => break, + Some(v) => v, + }; + match __section.name { + #( #parse_parsers ),* + _ => { + log::debug!("{} is not a valid section.", __section.name); } - __source.progress(__section.finish()); - } else { - break; } + __source.progress(__section.finish()); } - #( #section_finalizes )* - Ok(Self { - #( #sections ),* - }) + Ok(()) } - fn __patch_unit(__source: unit_parser::internal::UnitParser, __from: &mut Self) -> unit_parser::internal::Result<()> { - let mut __source = __source; - #( #section_inits )* - loop { - if let Some(mut __section) = __source.next() { - match __section.name { - #( #patch_parsers ),* - _ => { - log::debug!("{} is not a valid section.", __section.name); - } - } - __source.progress(__section.finish()); - } else { - break; - } - } - #( #section_patches )* - Ok(()) - } + fn __load_default(__res: &mut Self) { + #( #section_defaults )* + } } }; diff --git a/libs/unit_parser/examples/dropins.rs b/libs/unit_parser/examples/dropins.rs deleted file mode 100644 index c03d2f0f..00000000 --- a/libs/unit_parser/examples/dropins.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![allow(non_snake_case, dead_code)] - -use unit_parser::prelude::*; - -#[derive(UnitConfig, Debug)] -#[unit(suffix = "service")] -struct Unit { - #[section(must)] - Section: Section, -} - -#[derive(UnitSection, Debug)] -struct Section { - #[entry(must)] - Field1: u32, - - #[entry(must)] - Field2: u32, - - #[entry(must)] - Field3: u32, - - #[entry(must)] - Field4: u32, -} - -fn main() { - let result = Unit::load_named( - vec!["libs/unit_parser/examples/dropins"], - "foo-bar-baz", - false, - ) - .unwrap(); - - println!("result: {:#?}", result); -} diff --git a/libs/unit_parser/examples/example.rs b/libs/unit_parser/examples/example.rs deleted file mode 100644 index faa7bae0..00000000 --- a/libs/unit_parser/examples/example.rs +++ /dev/null @@ -1,81 +0,0 @@ -#![allow(non_snake_case, dead_code)] - -use chrono::Duration; -use unit_parser::prelude::*; - -#[derive(UnitConfig, Debug)] -#[unit(suffix = "unit")] -pub struct Unit { - // sections can be attributed with default to fallback to default when not present - // trait `Default` is required to be implemented - // sections can also have alternative key name - #[section(default, key = "AlternativeKey")] - pub Section1: SimpleSection, - - #[section(must)] - pub Section2: AdvancedSection, - - pub Section3: Option, -} - -#[derive(UnitSection, Debug)] -pub struct SimpleSection { - #[entry(must)] - pub Field: String, -} - -impl Default for SimpleSection { - fn default() -> Self { - Self { - Field: "value".to_string(), - } - } -} - -#[derive(UnitSection, Debug)] -pub struct AdvancedSection { - /// a regular public config field - #[entry(must)] - pub Regular: String, - - /// a private config field - #[entry(must)] - Private: String, - - /// a config field with values within an enum - #[entry(must)] - Enum: MyEnum, - - /// a config field with custom key name - #[entry(key = "AlternativeKey", must)] - CustomNamed: String, - - /// a config field with default value - #[entry(default = "default-value")] - DefaultValued: String, - - /// a duration config field - #[entry(must)] - Duration: Duration, - - /// a field that can appear multiple times - #[entry(multiple)] - Multiple: Vec, - - /// an optional field - Optional: Option, -} - -#[derive(UnitSection, Debug)] -pub struct OptionalSection {} - -#[derive(UnitEntry, Debug)] -enum MyEnum { - Val1, - Val2, -} - -fn main() { - let result = Unit::load_named(vec!["./libs/unit_parser/examples"], "example", false).unwrap(); - println!("{:#?}", result); -} diff --git a/libs/unit_parser/examples/specifiers.rs b/libs/unit_parser/examples/specifiers.rs deleted file mode 100644 index 5bbec754..00000000 --- a/libs/unit_parser/examples/specifiers.rs +++ /dev/null @@ -1,100 +0,0 @@ -#![allow(non_snake_case)] - -use unit_parser::prelude::*; - -#[derive(UnitConfig, Debug)] -#[unit(suffix = "unit")] -struct Unit { - #[section(must)] - Section: Section, -} - -#[derive(UnitSection, Debug)] -struct Section { - #[entry(must)] - Arch: String, - #[entry(must)] - OSImageVersion: String, - #[entry(must)] - BootID: String, - #[entry(must)] - OSBuildID: String, - #[entry(must)] - CacheRoot: String, - #[entry(must)] - CredentialsDir: String, - #[entry(must)] - ConfigRoot: String, - #[entry(must)] - UnescapedFilename: String, - #[entry(must)] - UserGroup: String, - #[entry(must)] - UserGID: String, - #[entry(must)] - UserHomeDir: String, - #[entry(must)] - HostName: String, - #[entry(must)] - InstanceName: String, - #[entry(must)] - UnescapedInstanceName: String, - #[entry(must)] - FinalComponentOfThePrefix: String, - #[entry(must)] - UnescapedFinalComponentOfThePrefix: String, - #[entry(must)] - ShortHostName: String, - #[entry(must)] - LogDirRoot: String, - #[entry(must)] - MachineID: String, - #[entry(must)] - OSImageID: String, - #[entry(must)] - FullUnitName: String, - #[entry(must)] - FullUnitNameWithoutSuffix: String, - #[entry(must)] - OSID: String, - #[entry(must)] - PrefixName: String, - #[entry(must)] - UnescapedPrefixName: String, - #[entry(must)] - PrettyHostName: String, - #[entry(must)] - UserShell: String, - #[entry(must)] - StateDirRoot: String, - #[entry(must)] - RuntimeDirRoot: String, - #[entry(must)] - TempDirRoot: String, - #[entry(must)] - UserName: String, - #[entry(must)] - UserUID: String, - #[entry(must)] - KernelRelease: String, - #[entry(must)] - PersistTempDirRoot: String, - #[entry(must)] - OSVersionID: String, - #[entry(must)] - OSVariantID: String, - #[entry(must)] - FragmentPath: String, - #[entry(must)] - FragmentDir: String, -} - -fn main() { - let user_result = - Unit::load_named(vec!["libs/unit_parser/examples"], "specifiers", false).unwrap(); - println!("result in user mode: {:#?}", user_result); - - let root_result = - Unit::load_named(vec!["libs/unit_parser/examples"], "specifiers", false).unwrap(); - println!("result in root mode: {:#?}", root_result); -} diff --git a/libs/unit_parser/examples/subdir.rs b/libs/unit_parser/examples/subdir.rs deleted file mode 100644 index 5e3262bc..00000000 --- a/libs/unit_parser/examples/subdir.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![allow(non_snake_case, dead_code)] - -use unit_parser::prelude::*; - -#[derive(UnitConfig, Debug)] -#[unit(suffix = "unit")] -struct Unit { - #[section(must)] - Section: Section, -} - -#[derive(UnitSection, Debug)] -struct Section { - #[entry(multiple, subdir = "wants")] - Wants: Vec, -} - -fn main() { - let result = Unit::load_named( - vec!["libs/unit_parser/examples/subdir"], - "subdir.unit", - false, - ) - .unwrap(); - - println!("result: {:#?}", result); -} diff --git a/libs/unit_parser/examples/template.rs b/libs/unit_parser/examples/template.rs deleted file mode 100644 index c6042fe1..00000000 --- a/libs/unit_parser/examples/template.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![allow(non_snake_case, dead_code)] - -use unit_parser::prelude::*; - -#[derive(UnitConfig, Debug)] -#[unit(suffix = "unit")] -struct Unit { - #[section(must)] - Section: Section, -} - -#[derive(UnitSection, Debug)] -struct Section { - #[entry(must)] - Name: String, - - #[entry(must)] - Description: String, -} - -fn main() { - let result = Unit::load_named( - vec!["libs/unit_parser/examples/templates"], - "myunit@template", - false, - ) - .unwrap(); - - println!("result: {:#?}", result); -} diff --git a/libs/unit_parser/src/config.rs b/libs/unit_parser/src/config.rs index b5571ba2..5c281d95 100644 --- a/libs/unit_parser/src/config.rs +++ b/libs/unit_parser/src/config.rs @@ -3,12 +3,11 @@ use crate::{ error::ReadFileSnafu, internal::Error, parser::{SectionParser, UnitParser}, - template::{unit_type, UnitType}, }; use snafu::ResultExt; use std::{ ffi::OsString, - fs::{canonicalize, read_dir, File}, + fs::File, io::Read, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, num::{ @@ -25,22 +24,16 @@ pub type Result = std::result::Result; /// The trait that needs to be implemented on the most-outer struct, /// representing a type of unit. -pub trait UnitConfig: Sized { +pub trait UnitConfig: Sized + Default { /// The suffix of a type of unit, parsed from an attribute. const SUFFIX: &'static str; /// Parses the unit from a [UnitParser]. - fn __parse_unit(__source: UnitParser) -> Result; - /// Parses the unit from a [UnitParser], but only patches the supplied entries onto the given struct. - fn __patch_unit(__source: UnitParser, __from: &mut Self) -> Result<()>; + fn __parse_unit(__source: UnitParser, res: &mut Self) -> Result<()>; + /// Load the default value + fn __load_default(__res: &mut Self); /// A convenient function that opens the file that needs to be loaded. - fn __load>( - path: S, - paths: Rc>, - config_file_name: &str, - unit_name: &str, - root: bool, - ) -> Result { + fn __load>(path: S, unit_name: &str, res: &mut Self) -> Result<()> { let path = path.as_ref(); let mut file = File::open(path).context(ReadFileSnafu { path: path.to_string_lossy().to_string(), @@ -49,139 +42,25 @@ pub trait UnitConfig: Sized { file.read_to_string(&mut content).context(ReadFileSnafu { path: path.to_string_lossy().to_string(), })?; - let canonical_path = canonicalize(path).unwrap_or_else(|_| path.into()); - let parser = crate::parser::UnitParser::new( - content.as_ref(), - paths, - (root, config_file_name, unit_name, &canonical_path), - ); - Self::__parse_unit(parser) + let parser = crate::parser::UnitParser::new(content.as_ref(), (unit_name,)); + Self::__parse_unit(parser, res) } - /// A convenient function that opens the file that needs to be patched. - fn __patch>( - path: S, - paths: Rc>, - config_file_name: &str, - unit_name: &str, - from: &mut Self, - root: bool, - ) -> Result<()> { - let path = path.as_ref(); - let mut file = File::open(path).context(ReadFileSnafu { - path: path.to_string_lossy().to_string(), - })?; - let mut content = String::new(); - file.read_to_string(&mut content).context(ReadFileSnafu { - path: path.to_string_lossy().to_string(), - })?; - let parser = crate::parser::UnitParser::new( - content.as_ref(), - paths, - (root, config_file_name, unit_name, path), - ); - Self::__patch_unit(parser, from) - } - - /// Loads a unit with the given name and search paths. + /// Loads a unit with the given config file list and unit name /// If no suffix is given, the unit's defined suffix will be added to the end. - fn load_named, P: AsRef>( - paths: Vec

, - name: S, - root: bool, - ) -> Result { + fn load_config>(paths: Vec

, unit_name: &str) -> Result { // return when first one is found? let paths: Vec = paths.iter().map(|x| x.as_ref().to_path_buf()).collect(); let paths_rc = Rc::new(paths); - let name = name.as_ref(); - let fullname = if name.ends_with(Self::SUFFIX) { - name.to_string() - } else { - format!("{}.{}", name, Self::SUFFIX) - }; + let fullname = unit_name.to_string(); - let config_file_names = match unit_type(fullname.as_str())? { - UnitType::Instance(_, template_filename) => { - vec![fullname.to_string(), template_filename] - } - _ => { - vec![fullname.to_string()] - } - }; - let mut result = None; + let mut result = Self::default(); + Self::__load_default(&mut result); // load itself let paths = Rc::clone(&paths_rc); for dir in (*paths).iter() { - for config_file_name in &config_file_names { - let mut path = dir.to_owned(); - path.push(config_file_name.as_str()); - if let Ok(res) = Self::__load( - path, - Rc::clone(&paths_rc), - config_file_name.as_str(), - &fullname, - root, - ) { - result = Some(res); - break; - } - } - if result.is_some() { - break; - } - } - - let mut result = if let Some(result) = result { - result - } else { - return Err(Error::NoUnitFoundError { - name: name.to_string(), - }); - }; - - // load drop-ins - let mut dropin_dir_names: Vec = Vec::new(); - dropin_dir_names.push(format!("{}.d", Self::SUFFIX)); - for config_file_name in config_file_names { - dropin_dir_names.push(format!("{}.d", config_file_name.as_str())); - } - let segments: Vec<&str> = fullname.split('-').collect(); - for i in (1..segments.len()).rev() { - let segmented = segments[0..i].join("-"); - let dir_name = format!("{}-.{}.d", segmented, Self::SUFFIX); - dropin_dir_names.push(dir_name); - } - - for dir_name in dropin_dir_names.iter() { - for dir in (*paths).iter() { - let mut path = dir.to_owned(); - path.push(dir_name.as_str()); - if !path.is_dir() { - continue; - } - if let Ok(dir_entries) = read_dir(&path) { - for entry in dir_entries.flatten() { - if let (Ok(filetype), Some(extension)) = - (entry.file_type(), entry.path().extension()) - { - if filetype.is_file() && extension == "conf" { - let paths = Rc::clone(&paths_rc); - if let Err(err) = Self::__patch( - entry.path(), - paths, - dir_name.as_str(), - fullname.as_str(), - &mut result, - root, - ) { - log::warn!("Failed to patch unit {}: {})", name, err); - } - } - } - } - } - } + let _ = Self::__load(dir, &fullname, &mut result); } Ok(result) @@ -191,9 +70,9 @@ pub trait UnitConfig: Sized { /// The trait that needs to be implemented on each section of the unit. pub trait UnitSection: Sized { /// Parses the section from a [SectionParser]. - fn __parse_section(__source: &mut SectionParser) -> Result>; - /// Parses the section from a [SectionParser], but only patches the supplied entries onto the given struct. - fn __patch_section(__source: &mut SectionParser, __from: &mut Self) -> Result<()>; + fn __parse_section(__source: &mut SectionParser, res: &mut Self) -> Result<()>; + /// Load the default value + fn __load_default(__res: &mut Self); } /// The trait that needs to be implemented on each entry of the unit. diff --git a/libs/unit_parser/src/parser.rs b/libs/unit_parser/src/parser.rs index f884150f..8df51397 100644 --- a/libs/unit_parser/src/parser.rs +++ b/libs/unit_parser/src/parser.rs @@ -9,14 +9,10 @@ use nom::{ sequence::{delimited, separated_pair}, IResult, }; -use std::{fs::read_dir, path::PathBuf, rc::Rc}; - // TODO: error callsite marking /// A parser for parsing a whole unit. pub struct UnitParser<'a> { - /// Search paths - paths: Rc>, /// Parsing cursor inner: &'a str, /// Specifier resolve context @@ -25,9 +21,8 @@ pub struct UnitParser<'a> { impl<'a> UnitParser<'a> { /// Creates a new [UnitParser] with input, scan paths and specifier resolve context. - pub fn new(input: &'a str, paths: Rc>, context: SpecifierContext<'a>) -> Self { + pub fn new(input: &'a str, context: SpecifierContext<'a>) -> Self { UnitParser { - paths, inner: input, context, } @@ -47,7 +42,6 @@ impl<'a> Iterator for UnitParser<'a> { if let Ok((i, name)) = section_header(self.inner) { self.inner = i; return Some(SectionParser { - paths: Rc::clone(&self.paths), name, inner: self.inner, context: self.context, @@ -78,8 +72,6 @@ fn section_header(i: &str) -> IResult<&str, &str> { /// A parser for parsing a section. pub struct SectionParser<'a> { - /// Specifier resolve context - paths: Rc>, /// Section name pub name: &'a str, /// Parsing cursor @@ -108,29 +100,6 @@ impl<'a> Iterator for SectionParser<'a> { } } -impl<'a> SectionParser<'a> { - /// Parses subdirs from paths. - pub fn __parse_subdir(&self, subdir: &str) -> Vec { - let mut result = Vec::new(); - for dir in (*self.paths).iter() { - let mut path = dir.to_owned(); - let path_end = format!("{}.{}", self.context.1, subdir); - path.push(path_end.as_str()); - if let Ok(read_res) = read_dir(path) { - for entry in read_res.flatten() { - // only look for symlinks - if let Ok(metadata) = entry.metadata() { - if metadata.file_type().is_symlink() { - result.push(entry.file_name().to_string_lossy().to_string()); - } - } - } - } - } - result - } -} - // returns (key, value) pair // specifiers are resolved in the process, leading to string copies /// Parses an entry. diff --git a/libs/unit_parser/src/specifiers.rs b/libs/unit_parser/src/specifiers.rs index 44a842be..f655a4d0 100644 --- a/libs/unit_parser/src/specifiers.rs +++ b/libs/unit_parser/src/specifiers.rs @@ -9,9 +9,9 @@ use nix::{ }; use once_cell::sync::Lazy; use os_release::OsRelease; -use std::{env, fs, path::Path}; +use std::{env, fs}; -pub(crate) type SpecifierContext<'a> = (bool, &'a str, &'a str, &'a Path); // (root, filename, path) +pub(crate) type SpecifierContext<'a> = (&'a str,); // (unit_name) static OS_RELEASE: Lazy = Lazy::new(|| OsRelease::new().expect("Failed to read os-release.")); @@ -31,6 +31,7 @@ pub(crate) fn resolve( specifier: char, context: SpecifierContext, ) -> Result<(), Error> { + let in_system_mode = true; match specifier { 'a' => { if let Some(res) = UTS_NAME.machine().to_str() { @@ -49,7 +50,7 @@ pub(crate) fn resolve( } } 'C' => { - if context.0 { + if in_system_mode { result.push_str("/var/cache"); } else if let Ok(res) = env::var("XDG_CACHE_HOME") { result.push_str(&res); @@ -63,7 +64,7 @@ pub(crate) fn resolve( } } 'E' => { - if context.0 { + if in_system_mode { result.push_str("/etc"); } else if let Ok(res) = env::var("XDG_CONFIG_HOME") { result.push_str(&res); @@ -71,9 +72,9 @@ pub(crate) fn resolve( result.push_str("~/.config"); } } - 'f' => result.push_str(context.1), + 'f' => result.push_str(context.0), 'g' => { - if context.0 { + if in_system_mode { result.push_str("root"); } else if let Some(gid) = Group::from_gid(*CURRENT_GID).expect("Failed to read current group info.") @@ -82,14 +83,14 @@ pub(crate) fn resolve( } } 'G' => { - if context.0 { + if in_system_mode { result.push('0'); } else { result.push_str(&CURRENT_GID.to_string()); } } 'h' => { - if context.0 { + if in_system_mode { result.push_str("/root"); } else if let Ok(res) = env::var("HOME") { result.push_str(&res); @@ -103,22 +104,22 @@ pub(crate) fn resolve( } } 'i' => { - if let UnitType::Instance(instance_name, _) = unit_type(context.2)? { + if let UnitType::Instance(instance_name, _) = unit_type(context.0)? { result.push_str(&escape(instance_name)); } } 'I' => { - if let UnitType::Instance(instance_name, _) = unit_type(context.2)? { + if let UnitType::Instance(instance_name, _) = unit_type(context.0)? { result.push_str(instance_name); } } 'j' => { - if let UnitType::Instance(instance_name, _) = unit_type(context.2)? { + if let UnitType::Instance(instance_name, _) = unit_type(context.0)? { result.push_str(&escape(instance_name.split('-').last().unwrap())); } else { result.push_str(&escape( context - .1 + .0 .split('.') .next() .unwrap() @@ -129,12 +130,12 @@ pub(crate) fn resolve( } } 'J' => { - if let UnitType::Instance(instance_name, _) = unit_type(context.1)? { + if let UnitType::Instance(instance_name, _) = unit_type(context.0)? { result.push_str(instance_name.split('-').last().unwrap()); } else { result.push_str( context - .1 + .0 .split('.') .next() .unwrap() @@ -153,7 +154,7 @@ pub(crate) fn resolve( .unwrap(), ), 'L' => { - if context.0 { + if in_system_mode { result.push_str("/var/log"); } else if let Ok(res) = env::var("XDG_STATE_HOME") { result.push_str(&res); @@ -168,21 +169,21 @@ pub(crate) fn resolve( result.push_str(res) } } - 'n' => result.push_str(&escape(context.2)), - 'N' => result.push_str(&escape(context.2.split('.').next().unwrap())), + 'n' => result.push_str(&escape(context.0)), + 'N' => result.push_str(&escape(context.0.split('.').next().unwrap())), 'o' => result.push_str(&OS_RELEASE.id), 'p' => { - if let UnitType::Instance(instance_name, _) = unit_type(context.1)? { + if let UnitType::Instance(instance_name, _) = unit_type(context.0)? { result.push_str(&escape(instance_name)); } else { - result.push_str(&escape(context.2.split('.').next().unwrap())); + result.push_str(&escape(context.0.split('.').next().unwrap())); } } 'P' => { - if let UnitType::Instance(instance_name, _) = unit_type(context.1)? { + if let UnitType::Instance(instance_name, _) = unit_type(context.0)? { result.push_str(instance_name); } else { - result.push_str(context.2.split('.').next().unwrap()); + result.push_str(context.0.split('.').next().unwrap()); } } 'q' => result.push_str( @@ -199,7 +200,7 @@ pub(crate) fn resolve( } } 'S' => { - if context.0 { + if in_system_mode { result.push_str("/var/lib"); } else if let Ok(res) = env::var("XDG_STATE_HOME") { result.push_str(&res); @@ -208,7 +209,7 @@ pub(crate) fn resolve( } } 't' => { - if context.0 { + if in_system_mode { result.push_str("/run"); } else if let Ok(res) = env::var("XDG_RUNTIME_DIR") { result.push_str(&res); @@ -256,16 +257,16 @@ pub(crate) fn resolve( result.push_str(res); } } - 'y' => { - if let Some(res) = context.3.to_str() { - result.push_str(res) - } - } - 'Y' => { - if let Some(res) = context.3.parent().expect("Invalid file path.").to_str() { - result.push_str(res) - } - } + // 'y' => { + // if let Some(res) = context.3.to_str() { + // result.push_str(res) + // } + // } + // 'Y' => { + // if let Some(res) = context.3.parent().expect("Invalid file path.").to_str() { + // result.push_str(res) + // } + // } '%' => result.push('%'), _ => return Err(Error::InvalidSpecifierError { specifier }), }; -- Gitee