first working version
This commit is contained in:
parent
92b8593be9
commit
cd2fcf90e1
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
.idea/
|
||||
1
.idea/lfcm.iml
generated
1
.idea/lfcm.iml
generated
@ -4,6 +4,7 @@
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.idea/dictionaries" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
|
||||
@ -15,4 +15,5 @@ proc-macro = true
|
||||
quote = "1"
|
||||
proc-macro2 = "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_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<ParseConfigSection> = 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("<no-name?>".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),*
|
||||
@ -228,3 +236,66 @@ 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::<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;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user