facet_macros_impl/
extension.rs1use crate::{Delimiter, Group, Ident, PFacetAttr, Punct, Spacing, TokenStream, TokenTree};
4use quote::{ToTokens, quote, quote_spanned};
5
6pub fn emit_extension_attr_for_field(
21 ns_ident: &Ident,
22 key_ident: &Ident,
23 args: &TokenStream,
24 field_name: &impl ToTokens,
25 field_type: &TokenStream,
26 facet_crate: &TokenStream,
27) -> TokenStream {
28 if args.is_empty() {
29 quote! {
31 #facet_crate::__ext!(#ns_ident::#key_ident { #field_name : #field_type })
32 }
33 } else {
34 quote! {
36 #facet_crate::__ext!(#ns_ident::#key_ident { #field_name : #field_type | #args })
37 }
38 }
39}
40
41pub fn emit_extension_attr(
55 ns_ident: &Ident,
56 key_ident: &Ident,
57 args: &TokenStream,
58 facet_crate: &TokenStream,
59) -> TokenStream {
60 if args.is_empty() {
61 quote! {
63 #facet_crate::__ext!(#ns_ident::#key_ident { })
64 }
65 } else {
66 quote! {
68 #facet_crate::__ext!(#ns_ident::#key_ident { | #args })
69 }
70 }
71}
72
73pub fn emit_attr(attr: &PFacetAttr, facet_crate: &TokenStream) -> TokenStream {
78 let key = &attr.key;
79 let args = &attr.args;
80
81 match &attr.ns {
82 Some(ns) => {
83 emit_extension_attr(ns, key, args, facet_crate)
85 }
86 None => {
87 if args.is_empty() {
89 quote! {
90 #facet_crate::__attr!(@ns { #facet_crate::builtin } #key { })
91 }
92 } else {
93 quote! {
94 #facet_crate::__attr!(@ns { #facet_crate::builtin } #key { | #args })
95 }
96 }
97 }
98 }
99}
100
101pub fn emit_attr_for_field(
106 attr: &PFacetAttr,
107 field_name: &impl ToTokens,
108 field_type: &TokenStream,
109 facet_crate: &TokenStream,
110) -> TokenStream {
111 let key = &attr.key;
112 let args = &attr.args;
113
114 match &attr.ns {
115 Some(ns) => {
116 emit_extension_attr_for_field(ns, key, args, field_name, field_type, facet_crate)
118 }
119 None => {
120 if args.is_empty() {
122 quote! {
123 #facet_crate::__attr!(@ns { #facet_crate::builtin } #key { #field_name : #field_type })
124 }
125 } else {
126 quote! {
127 #facet_crate::__attr!(@ns { #facet_crate::builtin } #key { #field_name : #field_type | #args })
128 }
129 }
130 }
131 }
132}
133
134pub fn ext_attr(input: TokenStream) -> TokenStream {
143 let mut tokens = input.into_iter().peekable();
144
145 let ns_ident = match tokens.next() {
147 Some(TokenTree::Ident(ident)) => ident,
148 _ => {
149 return quote! {
150 ::core::compile_error!("__ext!: expected namespace identifier")
151 };
152 }
153 };
154
155 match (tokens.next(), tokens.next()) {
157 (Some(TokenTree::Punct(p1)), Some(TokenTree::Punct(p2)))
158 if p1.as_char() == ':' && p2.as_char() == ':' => {}
159 _ => {
160 return quote! {
161 ::core::compile_error!("__ext!: expected '::'")
162 };
163 }
164 }
165
166 let attr_ident = match tokens.next() {
168 Some(TokenTree::Ident(ident)) => ident,
169 _ => {
170 return quote! {
171 ::core::compile_error!("__ext!: expected attribute name")
172 };
173 }
174 };
175
176 let body = match tokens.next() {
178 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => g,
179 _ => {
180 return quote! {
181 ::core::compile_error!("__ext!: expected braced body")
182 };
183 }
184 };
185
186 let __attr = Ident::new("__attr", attr_ident.span());
190 let at = Punct::new('@', Spacing::Alone);
191 let ns_keyword = Ident::new("ns", attr_ident.span());
192
193 let colon1 = Punct::new(':', Spacing::Joint);
194 let colon2 = Punct::new(':', Spacing::Alone);
195 let bang = Punct::new('!', Spacing::Alone);
196
197 let mut output = TokenStream::new();
199 output.extend([TokenTree::Ident(ns_ident.clone())]);
200 output.extend([TokenTree::Punct(colon1.clone())]);
201 output.extend([TokenTree::Punct(colon2.clone())]);
202 output.extend([TokenTree::Ident(__attr)]);
203 output.extend([TokenTree::Punct(bang)]);
204
205 let mut macro_args = TokenStream::new();
207 macro_args.extend([TokenTree::Punct(at)]);
209 macro_args.extend([TokenTree::Ident(ns_keyword)]);
210 let mut ns_group_content = TokenStream::new();
211 ns_group_content.extend([TokenTree::Ident(ns_ident)]);
212 macro_args.extend([TokenTree::Group(Group::new(
213 Delimiter::Brace,
214 ns_group_content,
215 ))]);
216 macro_args.extend([TokenTree::Ident(attr_ident)]);
218 macro_args.extend([TokenTree::Group(body)]);
219
220 let args_group = Group::new(Delimiter::Parenthesis, macro_args);
221 output.extend([TokenTree::Group(args_group)]);
222
223 output
224}
225
226pub fn unknown_attr(input: TokenStream) -> TokenStream {
233 let mut tokens = input.into_iter();
234
235 let ident = match tokens.next() {
237 Some(TokenTree::Ident(ident)) => ident,
238 _ => {
239 return quote! {
240 ::core::compile_error!("__unknown_attr!: expected identifier")
241 };
242 }
243 };
244
245 let span = ident.span();
246 let message = format!("unknown extension attribute `{ident}`");
247
248 quote_spanned! { span =>
249 ::core::compile_error!(#message)
250 }
251}
252
253pub fn no_args(input: TokenStream) -> TokenStream {
260 let mut tokens = input.into_iter();
261
262 let msg = match tokens.next() {
264 Some(TokenTree::Literal(lit)) => {
265 let s = lit.to_string();
266 s.trim_matches('"').to_string()
267 }
268 _ => {
269 return quote! {
270 ::core::compile_error!("__no_args!: expected string literal")
271 };
272 }
273 };
274
275 tokens.next();
277
278 let span = match tokens.next() {
280 Some(tt) => tt.span(),
281 None => {
282 return quote! {
283 ::core::compile_error!("__no_args!: expected token for span")
284 };
285 }
286 };
287
288 let message = format!("{msg} does not accept arguments");
289
290 quote_spanned! { span =>
291 ::core::compile_error!(#message)
292 }
293}