srcpos_get_derive/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2;
3use proc_macro2::Span;
4use quote::{format_ident, quote, quote_spanned};
5use syn::{parse_macro_input, DeriveInput, Ident, LitInt};
6
7/// # Example
8/// ```
9/// # use srcpos_get::*;
10/// #[derive(GetLoc)]
11/// struct A {
12///    loc: Loc,
13/// }
14///
15/// #[derive(GetLoc)]
16/// struct B {
17///     #[loc]
18///     a: Loc,
19/// }
20///
21/// #[derive(GetLoc)]
22/// struct C(Loc);
23///
24/// #[derive(GetLoc)]
25/// struct D(u8, #[loc] Loc);
26///
27/// #[derive(GetLoc)]
28/// enum E {
29///     A(Loc),
30///     B(u8, #[loc] Loc),
31///     C {
32///         loc: Loc,
33///     },
34///     D {
35///         #[loc]
36///         a: Loc,
37///         _b: u8,
38///     },
39/// }
40/// ```
41#[proc_macro_derive(GetLoc, attributes(loc))]
42pub fn derive_get_loc(item: TokenStream) -> TokenStream {
43    let input = parse_macro_input!(item as DeriveInput);
44    let name = input.ident;
45    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
46
47    match input.data {
48        syn::Data::Struct(v) => match v.fields {
49            syn::Fields::Named(v) => {
50                let mut has_loc = false;
51                let mut loc_id: Option<&Ident> = None;
52                for f in v.named.iter() {
53                    #[allow(unused_assignments)]
54                    if f.ident.as_ref().map(|id| id == "loc").unwrap_or(false) {
55                        has_loc = true;
56                        loc_id = f.ident.as_ref();
57                        break;
58                    }
59                    for attr in f.attrs.iter() {
60                        let path = &attr.path;
61                        if path.get_ident().map(|id| id == "loc").unwrap_or(false) {
62                            if !has_loc && loc_id.is_some() {
63                                return syn::Error::new(
64                                    Span::call_site(),
65                                    "Cannot have multiple loc",
66                                )
67                                .to_compile_error()
68                                .into();
69                            }
70                            loc_id = f.ident.as_ref();
71                            if loc_id.is_none() {
72                                return syn::Error::new(Span::call_site(), "Field has no name")
73                                    .to_compile_error()
74                                    .into();
75                            }
76                        }
77                    }
78                }
79                if loc_id.is_none() {
80                    return syn::Error::new(Span::call_site(), "Not found field loc")
81                        .to_compile_error()
82                        .into();
83                }
84                let loc_id = loc_id.unwrap();
85                let imp = quote! {
86                    impl #impl_generics ::srcpos_get::GetLoc for #name #ty_generics #where_clause {
87                        fn loc(&self) -> ::srcpos_get::Loc {
88                            use ::srcpos_get::GetLoc;
89                            self.#loc_id.loc()
90                        }
91                    }
92                };
93                return imp.into();
94            }
95            syn::Fields::Unnamed(v) => {
96                let mut loc_id: Option<usize> = None;
97                for (i, f) in v.unnamed.iter().enumerate() {
98                    for attr in f.attrs.iter() {
99                        let path = &attr.path;
100                        if path.get_ident().map(|id| id == "loc").unwrap_or(false) {
101                            if loc_id.is_some() {
102                                return syn::Error::new(
103                                    Span::call_site(),
104                                    "Cannot have multiple loc",
105                                )
106                                .to_compile_error()
107                                .into();
108                            }
109                            loc_id = Some(i);
110                        }
111                    }
112                }
113                if v.unnamed.len() == 0 {
114                    return syn::Error::new(Span::call_site(), "There is nothing to get")
115                        .to_compile_error()
116                        .into();
117                }
118                if v.unnamed.len() == 1 && loc_id.is_none() {
119                    loc_id = Some(0);
120                }
121                if loc_id.is_none() {
122                    return syn::Error::new(Span::call_site(), "Not found field loc")
123                        .to_compile_error()
124                        .into();
125                }
126                let loc_id = loc_id.unwrap().to_string();
127                let loc_id = LitInt::new(loc_id.as_str(), Span::call_site());
128                let imp = quote! {
129                    impl #impl_generics ::srcpos_get::GetLoc for #name #ty_generics #where_clause {
130                        fn loc(&self) -> ::srcpos_get::Loc {
131                            use ::srcpos_get::GetLoc;
132                            self.#loc_id.loc()
133                        }
134                    }
135                };
136                return imp.into();
137            }
138            syn::Fields::Unit => {
139                return syn::Error::new(Span::call_site(), "There is nothing to get")
140                    .to_compile_error()
141                    .into();
142            }
143        },
144        syn::Data::Enum(v) => {
145            let mut vimps = vec![];
146            if v.variants.len() == 0 {
147                return syn::Error::new(Span::call_site(), "There is nothing to get")
148                    .to_compile_error()
149                    .into();
150            }
151            for variant in v.variants.iter() {
152                match &variant.fields {
153                    syn::Fields::Named(v) => {
154                        let mut has_loc = false;
155                        let mut loc_id: Option<&Ident> = None;
156                        for f in v.named.iter() {
157                            #[allow(unused_assignments)]
158                            if f.ident.as_ref().map(|id| id == "loc").unwrap_or(false) {
159                                has_loc = true;
160                                loc_id = f.ident.as_ref();
161                                break;
162                            }
163                            for attr in f.attrs.iter() {
164                                let path = &attr.path;
165                                if path.get_ident().map(|id| id == "loc").unwrap_or(false) {
166                                    if !has_loc && loc_id.is_some() {
167                                        return syn::Error::new(
168                                            variant.ident.span(),
169                                            "[GetLoc] Cannot have multiple loc",
170                                        )
171                                        .to_compile_error()
172                                        .into();
173                                    }
174                                    loc_id = f.ident.as_ref();
175                                    if loc_id.is_none() {
176                                        return syn::Error::new(
177                                            variant.ident.span(),
178                                            "[GetLoc] Field has no name",
179                                        )
180                                        .to_compile_error()
181                                        .into();
182                                    }
183                                }
184                            }
185                        }
186                        if loc_id.is_none() {
187                            return syn::Error::new(
188                                variant.ident.span(),
189                                "[GetLoc] Not found field loc",
190                            )
191                            .to_compile_error()
192                            .into();
193                        }
194                        let loc_id = loc_id.unwrap();
195                        let vname = &variant.ident;
196                        let imp = quote_spanned! { variant.ident.span() => Self::#vname { #loc_id, .. } => #loc_id.loc() };
197                        vimps.push(imp);
198                    }
199                    syn::Fields::Unnamed(v) => {
200                        let mut loc_id: Option<usize> = None;
201                        for (i, f) in v.unnamed.iter().enumerate() {
202                            for attr in f.attrs.iter() {
203                                let path = &attr.path;
204                                if path.get_ident().map(|id| id == "loc").unwrap_or(false) {
205                                    if loc_id.is_some() {
206                                        return syn::Error::new(
207                                            variant.ident.span(),
208                                            "[GetLoc] Cannot have multiple loc",
209                                        )
210                                        .to_compile_error()
211                                        .into();
212                                    }
213                                    loc_id = Some(i);
214                                }
215                            }
216                        }
217                        if v.unnamed.len() == 0 {
218                            return syn::Error::new(
219                                variant.ident.span(),
220                                "[GetLoc] There is nothing to get",
221                            )
222                            .to_compile_error()
223                            .into();
224                        }
225                        if v.unnamed.len() == 1 && loc_id.is_none() {
226                            loc_id = Some(0);
227                        }
228                        if loc_id.is_none() {
229                            return syn::Error::new(
230                                variant.ident.span(),
231                                "[GetLoc] Not found field loc",
232                            )
233                            .to_compile_error()
234                            .into();
235                        }
236                        let loc_id = loc_id.unwrap();
237                        let ids = (0..(variant.fields.len())).into_iter().map(|i| {
238                            if i != loc_id {
239                                format_ident!("_")
240                            } else {
241                                format_ident!("v{}", i)
242                            }
243                        });
244                        let loc_id = format_ident!("v{}", loc_id);
245                        let vname = &variant.ident;
246                        let imp = quote_spanned! { variant.ident.span() => Self::#vname(#(#ids),*) => #loc_id.loc() };
247                        vimps.push(imp);
248                    }
249                    syn::Fields::Unit => {
250                        return syn::Error::new(
251                            variant.ident.span(),
252                            "[GetLoc] There is nothing to get",
253                        )
254                        .to_compile_error()
255                        .into();
256                    }
257                }
258            }
259            let imp = quote! {
260                impl #impl_generics ::srcpos_get::GetLoc for #name #ty_generics #where_clause {
261                    fn loc(&self) -> ::srcpos_get::Loc {
262                        use ::srcpos_get::GetLoc;
263                        match self {
264                            #(#vimps),*
265                        }
266                    }
267                }
268            };
269            return imp.into();
270        }
271        syn::Data::Union(_) => {
272            return syn::Error::new(Span::call_site(), "Does not support union")
273                .to_compile_error()
274                .into();
275        }
276    }
277}
278
279/// # Example
280/// ```
281/// # use srcpos_get::*;
282/// #[derive(GetPos)]
283/// struct A {
284///    pos: Pos,
285/// }
286///
287/// #[derive(GetPos)]
288/// struct B {
289///     #[pos]
290///     a: Pos,
291/// }
292///
293/// #[derive(GetPos)]
294/// struct C(Pos);
295///
296/// #[derive(GetPos)]
297/// struct D(u8, #[pos] Pos);
298///
299/// #[derive(GetPos)]
300/// enum E {
301///     A(Pos),
302///     B(u8, #[pos] Pos),
303///     C {
304///         pos: Pos,
305///     },
306///     D {
307///         #[pos]
308///         a: Pos,
309///         _b: u8,
310///     },
311/// }
312/// ```
313#[proc_macro_derive(GetPos, attributes(pos))]
314pub fn derive_get_pos(item: TokenStream) -> TokenStream {
315    let input = parse_macro_input!(item as DeriveInput);
316    let name = input.ident;
317    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
318
319    match input.data {
320        syn::Data::Struct(v) => match v.fields {
321            syn::Fields::Named(v) => {
322                let mut has_loc = false;
323                let mut loc_id: Option<&Ident> = None;
324                for f in v.named.iter() {
325                    #[allow(unused_assignments)]
326                    if f.ident.as_ref().map(|id| id == "pos").unwrap_or(false) {
327                        has_loc = true;
328                        loc_id = f.ident.as_ref();
329                        break;
330                    }
331                    for attr in f.attrs.iter() {
332                        let path = &attr.path;
333                        if path.get_ident().map(|id| id == "pos").unwrap_or(false) {
334                            if !has_loc && loc_id.is_some() {
335                                return syn::Error::new(
336                                    Span::call_site(),
337                                    "Cannot have multiple pos",
338                                )
339                                .to_compile_error()
340                                .into();
341                            }
342                            loc_id = f.ident.as_ref();
343                            if loc_id.is_none() {
344                                return syn::Error::new(Span::call_site(), "Field has no name")
345                                    .to_compile_error()
346                                    .into();
347                            }
348                        }
349                    }
350                }
351                if loc_id.is_none() {
352                    return syn::Error::new(Span::call_site(), "Not found field pos")
353                        .to_compile_error()
354                        .into();
355                }
356                let loc_id = loc_id.unwrap();
357                let imp = quote! {
358                    impl #impl_generics ::srcpos_get::GetPos for #name #ty_generics #where_clause {
359                        fn pos(&self) -> ::srcpos_get::Pos {
360                            use ::srcpos_get::GetPos;
361                            self.#loc_id.pos()
362                        }
363                    }
364                };
365                return imp.into();
366            }
367            syn::Fields::Unnamed(v) => {
368                let mut loc_id: Option<usize> = None;
369                for (i, f) in v.unnamed.iter().enumerate() {
370                    for attr in f.attrs.iter() {
371                        let path = &attr.path;
372                        if path.get_ident().map(|id| id == "pos").unwrap_or(false) {
373                            if loc_id.is_some() {
374                                return syn::Error::new(
375                                    Span::call_site(),
376                                    "Cannot have multiple pos",
377                                )
378                                .to_compile_error()
379                                .into();
380                            }
381                            loc_id = Some(i);
382                        }
383                    }
384                }
385                if v.unnamed.len() == 0 {
386                    return syn::Error::new(Span::call_site(), "There is nothing to get")
387                        .to_compile_error()
388                        .into();
389                }
390                if v.unnamed.len() == 1 && loc_id.is_none() {
391                    loc_id = Some(0);
392                }
393                if loc_id.is_none() {
394                    return syn::Error::new(Span::call_site(), "Not found field pos")
395                        .to_compile_error()
396                        .into();
397                }
398                let loc_id = loc_id.unwrap().to_string();
399                let loc_id = LitInt::new(loc_id.as_str(), Span::call_site());
400                let imp = quote! {
401                    impl #impl_generics ::srcpos_get::GetPos for #name #ty_generics #where_clause {
402                        fn pos(&self) -> ::srcpos_get::Pos {
403                            use ::srcpos_get::GetPos;
404                            self.#loc_id.pos()
405                        }
406                    }
407                };
408                return imp.into();
409            }
410            syn::Fields::Unit => {
411                return syn::Error::new(Span::call_site(), "There is nothing to get")
412                    .to_compile_error()
413                    .into();
414            }
415        },
416        syn::Data::Enum(v) => {
417            let mut vimps = vec![];
418            if v.variants.len() == 0 {
419                return syn::Error::new(Span::call_site(), "There is nothing to get")
420                    .to_compile_error()
421                    .into();
422            }
423            for variant in v.variants.iter() {
424                match &variant.fields {
425                    syn::Fields::Named(v) => {
426                        let mut has_loc = false;
427                        let mut loc_id: Option<&Ident> = None;
428                        for f in v.named.iter() {
429                            #[allow(unused_assignments)]
430                            if f.ident.as_ref().map(|id| id == "pos").unwrap_or(false) {
431                                has_loc = true;
432                                loc_id = f.ident.as_ref();
433                                break;
434                            }
435                            for attr in f.attrs.iter() {
436                                let path = &attr.path;
437                                if path.get_ident().map(|id| id == "pos").unwrap_or(false) {
438                                    if !has_loc && loc_id.is_some() {
439                                        return syn::Error::new(
440                                            variant.ident.span(),
441                                            "[GetPos] Cannot have multiple pos",
442                                        )
443                                        .to_compile_error()
444                                        .into();
445                                    }
446                                    loc_id = f.ident.as_ref();
447                                    if loc_id.is_none() {
448                                        return syn::Error::new(
449                                            variant.ident.span(),
450                                            "[GetPos] Field has no name",
451                                        )
452                                        .to_compile_error()
453                                        .into();
454                                    }
455                                }
456                            }
457                        }
458                        if loc_id.is_none() {
459                            return syn::Error::new(
460                                variant.ident.span(),
461                                "[GetPos] Not found field pos",
462                            )
463                            .to_compile_error()
464                            .into();
465                        }
466                        let loc_id = loc_id.unwrap();
467                        let vname = &variant.ident;
468                        let imp = quote_spanned! { variant.ident.span() => Self::#vname { #loc_id, .. } => #loc_id.pos() };
469                        vimps.push(imp);
470                    }
471                    syn::Fields::Unnamed(v) => {
472                        let mut loc_id: Option<usize> = None;
473                        for (i, f) in v.unnamed.iter().enumerate() {
474                            for attr in f.attrs.iter() {
475                                let path = &attr.path;
476                                if path.get_ident().map(|id| id == "pos").unwrap_or(false) {
477                                    if loc_id.is_some() {
478                                        return syn::Error::new(
479                                            variant.ident.span(),
480                                            "[GetPos] Cannot have multiple pos",
481                                        )
482                                        .to_compile_error()
483                                        .into();
484                                    }
485                                    loc_id = Some(i);
486                                }
487                            }
488                        }
489                        if v.unnamed.len() == 0 {
490                            return syn::Error::new(
491                                variant.ident.span(),
492                                "[GetPos] There is nothing to get",
493                            )
494                            .to_compile_error()
495                            .into();
496                        }
497                        if v.unnamed.len() == 1 && loc_id.is_none() {
498                            loc_id = Some(0);
499                        }
500                        if loc_id.is_none() {
501                            return syn::Error::new(
502                                variant.ident.span(),
503                                "[GetPos] Not found field pos",
504                            )
505                            .to_compile_error()
506                            .into();
507                        }
508                        let loc_id = loc_id.unwrap();
509                        let ids = (0..(variant.fields.len())).into_iter().map(|i| {
510                            if i != loc_id {
511                                format_ident!("_")
512                            } else {
513                                format_ident!("v{}", i)
514                            }
515                        });
516                        let loc_id = format_ident!("v{}", loc_id);
517                        let vname = &variant.ident;
518                        let imp = quote_spanned! { variant.ident.span() => Self::#vname(#(#ids),*) => #loc_id.pos() };
519                        vimps.push(imp);
520                    }
521                    syn::Fields::Unit => {
522                        return syn::Error::new(
523                            variant.ident.span(),
524                            "[GetPos] There is nothing to get",
525                        )
526                        .to_compile_error()
527                        .into();
528                    }
529                }
530            }
531            let imp = quote! {
532                impl #impl_generics ::srcpos_get::GetPos for #name #ty_generics #where_clause {
533                    fn pos(&self) -> ::srcpos_get::Pos {
534                        use ::srcpos_get::GetPos;
535                        match self {
536                            #(#vimps),*
537                        }
538                    }
539                }
540            };
541            return imp.into();
542        }
543        syn::Data::Union(_) => {
544            return syn::Error::new(Span::call_site(), "Does not support union")
545                .to_compile_error()
546                .into();
547        }
548    }
549}