diff --git a/.gitignore b/.gitignore
index 6936990..0668c27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
/target
**/*.rs.bk
Cargo.lock
+.idea/
\ No newline at end of file
diff --git a/.idea/lfcm.iml b/.idea/lfcm.iml
index bbe0a70..e4f2f92 100644
--- a/.idea/lfcm.iml
+++ b/.idea/lfcm.iml
@@ -4,6 +4,7 @@
+
diff --git a/Cargo.toml b/Cargo.toml
index 5de5e20..169f9c9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,4 +15,5 @@ proc-macro = true
quote = "1"
proc-macro2 = "1.0"
syn = "1.0"
+config = "0.15.25"
diff --git a/src/lib.rs b/src/lib.rs
index 1653857..23c20d6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,9 +1,8 @@
extern crate proc_macro;
extern crate proc_macro2;
-use proc_macro2::TokenStream;
use std::fmt::{Debug, Formatter};
-use proc_macro2::{Group, TokenTree};
+use proc_macro2::Literal;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{parse_macro_input, Expr, Type, Ident, braced};
@@ -148,26 +147,34 @@ impl Debug for ParseConfigDefinition {
}
#[proc_macro]
-pub fn config(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+pub fn config(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// parse the input and prepare the output
let definition: ParseConfigDefinition = parse_macro_input!(input as ParseConfigDefinition);
- let mut out = proc_macro2::TokenStream::new();
+ let mut out = quote!{
+ use ::config as _config_deser;
+
+ pub enum LFCMError {
+ UnableToBuild,
+ ParseError(_config_deser::ConfigError)
+ }
+ };
// generate the class Structure
let mut config_sections: Vec = Vec::new();
walk_config_sections(&definition, &mut config_sections);
- for config_section in config_sections.iter() {
- println!("Section: {}", config_section.name.span().source_text().unwrap_or("".to_string()));
- }
-
- for section in config_sections.iter() {
- emit_section_struct(&mut out, section)
- }
-
// TODO: generate implementations for necessary output
+ for section in config_sections.iter() {
+ // Generate the struct representation of the configuration
+ emit_section_struct(&mut out, section);
+ // TODO: generate Deserialization code
+ // generate the deserialization code for the configuration
+ emit_section_deser_impl(&mut out, section);
+ }
+
+ // TODO: generate the combined deserialization function
+ emit_deser_impl(&mut out, definition);
- println!("Seems done");
proc_macro::TokenStream::from(out)
}
@@ -220,6 +227,7 @@ fn emit_section_struct(out: &mut proc_macro2::TokenStream, section: &ParseConfig
let type_name = capitalize_name(§ion.name.to_string());
let type_name_ident = Ident::new(type_name.as_str(), section.name.span());
let struct_stream = quote! {
+ #[derive(Debug)]
pub struct #type_name_ident {
#(#vars),*
#(#subsections),*
@@ -227,4 +235,67 @@ fn emit_section_struct(out: &mut proc_macro2::TokenStream, section: &ParseConfig
};
out.extend(struct_stream.into_iter());
+}
+
+fn emit_section_deser_impl(out: &mut proc_macro2::TokenStream, section: &ParseConfigSection) {
+ // generate deserialisation for all variables
+ let var_deser_parts = section.vars.iter().map(|var| {
+ let var_name = var.name.clone();
+ let var_name_lit = Literal::string(var.name.to_string().as_str());
+ let var_default = var.default.clone();
+ quote!{
+ #var_name: match cfg.get((prefix.to_owned() + #var_name_lit).as_str()) {
+ Ok (value) => value,
+ Err (_config_deser::ConfigError::NotFound(_)) => #var_default,
+ Err (x) => return Err(LFCMError::ParseError(x))
+ }
+ }
+ }).collect::>();
+
+ // generate deserialisation calls for all substructs
+ let subsection_deser_parts = section.subsections.iter().map(|subsection| {
+ let subsection_name = subsection.name.clone();
+ let subsection_type_name = Ident::new(capitalize_name(&subsection.name.to_string()).as_str(), subsection.name.span());
+ let subsection_name_lit = Literal::string(subsection.name.to_string().as_str());
+ quote!{
+ #subsection_name: #subsection_type_name::deser(cfg, (prefix.to_owned() + #subsection_name_lit + ".").as_str())?
+ }
+ }).collect::>();
+
+ let section_type_name_ident = Ident::new(capitalize_name(section.name.to_string().as_str()).as_str(), section.name.span());
+
+ out.extend(quote! {
+ impl #section_type_name_ident {
+ fn deser(cfg: &_config_deser::Config, prefix: &str) -> Result {
+ Ok (Self {
+ #(#var_deser_parts,)*
+ #(#subsection_deser_parts,)*
+ })
+ }
+ }
+ })
+}
+
+fn emit_deser_impl(out: &mut proc_macro2::TokenStream, definition: ParseConfigDefinition) {
+ // name of the config object
+ let cfg_type_name = capitalize_name(&definition.name.to_string());
+ let cfg_type_name_ident = Ident::new(cfg_type_name.as_str(), definition.name.span());
+
+ // this is a new time
+ out.extend(quote! {
+ pub fn lfcm_load_config(files: Vec<&str>) -> Result<#cfg_type_name_ident, LFCMError> {
+ let mut _read_config = _config_deser::Config::builder();
+ for path in files {
+ _read_config = _read_config.add_source(_config_deser::File::with_name(path));
+ }
+ let _cfg_data = match _read_config.build() {
+ Err (error) => {
+ return Err (LFCMError::UnableToBuild)
+ },
+ Ok (cfg) => cfg
+ };
+
+ #cfg_type_name_ident::deser(&_cfg_data, "")
+ }
+ });
}
\ No newline at end of file
diff --git a/tests/fixtures/test1.yaml b/tests/fixtures/test1.yaml
new file mode 100644
index 0000000..2cf60f7
--- /dev/null
+++ b/tests/fixtures/test1.yaml
@@ -0,0 +1 @@
+rand: 15
\ No newline at end of file
diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs
index 5d54e24..56d85ee 100644
--- a/tests/integration_tests.rs
+++ b/tests/integration_tests.rs
@@ -1,5 +1,7 @@
+use config::ConfigError;
use lfcm::config;
+
config! {
param name = Config;
section top {
@@ -7,11 +9,21 @@ config! {
section database {
var seed: u64 = 42;
}
+ section test {
+ var reps: u64 = 10;
+ }
}
}
+
#[test]
fn test_parser() {
- //println!("{:?}");
+ let config = match lfcm_load_config(vec![
+ "tests/fixtures/test1.yaml",
+ ]) {
+ Ok(config) => config,
+ Err(e) => return,
+ };
+ println!("{:?}", config);
}
\ No newline at end of file