self_rust_tokenize_derive/
macro.rs

1use proc_macro::TokenStream;
2use syn_helpers::{
3    derive_trait,
4    proc_macro2::{Ident, Literal, Span},
5    quote,
6    syn::{parse_macro_input, parse_quote, DeriveInput, Member},
7    Constructable, FieldMut, Fields, Item, Trait, TraitItem, TypeOfSelf,
8};
9
10const PANIC_ON_SELF_TOKENIZE: &str = "panic_on_self_tokenize";
11const SINGLE_FIELD: &str = "self_tokenize_field";
12
13#[proc_macro_derive(
14    SelfRustTokenize,
15    attributes(panic_on_self_tokenize, self_tokenize_field)
16)]
17pub fn self_rust_tokenize(input: TokenStream) -> TokenStream {
18    let input = parse_macro_input!(input as DeriveInput);
19
20    let append_to_token_stream = TraitItem::new_method(
21        Ident::new("append_to_token_stream", Span::call_site()),
22        None,
23        TypeOfSelf::Reference,
24        vec![parse_quote!(
25            token_stream: &mut ::self_rust_tokenize::helpers::TokenStream
26        )],
27        None,
28        |mut item: Item| {
29            item.map_constructable(|mut constructable| {
30                let attributes = &constructable
31                    .get_fields()
32                    .get_field_attributes();
33
34                if attributes
35                    .iter()
36                    .any(|attr| attr.path().is_ident(PANIC_ON_SELF_TOKENIZE) )
37                {
38                    return Ok(vec![parse_quote!(panic!("Item not self-tokenize-able");)]);
39                }
40
41                if let Some(attribute) = attributes
42                    .iter()
43                    .find(|attr| attr.path().is_ident(SINGLE_FIELD))
44                {
45                    let member = attribute.parse_args::<Member>()?;
46
47                    let mut field = constructable.get_fields_mut().get_field_by_member_mut(member).ok_or("could not find the field")?;
48
49                    let reference = field.get_reference();
50
51                    let call = parse_quote!(
52                        ::self_rust_tokenize::SelfRustTokenize::append_to_token_stream(#reference, token_stream);
53                    );
54
55                    return Ok(vec![call]);
56                }
57
58                let segments =
59                    constructable.get_constructor_path().segments.into_iter().map(|seg| Literal::string(&seg.ident.to_string()));
60
61                let call = match constructable.get_fields_mut() {
62                    Fields::Named(named, _) => {
63                        let values = named.iter_mut().map(|named_field| {
64                            let reference = named_field.get_reference();
65                            let name = Literal::string(&named_field.name.to_string());
66                            quote!((#name, ::self_rust_tokenize::SelfRustTokenize::to_tokens(#reference)))
67                        });
68                        parse_quote! {
69                            ::self_rust_tokenize::_private::add_named_constructor_body(
70                                token_stream,
71                                &[#(#segments),*],
72                                vec![#(#values),*],
73                            );
74                        }
75                    }
76                    Fields::Unnamed(unnamed, _) => {
77                        let values = unnamed.iter_mut().map(|unnamed_field| {
78                            let reference = unnamed_field.get_reference();
79                            quote!(::self_rust_tokenize::SelfRustTokenize::to_tokens(#reference))
80                        });
81                        parse_quote! {
82                            ::self_rust_tokenize::_private::add_unnamed_constructor_body(
83                                token_stream,
84                                &[#(#segments),*],
85                                vec![#(#values),*],
86                            );
87                        }
88                    }
89                    Fields::Unit(_) => {
90                        parse_quote! {
91                            ::self_rust_tokenize::_private::add_unit_constructor_body(
92                                token_stream,
93                                &[#(#segments),*],
94                            );
95                        }
96                    }
97                };
98                Ok(vec![call])
99            })
100        },
101    );
102
103    let self_rust_tokenize = Trait {
104        name: parse_quote!(::self_rust_tokenize::SelfRustTokenize),
105        generic_parameters: None,
106        items: vec![append_to_token_stream],
107    };
108
109    let output = derive_trait(input, self_rust_tokenize);
110
111    output.into()
112}