first working version
This commit is contained in:
parent
92b8593be9
commit
cd2fcf90e1
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
.idea/
|
||||||
1
.idea/lfcm.iml
generated
1
.idea/lfcm.iml
generated
@ -4,6 +4,7 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.idea/dictionaries" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
|
|||||||
@ -15,4 +15,5 @@ proc-macro = true
|
|||||||
quote = "1"
|
quote = "1"
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
syn = "1.0"
|
syn = "1.0"
|
||||||
|
config = "0.15.25"
|
||||||
|
|
||||||
|
|||||||
97
src/lib.rs
97
src/lib.rs
@ -1,9 +1,8 @@
|
|||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
extern crate proc_macro2;
|
extern crate proc_macro2;
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use proc_macro2::{Group, TokenTree};
|
use proc_macro2::Literal;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::parse::{Parse, ParseStream};
|
use syn::parse::{Parse, ParseStream};
|
||||||
use syn::{parse_macro_input, Expr, Type, Ident, braced};
|
use syn::{parse_macro_input, Expr, Type, Ident, braced};
|
||||||
@ -148,26 +147,34 @@ impl Debug for ParseConfigDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[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
|
// parse the input and prepare the output
|
||||||
let definition: ParseConfigDefinition = parse_macro_input!(input as ParseConfigDefinition);
|
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
|
// generate the class Structure
|
||||||
let mut config_sections: Vec<ParseConfigSection> = Vec::new();
|
let mut config_sections: Vec<ParseConfigSection> = Vec::new();
|
||||||
walk_config_sections(&definition, &mut config_sections);
|
walk_config_sections(&definition, &mut config_sections);
|
||||||
|
|
||||||
for config_section in config_sections.iter() {
|
|
||||||
println!("Section: {}", config_section.name.span().source_text().unwrap_or("<no-name?>".to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
for section in config_sections.iter() {
|
|
||||||
emit_section_struct(&mut out, section)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: generate implementations for necessary output
|
// 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)
|
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 = capitalize_name(§ion.name.to_string());
|
||||||
let type_name_ident = Ident::new(type_name.as_str(), section.name.span());
|
let type_name_ident = Ident::new(type_name.as_str(), section.name.span());
|
||||||
let struct_stream = quote! {
|
let struct_stream = quote! {
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct #type_name_ident {
|
pub struct #type_name_ident {
|
||||||
#(#vars),*
|
#(#vars),*
|
||||||
#(#subsections),*
|
#(#subsections),*
|
||||||
@ -227,4 +235,67 @@ fn emit_section_struct(out: &mut proc_macro2::TokenStream, section: &ParseConfig
|
|||||||
};
|
};
|
||||||
|
|
||||||
out.extend(struct_stream.into_iter());
|
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::<Vec<_>>();
|
||||||
|
|
||||||
|
// 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::<Vec<_>>();
|
||||||
|
|
||||||
|
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<Self, LFCMError> {
|
||||||
|
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, "")
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
1
tests/fixtures/test1.yaml
vendored
Normal file
1
tests/fixtures/test1.yaml
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
rand: 15
|
||||||
@ -1,5 +1,7 @@
|
|||||||
|
use config::ConfigError;
|
||||||
use lfcm::config;
|
use lfcm::config;
|
||||||
|
|
||||||
|
|
||||||
config! {
|
config! {
|
||||||
param name = Config;
|
param name = Config;
|
||||||
section top {
|
section top {
|
||||||
@ -7,11 +9,21 @@ config! {
|
|||||||
section database {
|
section database {
|
||||||
var seed: u64 = 42;
|
var seed: u64 = 42;
|
||||||
}
|
}
|
||||||
|
section test {
|
||||||
|
var reps: u64 = 10;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parser() {
|
fn test_parser() {
|
||||||
//println!("{:?}");
|
let config = match lfcm_load_config(vec![
|
||||||
|
"tests/fixtures/test1.yaml",
|
||||||
|
]) {
|
||||||
|
Ok(config) => config,
|
||||||
|
Err(e) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{:?}", config);
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user