quick save

This commit is contained in:
2025-01-05 22:46:54 +01:00
parent e9f91575f2
commit a4bbe4b34b
5 changed files with 166 additions and 48 deletions
+1 -1
View File
@@ -9,4 +9,4 @@ dirs = "5.0.1"
serde = { version = "1.0.216", features = [ "std", "derive" ] }
sysinfo = "0.32.0"
toml = "0.8.19"
derive_config_parser = { path = "derive_config_parser" }
config_parser = { path = "config_parser" }
@@ -1,5 +1,5 @@
[package]
name = "derive_config_parser"
name = "config_parser"
version = "0.1.0"
autotests = false
edition = "2021"
@@ -10,5 +10,6 @@ proc-macro = true
[dependencies]
quote = "1.0.38"
syn = { version = "2.0.92", features = ["extra-traits"] }
syn = { version = "2.0.94", features = ["full", "extra-traits"] }
+70
View File
@@ -0,0 +1,70 @@
#![allow(dead_code)]
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
use quote::{quote};
use syn::{Attribute, Error, Field, Meta, Path, punctuated::Punctuated, token::Comma};
pub fn gen_config_load_function(fields: &Punctuated<Field, Comma>, config_map_name: &String) -> Result<TokenStream, Error> {
let assignments = fields.iter().map(|f|{
let attr = &f.attrs;
let name = match &f.ident {
Some(value) => value,
// skip anonymous fields
None => return quote! {},
};
let name_string: String = name.to_string();
let ty = &f.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)}
},
None => (),
}
}
}
quote! {
self.#name = match #config_map_name.get(#name_string) {
Some(value) => value,
None => self.#name,
}
}
});
Ok(quote! {assignments}.into())
}
//pub fn parse_config_file(input: &String) -> std::io::Result<std::collections::HashMap<String, String>> {
// let mut config = std::collections::HashMap::<String, String>::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)
//}
//
+92
View File
@@ -0,0 +1,92 @@
use common::gen_config_load_function;
use proc_macro::{TokenStream}; // TODO: change to proc_macro2, to hopefully fix #assignments
use syn::{parse_macro_input, spanned::Spanned, DeriveInput, Ident};
use quote::{quote};
#[macro_use]
mod common;
/// **for structs only**
/// - implements `parse_config(&mut self, input: &String) -> Result<()>`
/// which parses a string and fills the fills recognised values into the struct
/// - implements `write_default_config() -> Result<String>`
/// 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 {
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 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");
};
let assignments = match gen_config_load_function(fields, &config_name) {
Ok(value) => value,
Err(error) => 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);
let expanded_stream: TokenStream = quote::quote! {
impl #name {
pub fn parse_from_string(&mut self, input: &String) -> std::io::Result<()> {
let #config_name : std::collections::HashMap<String, String> = Self::parse_config_file(input)?;
#assignments
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<String, String>) -> std::io::Result<()> {
todo!();
}
fn parse_config_file(input: &String) -> std::io::Result<std::collections::HashMap<String, String>> {
let mut config = std::collections::HashMap::<String, String>::new();
let lines = input.lines();
for line in lines {
let mut 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)
}
}
}.into();
expanded_stream
//TokenStream::new()
}
-45
View File
@@ -1,45 +0,0 @@
use core::panic;
use proc_macro::TokenStream;
/// **for structs only**
/// - implements `parse_config(&mut self, input: &String) -> Result<()>`
/// which parses a string and fills the fills recognised values into the struct
/// - implements `write_default_config() -> Result<String>`
/// which write a default configuration, in case the documentation is lacking
#[proc_macro_derive(ConfigParser)]
pub fn derive(input: TokenStream) -> TokenStream {
let ast = syn::parse_macro_input!(input as syn::DeriveInput);
let name = &ast.ident;
let fields = if let syn::Data::Struct(syn::DataStruct{ fields: syn::Fields::Named(syn::FieldsNamed{ ref named, .. }), .. }) = ast.data {
named
} else {
panic!("failed to parse struct fields");
};
println!("{:#?}", ast);
// let string = format!("{:#?}", ast);
// _ = std::fs::write("test.txt", string);
let expanded_stream: TokenStream = quote::quote! {
impl #name {
pub fn parse_config(&mut self, input: &String) -> std::io::Result<()> {
let mut setting_names = std::collections::HashMap::new();
let lines = input.lines();
for line in lines {
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;
}
setting_names.insert(tokens[0], tokens[1]);
}
//println!("{:#?}", setting_names);
Ok(())
}
}
}.into();
expanded_stream
// TokenStream::new()
}