1use crate::{Delimiter, Group, Ident, PFacetAttr, Punct, Spacing, TokenStream, TokenTree};
4use quote::{ToTokens, quote, quote_spanned};
5
6fn const_prefix_tokens(needs_const_dispatch: bool) -> TokenStream {
7 if needs_const_dispatch {
8 quote! { @const }
9 } else {
10 TokenStream::new()
11 }
12}
13
14fn emit_extension_attr_with_prefix(
15 ns_ident: &Ident,
16 key_ident: &impl ToTokens,
17 args: &TokenStream,
18 facet_crate: &TokenStream,
19 const_prefix: &TokenStream,
20) -> TokenStream {
21 if args.is_empty() {
22 quote! {
23 #facet_crate::__ext!(#const_prefix #ns_ident::#key_ident { })
24 }
25 } else {
26 quote! {
27 #facet_crate::__ext!(#const_prefix #ns_ident::#key_ident { | #args })
28 }
29 }
30}
31
32fn emit_extension_attr_for_field_with_prefix(
33 ns_ident: &Ident,
34 key_ident: &impl ToTokens,
35 args: &TokenStream,
36 field_name: &impl ToTokens,
37 field_type: &TokenStream,
38 facet_crate: &TokenStream,
39 const_prefix: &TokenStream,
40) -> TokenStream {
41 if args.is_empty() {
42 quote! {
43 #facet_crate::__ext!(#const_prefix #ns_ident::#key_ident { #field_name : #field_type })
44 }
45 } else {
46 quote! {
47 #facet_crate::__ext!(#const_prefix #ns_ident::#key_ident { #field_name : #field_type | #args })
48 }
49 }
50}
51
52fn emit_builtin_attr_with_prefix(
53 key: &impl ToTokens,
54 args: &TokenStream,
55 facet_crate: &TokenStream,
56 const_prefix: &TokenStream,
57) -> TokenStream {
58 if args.is_empty() {
59 quote! {
60 #facet_crate::__attr!(#const_prefix @ns { #facet_crate::builtin } #key { })
61 }
62 } else {
63 quote! {
64 #facet_crate::__attr!(#const_prefix @ns { #facet_crate::builtin } #key { | #args })
65 }
66 }
67}
68
69fn emit_builtin_attr_for_field_with_prefix(
70 key: &impl ToTokens,
71 args: &TokenStream,
72 field_name: &impl ToTokens,
73 field_type: &TokenStream,
74 facet_crate: &TokenStream,
75 const_prefix: &TokenStream,
76) -> TokenStream {
77 if args.is_empty() {
78 quote! {
79 #facet_crate::__attr!(#const_prefix @ns { #facet_crate::builtin } #key { #field_name : #field_type })
80 }
81 } else {
82 quote! {
83 #facet_crate::__attr!(#const_prefix @ns { #facet_crate::builtin } #key { #field_name : #field_type | #args })
84 }
85 }
86}
87
88pub fn emit_extension_attr_for_field(
103 ns_ident: &Ident,
104 key_ident: &impl ToTokens,
105 args: &TokenStream,
106 field_name: &impl ToTokens,
107 field_type: &TokenStream,
108 facet_crate: &TokenStream,
109 needs_const_dispatch: bool,
110) -> TokenStream {
111 let const_prefix = const_prefix_tokens(needs_const_dispatch);
112 emit_extension_attr_for_field_with_prefix(
113 ns_ident,
114 key_ident,
115 args,
116 field_name,
117 field_type,
118 facet_crate,
119 &const_prefix,
120 )
121}
122
123pub fn emit_extension_attr(
137 ns_ident: &Ident,
138 key_ident: &impl ToTokens,
139 args: &TokenStream,
140 facet_crate: &TokenStream,
141 needs_const_dispatch: bool,
142) -> TokenStream {
143 let const_prefix = const_prefix_tokens(needs_const_dispatch);
144 emit_extension_attr_with_prefix(ns_ident, key_ident, args, facet_crate, &const_prefix)
145}
146
147pub fn emit_attr(
152 attr: &PFacetAttr,
153 facet_crate: &TokenStream,
154 needs_const_dispatch: bool,
155) -> TokenStream {
156 let key = &attr.key;
157 let args = &attr.args;
158
159 match &attr.ns {
160 Some(ns) => {
161 emit_extension_attr(ns, key, args, facet_crate, needs_const_dispatch)
163 }
164 None => {
165 let const_prefix = const_prefix_tokens(needs_const_dispatch);
167 emit_builtin_attr_with_prefix(key, args, facet_crate, &const_prefix)
168 }
169 }
170}
171
172pub fn emit_attr_for_field(
177 attr: &PFacetAttr,
178 field_name: &impl ToTokens,
179 field_type: &TokenStream,
180 facet_crate: &TokenStream,
181 needs_const_dispatch: bool,
182) -> TokenStream {
183 let key = &attr.key;
184 let args = &attr.args;
185
186 match &attr.ns {
187 Some(ns) => {
188 emit_extension_attr_for_field(
190 ns,
191 key,
192 args,
193 field_name,
194 field_type,
195 facet_crate,
196 needs_const_dispatch,
197 )
198 }
199 None => {
200 let const_prefix = const_prefix_tokens(needs_const_dispatch);
202 emit_builtin_attr_for_field_with_prefix(
203 key,
204 args,
205 field_name,
206 field_type,
207 facet_crate,
208 &const_prefix,
209 )
210 }
211 }
212}
213
214pub fn ext_attr(input: TokenStream) -> TokenStream {
223 let mut tokens = input.into_iter().peekable();
224
225 let mut use_const_dispatch = false;
227 if let Some(TokenTree::Punct(p)) = tokens.peek()
228 && p.as_char() == '@'
229 {
230 tokens.next(); match tokens.next() {
232 Some(TokenTree::Ident(mode)) if mode == "const" => {
233 use_const_dispatch = true;
234 }
235 _ => {
236 return quote! {
237 ::core::compile_error!("__ext!: expected `const` after `@`")
238 };
239 }
240 }
241 }
242
243 let ns_ident = match tokens.next() {
245 Some(TokenTree::Ident(ident)) => ident,
246 _ => {
247 return quote! {
248 ::core::compile_error!("__ext!: expected namespace identifier")
249 };
250 }
251 };
252
253 match (tokens.next(), tokens.next()) {
255 (Some(TokenTree::Punct(p1)), Some(TokenTree::Punct(p2)))
256 if p1.as_char() == ':' && p2.as_char() == ':' => {}
257 _ => {
258 return quote! {
259 ::core::compile_error!("__ext!: expected '::'")
260 };
261 }
262 }
263
264 let attr_ident = match tokens.next() {
266 Some(TokenTree::Ident(ident)) => ident,
267 _ => {
268 return quote! {
269 ::core::compile_error!("__ext!: expected attribute name")
270 };
271 }
272 };
273
274 let body = match tokens.next() {
276 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => g,
277 _ => {
278 return quote! {
279 ::core::compile_error!("__ext!: expected braced body")
280 };
281 }
282 };
283
284 let __attr = Ident::new("__attr", attr_ident.span());
288 let at = Punct::new('@', Spacing::Alone);
289 let ns_keyword = Ident::new("ns", attr_ident.span());
290
291 let colon1 = Punct::new(':', Spacing::Joint);
292 let colon2 = Punct::new(':', Spacing::Alone);
293 let bang = Punct::new('!', Spacing::Alone);
294
295 let mut output = TokenStream::new();
297 output.extend([TokenTree::Ident(ns_ident.clone())]);
298 output.extend([TokenTree::Punct(colon1.clone())]);
299 output.extend([TokenTree::Punct(colon2.clone())]);
300 output.extend([TokenTree::Ident(__attr)]);
301 output.extend([TokenTree::Punct(bang)]);
302
303 let mut macro_args = TokenStream::new();
305 if use_const_dispatch {
306 macro_args.extend([TokenTree::Punct(Punct::new('@', Spacing::Alone))]);
307 macro_args.extend([TokenTree::Ident(Ident::new("const", attr_ident.span()))]);
308 }
309 macro_args.extend([TokenTree::Punct(at)]);
311 macro_args.extend([TokenTree::Ident(ns_keyword)]);
312 let mut ns_group_content = TokenStream::new();
313 ns_group_content.extend([TokenTree::Ident(ns_ident)]);
314 macro_args.extend([TokenTree::Group(Group::new(
315 Delimiter::Brace,
316 ns_group_content,
317 ))]);
318 macro_args.extend([TokenTree::Ident(attr_ident)]);
320 macro_args.extend([TokenTree::Group(body)]);
321
322 let args_group = Group::new(Delimiter::Parenthesis, macro_args);
323 output.extend([TokenTree::Group(args_group)]);
324
325 output
326}
327
328pub fn unknown_attr(input: TokenStream) -> TokenStream {
335 let mut tokens = input.into_iter();
336
337 let ident = match tokens.next() {
339 Some(TokenTree::Ident(ident)) => ident,
340 _ => {
341 return quote! {
342 ::core::compile_error!("__unknown_attr!: expected identifier")
343 };
344 }
345 };
346
347 let span = ident.span();
348 let message = format!("unknown extension attribute `{ident}`");
349
350 quote_spanned! { span =>
351 ::core::compile_error!(#message)
352 }
353}
354
355pub fn no_args(input: TokenStream) -> TokenStream {
362 let mut tokens = input.into_iter();
363
364 let msg = match tokens.next() {
366 Some(TokenTree::Literal(lit)) => {
367 let s = lit.to_string();
368 s.trim_matches('"').to_string()
369 }
370 _ => {
371 return quote! {
372 ::core::compile_error!("__no_args!: expected string literal")
373 };
374 }
375 };
376
377 tokens.next();
379
380 let span = match tokens.next() {
382 Some(tt) => tt.span(),
383 None => {
384 return quote! {
385 ::core::compile_error!("__no_args!: expected token for span")
386 };
387 }
388 };
389
390 let message = format!("{msg} does not accept arguments");
391
392 quote_spanned! { span =>
393 ::core::compile_error!(#message)
394 }
395}