1use proc_macro::TokenStream;
2use quote::quote;
3use syn::DeriveInput;
4
5fn is_required(attrs: &[syn::Attribute]) -> bool {
6 attrs.iter().any(|attr| attr.path().is_ident("required"))
7}
8
9fn get_rename(attrs: &[syn::Attribute]) -> Option<proc_macro2::TokenStream> {
10 for attr in attrs {
11 if attr.path().is_ident("serde") {
13 if let syn::Attribute {
14 meta: syn::Meta::List(syn::MetaList { tokens, .. }),
15 ..
16 } = attr
17 {
18 let tokens: Vec<proc_macro2::TokenTree> = tokens.clone().into_iter().collect();
19 if let Some(proc_macro2::TokenTree::Ident(ident)) = tokens.first() {
20 if *ident == "rename" {
21 if let Some(proc_macro2::TokenTree::Literal(name)) = tokens.get(2) {
22 let value = name.to_string();
24
25 if let Ok(lit) = syn::parse_str::<syn::LitStr>(&value) {
27 return Some(quote! { #[serde(rename = #lit)] });
28 }
29 }
30 }
31 }
32 }
33 }
34 }
35 None
36}
37
38fn get_rename_all(attrs: &[syn::Attribute]) -> Option<proc_macro2::TokenStream> {
39 for attr in attrs {
40 if attr.path().is_ident("serde") {
42 if let syn::Attribute {
43 meta: syn::Meta::List(syn::MetaList { tokens, .. }),
44 ..
45 } = attr
46 {
47 let tokens: Vec<proc_macro2::TokenTree> = tokens.clone().into_iter().collect();
48 if let Some(proc_macro2::TokenTree::Ident(ident)) = tokens.first() {
49 if *ident == "rename_all" {
50 if let Some(proc_macro2::TokenTree::Literal(name)) = tokens.get(2) {
51 let value = name.to_string();
53
54 if let Ok(lit) = syn::parse_str::<syn::LitStr>(&value) {
56 return Some(quote! { #[serde(rename_all = #lit)] });
57 }
58 }
59 }
60 }
61 }
62 }
63 }
64 None
65}
66
67#[proc_macro_derive(New, attributes(required))]
68pub fn derive(input: TokenStream) -> TokenStream {
69 let input = syn::parse_macro_input!(input as DeriveInput);
70 let ident = &input.ident;
71 let builder_ident = syn::Ident::new(&format!("New{}", ident), ident.span());
72
73 let fields = if let syn::Data::Struct(syn::DataStruct {
74 fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
75 ..
76 }) = input.data
77 {
78 named.iter()
79 } else {
80 unimplemented!()
81 };
82
83 let new_fields = fields.clone().map(|field| {
84 let name = &field.ident;
85 if !is_required(&field.attrs) {
86 quote! {#name: std::option::Option::None}
87 } else {
88 quote! {#name}
89 }
90 });
91
92 let required_args = fields.clone().filter_map(|field| {
93 if is_required(&field.attrs) {
94 let name = &field.ident;
95 let ty = &field.ty;
96 Some(quote! {#name: #ty})
97 } else {
98 None
99 }
100 });
101
102 let options = fields.clone().map(|field| {
103 let name = &field.ident;
104 let ty = &field.ty;
105 let rename = get_rename(&field.attrs);
106 if !is_required(&field.attrs) {
107 quote! {
108 #rename
109 #[serde(skip_serializing_if = "Option::is_none")]
110 #name: std::option::Option<#ty>}
111 } else {
112 quote! {
113 #rename
114 #name: #ty
115 }
116 }
117 });
118
119 let funcs = fields.clone().map(|field| {
120 let name = &field.ident;
121 if let Some(name) = name {
122 let getter = format!("get_{}", name);
123 let getter = proc_macro2::Ident::new(&getter, name.span());
124 let ty = &field.ty;
125 if !is_required(&field.attrs) {
126 quote! {
127 pub fn #name(mut self, #name: #ty) -> Self {
128 self.#name = std::option::Option::Some(#name);
129 self
130 }
131 pub fn #getter(&self) -> &std::option::Option<#ty> {
132 &self.#name
133 }
134 }
135 } else {
136 quote! {
137 pub fn #name(mut self, #name: #ty) -> Self {
138 self.#name = #name;
139 self
140 }
141
142 pub fn #getter(&self) -> &#ty {
143 &self.#name
144 }
145 }
146 }
147 } else {
148 quote! {}
150 }
151 });
152
153 let from = fields.clone().map(|field| {
154 let name = &field.ident;
155 if !is_required(&field.attrs) {
156 quote! { #name: Some(value.#name) }
157 } else {
158 quote! { #name: value.#name }
159 }
160 });
161
162 let rename_all = get_rename_all(&input.attrs);
163
164 let expanded = quote! {
165 #[derive(Clone, Debug, PartialEq, serde::Serialize)]
166 #rename_all
167 pub struct #builder_ident {
168 #(#options),*
169 }
170
171 impl #builder_ident {
172 pub fn new(#(#required_args),*) -> Self {
173 Self {
174 #(#new_fields),*
175 }
176 }
177
178 #(#funcs)*
179 }
180
181 impl From<#ident> for #builder_ident {
182 fn from(value: #ident) -> Self {
183 Self {
184 #(#from),*
185 }
186 }
187
188 }
189 };
190
191 expanded.into()
192}
193
194#[cfg(test)]
195mod tests {
196 #[test]
197 fn test_name() {
198 let t = trybuild::TestCases::new();
199 t.pass("tests/ui/*.rs");
200 }
201}