diff --git a/config_parser/src/common.rs b/config_parser/src/common.rs index 336d3bf..ff75a9a 100644 --- a/config_parser/src/common.rs +++ b/config_parser/src/common.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use proc_macro2::TokenStream; -use quote::{quote, TokenStreamExt}; +use quote::quote; use syn::{Attribute, Error, Field, Meta, Path, punctuated::Punctuated, token::Comma}; @@ -21,9 +21,11 @@ pub fn gen_config_load_function(fields: &Punctuated, config_map_na match attr_name.first() { Some(value) => if value.ident == "nested_config" { assignments.extend(quote! { - self.#name.parse_from_map(&#config_map_name); + self.#name.parse_from_map(&mut #config_map_name); }); continue 'field_loop; + } else if value.ident == "no_config" { + continue 'field_loop; }, None => (), } @@ -32,8 +34,13 @@ pub fn gen_config_load_function(fields: &Punctuated, config_map_na assignments.extend(quote! { self.#name = match #config_map_name.get(#name_string) { Some(value) => match value.parse::<#ty>() { - Ok(parsed) => parsed, - Err(_) => self.#name.clone(), + Ok(parsed) => { + _ = #config_map_name.remove(#name_string); + parsed + }, + Err(_) => { + self.#name.clone() + }, }, None => self.#name.clone(), }; @@ -42,35 +49,3 @@ pub fn gen_config_load_function(fields: &Punctuated, config_map_na Ok(assignments.into()) } -//pub fn parse_config_file(input: &String) -> std::io::Result> { -// let mut config = std::collections::HashMap::::new(); -// let lines = input.lines(); -// -// for line in lines { -// let line = line.trim(); -// // ignore empty lines -// if line.is_empty() { continue; } -// -// if line.starts_with("[") { -// // check for table -// if !line.ends_with("]") { -// // TODO: implement error handling -// } else if line.contains(' ') { -// // TODO: implement error handling -// } -// //let tokens = line.split('.'); -// // TODO: implement hirarchical map -// } else { -// // check for config -// let mut tokens: Vec<&str> = line.split('=').map(|entry| entry.trim()).collect(); -// tokens.retain(|entry| !entry.is_empty()); -// if tokens.len() != 2 { -// // println!("error in line'", line); -// continue; -// } -// config.insert(tokens[0].to_string(), tokens[1].to_string()); -// } -// } -// Ok(config) -//} -// diff --git a/config_parser/src/lib.rs b/config_parser/src/lib.rs index 4048c64..e530983 100644 --- a/config_parser/src/lib.rs +++ b/config_parser/src/lib.rs @@ -1,7 +1,7 @@ use common::gen_config_load_function; -use proc_macro2::{TokenStream}; // TODO: change to proc_macro2, to hopefully fix #assignments -use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Ident}; -use quote::{quote}; +use proc_macro2::TokenStream; +use syn::{parse_macro_input, DeriveInput}; +use quote::quote; #[macro_use] mod common; @@ -11,7 +11,7 @@ mod common; /// which parses a string and fills the fills recognised values into the struct /// - implements `write_default_config() -> Result` /// which write a default configuration, in case the documentation is lacking -#[proc_macro_derive(ConfigParser, attributes(nested_config))] +#[proc_macro_derive(ConfigParser, attributes(color_config, nested_config, no_config))] pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = parse_macro_input!(input as DeriveInput); let name = &ast.ident; @@ -19,7 +19,7 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let fields = if let syn::Data::Struct(syn::DataStruct{ fields: syn::Fields::Named(syn::FieldsNamed{ ref named, .. }), .. }) = ast.data { named } else { - todo!("use `syn::Error` to return comprehensive error message"); + panic!("the macro `ConfigParser` applies only to structs!"); }; let assignments = match gen_config_load_function(fields, &config_name) { Ok(value) => value, @@ -29,21 +29,26 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { //let string = format!("{:#?}", ast); //_ = std::fs::write("test.txt", string); - let expanded_stream: TokenStream = quote::quote! { + let expanded_stream: TokenStream = quote! { impl #name { + /// tries to parse config from a string pub fn parse_from_string(&mut self, input: &String) -> std::io::Result<()> { - let #config_name : std::collections::HashMap = Self::parse_config_file(input)?; + let mut #config_name : std::collections::HashMap = Self::parse_config_file(input)?; #assignments + if !#config_name.is_empty() { + let leftovers = #config_name.keys().cloned().collect::>(); + return Err(std::io::Error::other(format!("the following settings were not recognised: {:#?}", leftovers))); + } Ok(()) } /// **do not call** /// this function needs to be public for nested configs but is not intended /// to be called by the user - pub fn parse_from_map(&mut self, input: &std::collections::HashMap) -> std::io::Result<()> { - let #config_name = input; + pub fn parse_from_map(&mut self, input: &mut std::collections::HashMap) -> std::io::Result<()> { + let mut #config_name = input; #assignments diff --git a/src/config.rs b/src/config.rs index 34fd60f..c0daa0e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,7 +8,7 @@ use dirs::config_dir; use std::fs; use std::io::{Error, Result}; use std::path::PathBuf; -use derive_config_parser::ConfigParser; +use config_parser::ConfigParser; #[derive(Debug, Clone)] pub struct Config { @@ -16,21 +16,24 @@ pub struct Config { pub settings: Settings, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, ConfigParser)] pub struct Settings { + #[nested_config] pub general: GeneralSettings, + #[nested_config] pub format: FormatSettings, + #[nested_config] pub styles: StyleSettings, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, ConfigParser)] pub struct GeneralSettings { pub show_stack_on_push: bool, pub show_stack_on_pop: bool, pub show_stack_on_bookmark: bool, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, ConfigParser)] pub struct FormatSettings { pub stack_separator: String, pub bookmarks_separator: String, @@ -90,12 +93,19 @@ impl Config { .push(format!("navigate/{}", Self::CONFIG_FILE_NAME)); // parse configuration file and populate config struct - let file_error = config.build_settings(); - config.set_default_settings()?; - if file_error.is_err() { - config.write_config_file()?; + if config.conf_file.is_file() { + let config_str = match fs::read_to_string(&config.conf_file) { + Ok(value) => value, + Err(error) => return Err(error), + }; + _ = config.settings.parse_from_string(&config_str); + } else { + // TODO: write default configuration } - config.parse_color_settings()?; + + // TODO: ALSDKJFJFJASDkk + //config.set_default_settings()?; + //config.parse_color_settings()?; Ok(config) } @@ -107,19 +117,6 @@ impl Config { /// reads and parses the configuration file fn build_settings(&mut self) -> Result<()> { - if !self.conf_file.is_file() { - return Err(Error::other( - "-- config file `navigation.conf` does not exist", - )); - } - let config_str = match fs::read_to_string(&self.conf_file) { - Ok(value) => value, - Err(error) => return Err(error), - }; - self.settings = match toml::from_str(&config_str) { - Ok(value) => value, - Err(error) => return Err(Error::other(error.to_string())), - }; Ok(()) } @@ -159,15 +156,6 @@ impl Config { Ok(()) } - /// write configuration file to save changed settings - pub fn write_config_file(&self) -> Result<()> { - let conf_str = match toml::to_string(&self.settings) { - Ok(value) => value, - Err(error) => return Err(Error::other(error.to_string())), - }; - fs::write(self.conf_file.clone(), conf_str) - } - /// convert color settings to ansi escape sequences pub fn parse_color_settings(&mut self) -> Result<()> { self.settings.styles.stack_number = parse_style(self.settings.styles.stack_number.clone())?;