self_rust_tokenize_derive/
macro.rs1use 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}