1use proc_macro2::{self, TokenStream};
2use quote::quote;
3use syn::{DeriveInput, parse2};
4use crate::r#enum::MapEnum;
5
6use crate::r#struct::MapStruct;
7
8mod r#struct;
9mod r#enum;
10mod named_field_change;
11mod generic;
12mod variant;
13mod transformer;
14mod struct_change;
15mod enum_change;
16mod tuple_change;
17mod unnamed_field_change;
18
19#[macro_export]
20macro_rules! unwrap_one_variant {
21 ($expression:expr, $pattern:pat $(if $guard:expr)?, $extract:expr $(,)?) => {
22 match $expression {
23 $pattern $(if $guard)? => $extract,
24 _ => unreachable!(),
25 }
26 };
27}
28
29pub fn derive(input: TokenStream) -> TokenStream {
30 match syn_derive(input) {
31 Ok(input) => quote! {
32 #(#input)*
33 },
34 Err(err) => err.to_compile_error(),
35 }
36}
37
38fn syn_derive(input: TokenStream) -> syn::Result<Vec<DeriveInput>> {
39 let input = parse2::<DeriveInput>(input)?;
40 let attrs = input.attrs
41 .iter()
42 .filter(|attr| attr.path().is_ident("mapstruct"))
43 .map(|attr| &attr.meta)
44 .map(|meta| match meta {
45 syn::Meta::List(list) => Ok(list.tokens.clone()),
46 _ => Err(syn::Error::new_spanned(meta, "expected #[mapstruct(...)]"))
47 })
48 .collect::<Result<Vec<_>, _>>()?;
49
50 match input.data {
52 syn::Data::Struct(_) => {
53 attrs.into_iter()
54 .map(|tokens| parse2::<MapStruct>(tokens))
55 .collect::<Result<Vec<_>, _>>()?
56 .into_iter()
57 .map(|mapstruct| mapstruct.transform(input.clone()))
58 .collect()
59 },
60 syn::Data::Enum(_) => {
61 attrs.into_iter()
62 .map(|tokens| parse2::<MapEnum>(tokens))
63 .collect::<Result<Vec<_>, _>>()?
64 .into_iter()
65 .map(|mapenum| mapenum.transform(input.clone()))
66 .collect()
67 },
68 _ => return Err(syn::Error::new_spanned(input, "expected struct or enum")),
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn test_derive_struct() {
78 let input = quote! {
79 #[mapstruct(
80 #[derive(Debug)]
81 struct Y<
82 +'a,
83 +T,
84 > {
85 ~id -> x_id,
86 ~name: &'a str,
87 ~some: &'a str,
88 +last_name: &'a str,
89 -height,
90 +t: T,
91 }
92 )]
93 struct X {
94 id: i64,
95 name: String,
96 age: i32,
97 height: f32,
98 some: String,
99 }
100 };
101 let expected = quote! {
102 #[derive(Debug)]
103 struct Y<'a, T> {
104 x_id: i64,
105 name: &'a str,
106 age: i32,
107 some: &'a str,
108 last_name: &'a str,
109 t: T
110 }
111 };
112 assert_eq!(expected.to_string(), derive(input).to_string());
113 }
114
115 #[test]
116 fn test_derive_enum_tuple() {
117 let input = quote! {
118 #[mapstruct(
119 #[derive(Debug)]
120 enum Y {
121 -A,
122 +D(i8),
123 }
124 )]
125 enum X {
126 A(i64),
127 B(i32),
128 C(i16),
129 }
130 };
131 let expected = quote! {
132 #[derive(Debug)]
133 enum Y {
134 B(i32),
135 C(i16),
136 D(i8)
137 }
138 };
139 assert_eq!(expected.to_string(), derive(input).to_string());
140 }
141
142 #[test]
143 fn test_derive_enum_struct() {
144 let input = quote! {
145 #[mapstruct(
146 #[derive(Debug)]
147 enum Y {
148 -A,
149 ~B {
150 -name,
151 },
152 ~C {
153 -name,
154 },
155 +D {
156 id: i8,
157 },
158 }
159 )]
160 enum X {
161 A {
162 id: i64,
163 name: String,
164 },
165 B {
166 id: i32,
167 name: String,
168 },
169 C {
170 id: i16,
171 name: String,
172 },
173 }
174 };
175 let expected = quote! {
176 #[derive(Debug)]
177 enum Y {
178 B {
179 id: i32
180 },
181 C {
182 id: i16
183 },
184 D {
185 id: i8,
186 }
187 }
188 };
189 assert_eq!(expected.to_string(), derive(input).to_string());
190 }
191
192 #[test]
193 fn test_derive_enum_struct_replace() {
194 let input = quote! {
195 #[mapstruct(
196 #[derive(Debug)]
197 enum Y {
198 ~B {
199 -name,
200 },
201 ~C {
202 -name,
203 },
204 ~A -> D {
205 ~id: i8,
206 -name,
207 },
208 }
209 )]
210 enum X {
211 A {
212 id: i64,
213 name: String,
214 },
215 B {
216 id: i32,
217 name: String,
218 },
219 C {
220 id: i16,
221 name: String,
222 },
223 }
224 };
225 let expected = quote! {
226 #[derive(Debug)]
227 enum Y {
228 D {
229 id: i8
230 },
231 B {
232 id: i32
233 },
234 C {
235 id: i16
236 }
237 }
238 };
239 assert_eq!(expected.to_string(), derive(input).to_string());
240 }
241
242 #[test]
243 fn test_derive_enum_tuple_replace() {
244 let input = quote! {
245 #[mapstruct(
246 #[derive(Debug)]
247 pub enum Y {
248 A(i64),
249 B(i32),
250 C(i16),
251 +D(i8),
252 }
253 )]
254 enum X {
255 A {
256 id: i64,
257 name: String,
258 },
259 B {
260 id: i32,
261 name: String,
262 },
263 C {
264 id: i16,
265 name: String,
266 },
267 }
268 };
269 let expected = quote! {
270 #[derive(Debug)]
271 pub enum Y {
272 A(i64),
273 B(i32),
274 C(i16),
275 D(i8)
276 }
277 };
278 assert_eq!(expected.to_string(), derive(input).to_string());
279 }
280
281 #[test]
282 fn test_derive_enum_tuple_change() {
283 let input = quote! {
284 #[mapstruct(
285 #[derive(Debug)]
286 pub enum Y {
287 ~A(_, _, _, +i64),
288 ~B(_, _, ~i128),
289 ~C(_, _, ~u16, +u8),
290 +E(i8, i16, i32, i64),
291 }
292 )]
293 enum X {
294 A(i8, i16, i32),
295 B(i32, i64, i16),
296 C(i16, i8, i32),
297 D(i8, i16, i32, i64),
298 }
299 };
300 let expected = quote! {
301 #[derive(Debug)]
302 pub enum Y {
303 A(i8, i16, i32, i64),
304 B(i32, i64, i128),
305 C(i16, i8, u16, u8),
306 D(i8, i16, i32, i64),
307 E(i8, i16, i32, i64)
308 }
309 };
310 assert_eq!(expected.to_string(), derive(input).to_string());
311 }
312}