diff --git a/config_parser/Cargo.toml b/config_parser/Cargo.toml index 062078e..5eefc16 100644 --- a/config_parser/Cargo.toml +++ b/config_parser/Cargo.toml @@ -9,6 +9,7 @@ publish = false proc-macro = true [dependencies] +proc-macro2 = "1.0.92" quote = "1.0.38" syn = { version = "2.0.94", features = ["full", "extra-traits"] } diff --git a/config_parser/src/common.rs b/config_parser/src/common.rs index 57a1503..336d3bf 100644 --- a/config_parser/src/common.rs +++ b/config_parser/src/common.rs @@ -1,39 +1,45 @@ #![allow(dead_code)] -use proc_macro::TokenStream; -use syn::{parse_macro_input, DeriveInput}; -use quote::{quote}; +use proc_macro2::TokenStream; +use quote::{quote, TokenStreamExt}; use syn::{Attribute, Error, Field, Meta, Path, punctuated::Punctuated, token::Comma}; -pub fn gen_config_load_function(fields: &Punctuated, config_map_name: &String) -> Result { - let assignments = fields.iter().map(|f|{ - let attr = &f.attrs; - let name = match &f.ident { +pub fn gen_config_load_function(fields: &Punctuated, config_map_name: &syn::Ident) -> Result { + let mut assignments : TokenStream = TokenStream::new(); + 'field_loop: for field in fields.iter() { + let attr = &field.attrs; + let name = match &field.ident { Some(value) => value, // skip anonymous fields - None => return quote! {}, + None => continue 'field_loop, }; let name_string: String = name.to_string(); - let ty = &f.ty; + let ty = &field.ty; for attribute in attr { if let Attribute{ meta: Meta::Path( Path{segments: attr_name, ..} ), .. } = attribute { match attr_name.first() { Some(value) => if value.ident == "nested_config" { - return quote! {#name.parse_from_map(&#config_map_name)} + assignments.extend(quote! { + self.#name.parse_from_map(&#config_map_name); + }); + continue 'field_loop; }, None => (), } } } - quote! { + assignments.extend(quote! { self.#name = match #config_map_name.get(#name_string) { - Some(value) => value, - None => self.#name, - } - } - }); - Ok(quote! {assignments}.into()) + Some(value) => match value.parse::<#ty>() { + Ok(parsed) => parsed, + Err(_) => self.#name.clone(), + }, + None => self.#name.clone(), + }; + }); + } + Ok(assignments.into()) } //pub fn parse_config_file(input: &String) -> std::io::Result> { diff --git a/config_parser/src/lib.rs b/config_parser/src/lib.rs index 3226523..4048c64 100644 --- a/config_parser/src/lib.rs +++ b/config_parser/src/lib.rs @@ -1,5 +1,5 @@ use common::gen_config_load_function; -use proc_macro::{TokenStream}; // TODO: change to proc_macro2, to hopefully fix #assignments +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}; @@ -11,11 +11,11 @@ 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, testtest))] -pub fn derive(input: TokenStream) -> TokenStream { +#[proc_macro_derive(ConfigParser, attributes(nested_config))] +pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = parse_macro_input!(input as DeriveInput); let name = &ast.ident; - let config_name = "config".to_string(); //syn::Ident::new("config", name.span()); // TODO: use correct span + let config_name = syn::Ident::new("config", name.span()); // TODO: use correct span let fields = if let syn::Data::Struct(syn::DataStruct{ fields: syn::Fields::Named(syn::FieldsNamed{ ref named, .. }), .. }) = ast.data { named } else { @@ -23,17 +23,9 @@ pub fn derive(input: TokenStream) -> TokenStream { }; let assignments = match gen_config_load_function(fields, &config_name) { Ok(value) => value, - Err(error) => panic!("loading config failed"), + Err(_) => panic!("loading config failed"), }; - //let assign_fields = fields.iter().map(|f|{ - // if let Some(name) = &f.ident { - // quote! { - // if - // } - // } - //}); - //let string = format!("{:#?}", ast); //_ = std::fs::write("test.txt", string); @@ -41,7 +33,9 @@ pub fn derive(input: TokenStream) -> TokenStream { impl #name { pub fn parse_from_string(&mut self, input: &String) -> std::io::Result<()> { let #config_name : std::collections::HashMap = Self::parse_config_file(input)?; + #assignments + Ok(()) } @@ -49,7 +43,11 @@ pub fn derive(input: TokenStream) -> TokenStream { /// 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<()> { - todo!(); + let #config_name = input; + + #assignments + + Ok(()) } fn parse_config_file(input: &String) -> std::io::Result> { @@ -84,9 +82,16 @@ pub fn derive(input: TokenStream) -> TokenStream { } Ok(config) } + + // TODO: implement + pub fn write_default_config(&self) -> Result<()> { + Ok(()) + } + + // TODO: implement function to parse style settings } }.into(); - expanded_stream + expanded_stream.into() //TokenStream::new() }