structmeta_derive/
to_tokens.rs1use crate::{syn_utils::*, to_tokens_attribute::*};
2use proc_macro2::{Delimiter, Ident, Span, TokenStream};
3use quote::{format_ident, quote, quote_spanned};
4use std::unreachable;
5use syn::{
6 parse2, parse_quote, spanned::Spanned, Data, DataEnum, DataStruct, DeriveInput, Field, Fields,
7 Result,
8};
9
10pub fn derive_to_tokens(input: DeriveInput) -> Result<TokenStream> {
11 let mut dump = false;
12 for attr in &input.attrs {
13 if attr.path.is_ident("to_tokens") {
14 let attr: ToTokensAttribute = parse2(attr.tokens.clone())?;
15 dump = dump || attr.dump.is_some();
16 }
17 }
18
19 let ts = match &input.data {
20 Data::Struct(data) => code_from_struct(data)?,
21 Data::Enum(data) => code_from_enum(data)?,
22 Data::Union(_) => {
23 bail!(Span::call_site(), "Not supported for union.")
24 }
25 };
26 let ts = quote! {
27 fn to_tokens(&self, tokens: &mut ::proc_macro2::TokenStream) {
28 #ts
29 }
30 };
31 let ts = impl_trait_result(&input, &parse_quote!(::quote::ToTokens), &[], ts, dump)?;
32 Ok(ts)
33}
34fn code_from_struct(data: &DataStruct) -> Result<TokenStream> {
35 let p = to_pattern(quote!(Self), &data.fields);
36 let ts = code_from_fields(&data.fields)?;
37 let ts = quote! {
38 let #p = self;
39 #ts
40 };
41 Ok(ts)
42}
43fn code_from_enum(data: &DataEnum) -> Result<TokenStream> {
44 let mut arms = Vec::new();
45 for variant in &data.variants {
46 let ident = &variant.ident;
47 let p = to_pattern(quote!(Self::#ident), &variant.fields);
48 let code = code_from_fields(&variant.fields)?;
49 arms.push(quote! {
50 #p => {
51 #code
52 }
53 });
54 }
55 Ok(quote! {
56 match self {
57 #(#arms)*
58 }
59 })
60}
61fn to_pattern(self_path: TokenStream, fields: &Fields) -> TokenStream {
62 let mut vars = Vec::new();
63 match fields {
64 Fields::Unit => self_path,
65 Fields::Unnamed(_) => {
66 for (index, field) in fields.iter().enumerate() {
67 let var_ident = to_var_ident(Some(index), &field.ident);
68 vars.push(quote!(#var_ident));
69 }
70 quote!( #self_path( #(#vars,)*))
71 }
72 Fields::Named(_) => {
73 for field in fields.iter() {
74 let field_ident = &field.ident;
75 let var_ident = to_var_ident(None, field_ident);
76 vars.push(quote!(#field_ident : #var_ident));
77 }
78 quote!( #self_path { #(#vars,)* } )
79 }
80 }
81}
82struct Scope<'a> {
83 ts: TokenStream,
84 surround: Option<Surround<'a>>,
85}
86struct Surround<'a> {
87 ident: Ident,
88 field: &'a Field,
89 delimiter: Delimiter,
90}
91
92fn delimiter_from_open_char(value: char) -> Option<Delimiter> {
93 match value {
94 '[' => Some(Delimiter::Bracket),
95 '{' => Some(Delimiter::Brace),
96 '(' => Some(Delimiter::Parenthesis),
97 _ => None,
98 }
99}
100fn delimiter_from_close_char(value: char) -> Option<Delimiter> {
101 match value {
102 ']' => Some(Delimiter::Bracket),
103 '}' => Some(Delimiter::Brace),
104 ')' => Some(Delimiter::Parenthesis),
105 _ => None,
106 }
107}
108
109impl<'a> Scope<'a> {
110 fn new(surround: Option<Surround<'a>>) -> Self {
111 Self {
112 ts: TokenStream::new(),
113 surround,
114 }
115 }
116}
117fn close_char_of(delimiter: Delimiter) -> char {
118 match delimiter {
119 Delimiter::Bracket => ']',
120 Delimiter::Brace => '}',
121 Delimiter::Parenthesis => ')',
122 _ => unreachable!("unsupported delimiter"),
123 }
124}
125impl<'a> Surround<'a> {
126 fn token_type_ident(&self) -> Ident {
127 match self.delimiter {
128 Delimiter::Bracket => parse_quote!(Bracket),
129 Delimiter::Brace => parse_quote!(Brace),
130 Delimiter::Parenthesis => parse_quote!(Paren),
131 _ => unreachable!("unsupported delimiter"),
132 }
133 }
134}
135
136impl<'a> Scope<'a> {
137 fn into_code(self, delimiter: Option<Delimiter>, span: Span) -> Result<TokenStream> {
138 if let Some(s) = self.surround {
139 if let Some(delimiter) = delimiter {
140 if s.delimiter != delimiter {
141 bail!(
142 span,
143 "mismatched closing delimiter expected `{}`, found `{}`.",
144 close_char_of(s.delimiter),
145 close_char_of(delimiter),
146 )
147 }
148 }
149 let ident = &s.ident;
150 let ts = self.ts;
151 let ty = &s.field.ty;
152 let span = s.field.span();
153 let func = if is_macro_delimiter(ty) {
154 quote_spanned!(span=> ::structmeta::helpers::surround_macro_delimiter)
155 } else {
156 let ty = s.token_type_ident();
157 quote_spanned!(span=> ::syn::token::#ty::surround)
158 };
159 let code = quote_spanned!(span=> #func(#ident, tokens, |tokens| { #ts }););
160 return Ok(code);
161 }
162 Ok(quote!())
163 }
164}
165fn code_from_fields(fields: &Fields) -> Result<TokenStream> {
166 let mut scopes = vec![Scope::new(None)];
167 for (index, field) in fields.iter().enumerate() {
168 let ident = to_var_ident(Some(index), &field.ident);
169 let mut field_to_tokens = true;
170 for attr in &field.attrs {
171 if attr.path.is_ident("to_tokens") {
172 let attr: ToTokensAttribute = parse2(attr.tokens.clone())?;
173 for token in &attr.token {
174 for c in token.value().chars() {
175 if let Some(delimiter) = delimiter_from_open_char(c) {
176 scopes.push(Scope::new(Some(Surround {
177 ident: ident.clone(),
178 field,
179 delimiter,
180 })));
181 field_to_tokens = false;
182 } else if let Some(delimiter) = delimiter_from_close_char(c) {
183 let scope = scopes.pop().unwrap();
184 scopes
185 .last_mut()
186 .unwrap()
187 .ts
188 .extend(scope.into_code(Some(delimiter), token.span())?);
189 } else {
190 bail!(
191 token.span(),
192 "expected '(', ')', '[', ']', '{{' or '}}', found `{}`.",
193 c
194 );
195 }
196 }
197 }
198 }
199 }
200 if field_to_tokens {
201 let code = quote_spanned!(field.span()=> ::quote::ToTokens::to_tokens(#ident, tokens););
202 scopes.last_mut().unwrap().ts.extend(code);
203 }
204 }
205 while let Some(scope) = scopes.pop() {
206 if scopes.is_empty() {
207 return Ok(scope.ts);
208 }
209 scopes
210 .last_mut()
211 .unwrap()
212 .ts
213 .extend(scope.into_code(None, Span::call_site())?);
214 }
215 unreachable!()
216}
217fn to_var_ident(index: Option<usize>, ident: &Option<Ident>) -> Ident {
218 if let Some(ident) = ident {
219 format_ident!("_{}", ident)
220 } else {
221 format_ident!("_{}", index.unwrap())
222 }
223}