tonysd_config_manager_proc/
lib.rs1mod generator;
5mod utils;
6
7use std::{fmt::Write, str::FromStr};
8
9use proc_macro::TokenStream as TokenStream0;
10use proc_macro2::TokenStream;
11use quote::{quote, ToTokens};
12use syn::{parse::Parser, punctuated::Punctuated, *};
13
14use generator::*;
15use utils::{config::*, field::*, parser::*, top_level::*};
16
17#[proc_macro_attribute]
22pub fn config(attrs: TokenStream0, input: TokenStream0) -> TokenStream0 {
23 let parser = Punctuated::<Meta, Token![,]>::parse_terminated;
24 let attrs = parser.parse(attrs).unwrap();
25 let mut annotations = String::from("#[derive(::config_manager::__private::__Config__)]");
26 attrs.iter().for_each(|attr| {
27 std::write!(&mut annotations, "\n{}", (quote! { #[#attr]})).unwrap();
28 });
29 let mut annotations =
30 TokenStream0::from_str(&annotations).expect("can't parse annotations as tokenstream");
31 annotations.extend(input.into_iter());
32 annotations
33}
34
35#[proc_macro_derive(
36 __Config__,
37 attributes(
38 source,
39 flatten,
40 subcommand,
41 config,
42 env_prefix,
43 clap,
44 global_name,
45 file,
46 table,
47 default_order,
48 __debug_cmd_input__
49 )
50)]
51pub fn generate_config(input: TokenStream0) -> TokenStream0 {
52 let input = parse_macro_input!(input as DeriveInput);
53
54 let class_ident = input.ident;
55
56 let AppTopLevelInfo {
57 env_prefix,
58 clap_app_info,
59 configs,
60 debug_cmd_input,
61 table_name,
62 default_order,
63 } = AppTopLevelInfo::extract(&input.attrs);
64
65 let class: DataStruct = match input.data {
66 Data::Struct(s) => s,
67 _ => panic!("config macro input should be a Struct"),
68 };
69
70 unzip_n::unzip_n!(2);
71 let (fields_json_definition, clap_fields): (
72 Vec<(proc_macro2::Ident, TokenStream)>,
73 Vec<ClapInitialization>,
74 ) = class
75 .fields
76 .into_iter()
77 .map(|field| {
78 let ProcessFieldResult {
79 name,
80 clap_field,
81 initialization,
82 } = if field_is_flatten(&field) {
83 process_flatten_field(field)
84 } else if field_is_subcommand(&field) {
85 process_subcommand_field(field, &debug_cmd_input)
86 } else {
87 process_field(field, &table_name, &default_order)
88 };
89 ((name, initialization), clap_field)
90 })
91 .unzip_n();
92
93 generate_final_struct_and_supporting_code(InitializationInfo {
94 env_prefix,
95 class_ident,
96 clap_app_info,
97 configs,
98 clap_fields,
99 fields_json_definition,
100 debug_cmd_input,
101 })
102 .into()
103}
104
105#[proc_macro_derive(Flatten, attributes(source, flatten, subcommand, table, default_order))]
108pub fn generate_flatten(input: TokenStream0) -> TokenStream0 {
109 let input = parse_macro_input!(input as DeriveInput);
110
111 let table_name = extract_table_name(&input.attrs);
112 let default_order = extract_source_order(&input.attrs);
113
114 let class_ident = input.ident;
115 let class: DataStruct = match input.data {
116 Data::Struct(s) => s,
117 _ => panic!("config macro input should be a Struct"),
118 };
119
120 unzip_n::unzip_n!(2);
121 let (fields_json_definition, clap_fields): (
122 Vec<(proc_macro2::Ident, TokenStream)>,
123 Punctuated<ClapInitialization, Token![.]>,
124 ) = class
125 .fields
126 .into_iter()
127 .map(|field| {
128 let ProcessFieldResult {
129 name,
130 clap_field,
131 initialization,
132 } = if field_is_flatten(&field) {
133 process_flatten_field(field)
134 } else if field_is_subcommand(&field) {
135 panic!("subcommands are forbidden in the nested structures")
136 } else {
137 process_field(field, &table_name, &default_order)
138 };
139 ((name, initialization), clap_field)
140 })
141 .unzip_n();
142
143 generate_flatten_implementation(class_ident, clap_fields, fields_json_definition).into()
144}