mod generator;
mod utils;
use std::{fmt::Write, str::FromStr};
use proc_macro::TokenStream as TokenStream0;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse::Parser, punctuated::Punctuated, *};
use generator::*;
use utils::{config::*, field::*, parser::*, top_level::*};
#[proc_macro_attribute]
pub fn config(attrs: TokenStream0, input: TokenStream0) -> TokenStream0 {
let parser = Punctuated::<Meta, Token![,]>::parse_terminated;
let attrs = parser.parse(attrs).unwrap();
let mut annotations = String::from("#[derive(::config_manager::__private::__Config__)]");
attrs.iter().for_each(|attr| {
std::write!(&mut annotations, "\n{}", (quote! { #[#attr]})).unwrap();
});
let mut annotations =
TokenStream0::from_str(&annotations).expect("can't parse annotations as tokenstream");
annotations.extend(input.into_iter());
annotations
}
#[proc_macro_derive(
__Config__,
attributes(
source,
flatten,
subcommand,
config,
env_prefix,
clap,
global_name,
file,
table,
default_order,
__debug_cmd_input__
)
)]
pub fn generate_config(input: TokenStream0) -> TokenStream0 {
let input = parse_macro_input!(input as DeriveInput);
let class_ident = input.ident;
let AppTopLevelInfo {
env_prefix,
clap_app_info,
configs,
debug_cmd_input,
table_name,
default_order,
} = AppTopLevelInfo::extract(&input.attrs);
let class: DataStruct = match input.data {
Data::Struct(s) => s,
_ => panic!("config macro input should be a Struct"),
};
unzip_n::unzip_n!(2);
let (fields_json_definition, clap_fields): (
Vec<(proc_macro2::Ident, TokenStream)>,
Vec<ClapInitialization>,
) = class
.fields
.into_iter()
.map(|field| {
let ProcessFieldResult {
name,
clap_field,
initialization,
} = if field_is_flatten(&field) {
process_flatten_field(field)
} else if field_is_subcommand(&field) {
process_subcommand_field(field, &debug_cmd_input)
} else {
process_field(field, &table_name, &default_order)
};
((name, initialization), clap_field)
})
.unzip_n();
generate_final_struct_and_supporting_code(InitializationInfo {
env_prefix,
class_ident,
clap_app_info,
configs,
clap_fields,
fields_json_definition,
debug_cmd_input,
})
.into()
}
#[proc_macro_derive(Flatten, attributes(source, flatten, subcommand, table, default_order))]
pub fn generate_flatten(input: TokenStream0) -> TokenStream0 {
let input = parse_macro_input!(input as DeriveInput);
let table_name = extract_table_name(&input.attrs);
let default_order = extract_source_order(&input.attrs);
let class_ident = input.ident;
let class: DataStruct = match input.data {
Data::Struct(s) => s,
_ => panic!("config macro input should be a Struct"),
};
unzip_n::unzip_n!(2);
let (fields_json_definition, clap_fields): (
Vec<(proc_macro2::Ident, TokenStream)>,
Punctuated<ClapInitialization, Token![.]>,
) = class
.fields
.into_iter()
.map(|field| {
let ProcessFieldResult {
name,
clap_field,
initialization,
} = if field_is_flatten(&field) {
process_flatten_field(field)
} else if field_is_subcommand(&field) {
panic!("subcommands are forbidden in the nested structures")
} else {
process_field(field, &table_name, &default_order)
};
((name, initialization), clap_field)
})
.unzip_n();
generate_flatten_implementation(class_ident, clap_fields, fields_json_definition).into()
}