userprompt_derive/
lib.rs

1use proc_macro::TokenStream;
2use syn::{DeriveInput, Ident};
3
4#[cfg(feature = "egui")]
5fn build_enum_variant_builder(v: &syn::Variant) -> proc_macro2::TokenStream {
6    let sident = proc_macro2::Ident::new("Self", proc_macro2::Span::call_site());
7    match &v.fields {
8        syn::Fields::Named(f) => {
9            let mut def: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
10            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
11            for f in f.named.iter() {
12                tokens.extend([proc_macro2::TokenTree::Ident(
13                    f.ident.as_ref().unwrap().clone(),
14                )]);
15                let ftype = &f.ty;
16                let val = quote::quote!(<#ftype as core::default::Default>::default());
17                tokens.extend([proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
18                    ':',
19                    proc_macro2::Spacing::Alone,
20                ))]);
21                tokens.extend(val);
22                tokens.extend([proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
23                    ',',
24                    proc_macro2::Spacing::Alone,
25                ))]);
26            }
27            def.extend(tokens);
28
29            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
30            tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
31            tokens.extend([
32                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
33                    ':',
34                    proc_macro2::Spacing::Joint,
35                )),
36                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
37                    ':',
38                    proc_macro2::Spacing::Alone,
39                )),
40            ]);
41            tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
42            tokens.extend([proc_macro2::TokenTree::Group(proc_macro2::Group::new(
43                proc_macro2::Delimiter::Brace,
44                def,
45            ))]);
46            tokens
47        }
48        syn::Fields::Unnamed(f) => {
49            let mut def: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
50            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
51            for (i, f) in f.unnamed.iter().enumerate() {
52                let ty = &f.ty;
53                tokens.extend(quote::quote!(<#ty as core::default::Default>::default()));
54                tokens.extend([proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
55                    ',',
56                    proc_macro2::Spacing::Alone,
57                ))]);
58            }
59            def.extend(tokens);
60
61            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
62            tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
63            tokens.extend([
64                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
65                    ':',
66                    proc_macro2::Spacing::Joint,
67                )),
68                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
69                    ':',
70                    proc_macro2::Spacing::Alone,
71                )),
72            ]);
73            tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
74            tokens.extend([proc_macro2::TokenTree::Group(proc_macro2::Group::new(
75                proc_macro2::Delimiter::Parenthesis,
76                def,
77            ))]);
78            tokens
79        }
80        syn::Fields::Unit => {
81            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
82            tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
83            tokens.extend([
84                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
85                    ':',
86                    proc_macro2::Spacing::Joint,
87                )),
88                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
89                    ':',
90                    proc_macro2::Spacing::Alone,
91                )),
92            ]);
93            tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
94            tokens
95        }
96    }
97}
98
99/// Builds a match pair for a given enum variant to help convert an enum to a string
100#[cfg(feature = "egui")]
101fn build_enum_variant_to_fields(v: &syn::Variant) -> (proc_macro2::TokenStream, Vec<&syn::Field>) {
102    let sident = proc_macro2::Ident::new("Self", proc_macro2::Span::call_site());
103    let mut fields = Vec::new();
104    let q: proc_macro2::TokenStream = match &v.fields {
105        syn::Fields::Named(f) => {
106            let mut def: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
107            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
108            for f in f.named.iter() {
109                tokens.extend([proc_macro2::TokenTree::Ident(
110                    f.ident.as_ref().unwrap().clone(),
111                )]);
112                tokens.extend([proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
113                    ',',
114                    proc_macro2::Spacing::Alone,
115                ))]);
116                fields.push(f);
117            }
118            def.extend(tokens);
119
120            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
121            tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
122            tokens.extend([
123                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
124                    ':',
125                    proc_macro2::Spacing::Joint,
126                )),
127                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
128                    ':',
129                    proc_macro2::Spacing::Alone,
130                )),
131            ]);
132            tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
133            tokens.extend([proc_macro2::TokenTree::Group(proc_macro2::Group::new(
134                proc_macro2::Delimiter::Brace,
135                def,
136            ))]);
137            tokens
138        }
139        syn::Fields::Unnamed(f) => {
140            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
141            tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
142            tokens.extend([
143                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
144                    ':',
145                    proc_macro2::Spacing::Joint,
146                )),
147                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
148                    ':',
149                    proc_macro2::Spacing::Alone,
150                )),
151            ]);
152            tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
153            let mut tokens2: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
154            for (i, f) in f.unnamed.iter().enumerate() {
155                let varname = quote::format_ident!("a_{}", i);
156                tokens2.extend([proc_macro2::TokenTree::Ident(varname)]);
157                tokens2.extend([proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
158                    ',',
159                    proc_macro2::Spacing::Alone,
160                ))]);
161                fields.push(f);
162            }
163            quote::quote! {#tokens (#tokens2)}
164        }
165        syn::Fields::Unit => {
166            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
167            tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
168            tokens.extend([
169                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
170                    ':',
171                    proc_macro2::Spacing::Joint,
172                )),
173                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
174                    ':',
175                    proc_macro2::Spacing::Alone,
176                )),
177            ]);
178            tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
179            tokens
180        }
181    };
182    (q, fields)
183}
184
185/// Builds a match pair for a given enum variant to help convert an enum to a string
186#[cfg(feature = "egui")]
187fn build_enum_variant_to_string(v: &syn::Variant) -> (proc_macro2::TokenStream, String) {
188    let sident = proc_macro2::Ident::new("Self", proc_macro2::Span::call_site());
189    let text2 = v.ident.to_string();
190    let q: proc_macro2::TokenStream = match &v.fields {
191        syn::Fields::Named(f) => {
192            let mut def: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
193            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
194            for f in f.named.iter() {
195                tokens.extend([proc_macro2::TokenTree::Ident(
196                    f.ident.as_ref().unwrap().clone(),
197                )]);
198                tokens.extend([proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
199                    ',',
200                    proc_macro2::Spacing::Alone,
201                ))]);
202            }
203            def.extend(tokens);
204
205            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
206            tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
207            tokens.extend([
208                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
209                    ':',
210                    proc_macro2::Spacing::Joint,
211                )),
212                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
213                    ':',
214                    proc_macro2::Spacing::Alone,
215                )),
216            ]);
217            tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
218            tokens.extend([proc_macro2::TokenTree::Group(proc_macro2::Group::new(
219                proc_macro2::Delimiter::Brace,
220                def,
221            ))]);
222            tokens
223        }
224        syn::Fields::Unnamed(f) => {
225            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
226            tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
227            tokens.extend([
228                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
229                    ':',
230                    proc_macro2::Spacing::Joint,
231                )),
232                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
233                    ':',
234                    proc_macro2::Spacing::Alone,
235                )),
236            ]);
237            tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
238            let mut tokens2: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
239            for (i, _f) in f.unnamed.iter().enumerate() {
240                let varname = quote::format_ident!("a_{}", i);
241                tokens2.extend([proc_macro2::TokenTree::Ident(varname)]);
242                tokens2.extend([proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
243                    ',',
244                    proc_macro2::Spacing::Alone,
245                ))]);
246            }
247            quote::quote! {#tokens (#tokens2)}
248        }
249        syn::Fields::Unit => {
250            let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
251            tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
252            tokens.extend([
253                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
254                    ':',
255                    proc_macro2::Spacing::Joint,
256                )),
257                proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
258                    ':',
259                    proc_macro2::Spacing::Alone,
260                )),
261            ]);
262            tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
263            tokens
264        }
265    };
266    (q, text2)
267}
268
269#[cfg(feature = "egui")]
270#[proc_macro_derive(EguiPrompting)]
271pub fn derive_egui_prompting(input: TokenStream) -> TokenStream {
272    use std::any::Any;
273
274    let input = syn::parse_macro_input!(input as DeriveInput);
275    let sident = input.ident;
276    let expanded: TokenStream;
277
278    match &input.data {
279        syn::Data::Enum(e) => {
280            let mut field_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
281            let q: proc_macro2::TokenStream = quote::quote! {
282                let combobox = if let Some(name) = name {
283                    let mut s = "Select a ".to_string();
284                    s.push_str(&name);
285                    egui::ComboBox::from_label(s)
286                } else {
287                    egui::ComboBox::from_label("Select")
288                };
289            };
290            field_stuff.extend(q);
291
292            let mut match_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
293            for v in &e.variants {
294                let (q, t) = build_enum_variant_to_string(v);
295                match_stuff.extend(quote::quote! {
296                    #q => #t,
297                });
298            }
299
300            let q_start: proc_macro2::TokenStream = quote::quote! {
301                let val = match self {
302                    #match_stuff
303                };
304            };
305            field_stuff.extend(q_start);
306
307            let mut combo_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
308            for v in &e.variants {
309                let text = v.ident.to_string();
310                let assign = build_enum_variant_builder(v);
311                let t = v.type_id();
312                let q: proc_macro2::TokenStream = quote::quote! {
313                    if ui.selectable_label(false, #text).clicked() {
314                        *self = #assign;
315                    }
316                };
317                combo_stuff.extend(q);
318            }
319
320            let mut option_prompt: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
321            for v in &e.variants {
322                let (q, f) = build_enum_variant_to_fields(v);
323                let mut option_code: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
324                if !f.is_empty() {
325                    for (i, f) in f.iter().enumerate() {
326                        if let Some(ident) = &f.ident {
327                            let varname = quote::format_ident!("{}", ident);
328                            let text = ident.to_string();
329                            let q: proc_macro2::TokenStream = quote::quote! {
330                                let subname = format!("{}/{}", name.unwrap_or(""), #text);
331                                #varname.build_gui(ui, Some(&subname))?;
332                            };
333                            option_code.extend(q);
334                        } else {
335                            let varname = quote::format_ident!("a_{}", i);
336                            let text = format!("{}", i);
337                            let q: proc_macro2::TokenStream = quote::quote! {
338                                let subname = format!("{}/{}", name.unwrap_or(""), #text);
339                                #varname.build_gui(ui, Some(&subname))?;
340                            };
341                            option_code.extend(q);
342                        }
343                    }
344                    option_prompt.extend(quote::quote! {
345                        #q => { #option_code },
346                    });
347                }
348            }
349
350            expanded = quote::quote! {
351                impl userprompt::EguiPrompting for #sident {
352                    fn build_gui(&mut self, ui: &mut egui::Ui, name: Option<&str>) -> Result<(), String> {
353                        #field_stuff
354                        combobox.selected_text(val)
355                            .show_ui(ui, |ui| { #combo_stuff });
356                        match self {
357                            #option_prompt
358                            _ => {}
359                        }
360                        Ok(())
361                    }
362                }
363            }
364            .into();
365        }
366        syn::Data::Struct(s) => {
367            let fields = &s.fields;
368            let mut field_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
369
370            let q: proc_macro2::TokenStream = quote::quote! {
371                if let Some(name) = name {
372                    ui.label(name);
373                }
374            };
375            field_stuff.extend(q);
376
377            if let syn::Fields::Named(n) = fields {
378                for n in n.named.iter() {
379                    if let Some(ident) = &n.ident {
380                        let text = ident.to_string();
381                        let varname = quote::format_ident!("{}", ident);
382                        let q: proc_macro2::TokenStream = quote::quote! {
383                            let subname = format!("{}/{}", name.unwrap_or(""), #text);
384                            self.#varname.build_gui(ui, Some(&subname))?;
385                        };
386                        field_stuff.extend(q);
387                    }
388                }
389
390                let mut q2s: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
391
392                for (i, n) in n.named.iter().enumerate() {
393                    if let Some(ident) = &n.ident {
394                        let name = Ident::new(&format!("a_{}", i), proc_macro2::Span::call_site());
395                        let q2: proc_macro2::TokenStream = quote::quote! {
396                            #ident: #name,
397                        };
398                        q2s.extend(q2);
399                    }
400                }
401
402                let q: proc_macro2::TokenStream = quote::quote! {
403                    Ok(())
404                };
405                field_stuff.extend(q);
406            }
407            expanded = quote::quote! {
408                impl userprompt::EguiPrompting for #sident {
409                    fn build_gui(&mut self, ui: &mut egui::Ui, name: Option<&str>) -> Result<(), String> {
410                        #field_stuff
411                    }
412                }
413            }
414            .into();
415        }
416        _ => panic!("Unhandled object type"),
417    };
418    TokenStream::from(expanded)
419}
420
421#[proc_macro_derive(Prompting)]
422pub fn derive_prompting(input: TokenStream) -> TokenStream {
423    let input = syn::parse_macro_input!(input as DeriveInput);
424    let sident = input.ident;
425    let expanded: TokenStream;
426    match &input.data {
427        syn::Data::Enum(e) => {
428            let mut field_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
429            let q: proc_macro2::TokenStream = quote::quote! {
430                if let Some(name) = name {
431                    println!("[{}]", name);
432                }
433            };
434            field_stuff.extend(q);
435
436            let q: proc_macro2::TokenStream = quote::quote! {
437                println!("Enter the variant type, valid options are listed below");
438            };
439            field_stuff.extend(q);
440
441            for v in &e.variants {
442                let text = v.ident.to_string();
443                let q: proc_macro2::TokenStream = quote::quote! {
444                    println!("\t{}", #text);
445                };
446                field_stuff.extend(q);
447            }
448
449            let name = Ident::new(&format!("a"), proc_macro2::Span::call_site());
450            let q: proc_macro2::TokenStream = quote::quote! {
451                let #name = <String as userprompt::Prompting>::prompt(None)?;
452            };
453            field_stuff.extend(q);
454
455            let mut match_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
456            for v in &e.variants {
457                let text2 = v.ident.to_string();
458                let q: proc_macro2::TokenStream = match &v.fields {
459                    syn::Fields::Named(f) => {
460                        let mut def: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
461                        let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
462                        for (i, f) in f.named.iter().enumerate() {
463                            if i != 0 {
464                                tokens.extend([proc_macro2::TokenTree::Punct(
465                                    proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
466                                )]);
467                            }
468                            tokens.extend([proc_macro2::TokenTree::Ident(
469                                f.ident.as_ref().unwrap().clone(),
470                            )]);
471                            let ftype = &f.ty;
472                            let text = f.ident.as_ref().unwrap().to_string();
473                            let val = quote::quote!(<#ftype as userprompt::Prompting>::prompt(Some(#text))?);
474                            tokens.extend([proc_macro2::TokenTree::Punct(
475                                proc_macro2::Punct::new(':', proc_macro2::Spacing::Alone),
476                            )]);
477                            tokens.extend(val);
478                        }
479                        def.extend(tokens);
480
481                        let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
482                        tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
483                        tokens.extend([
484                            proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
485                                ':',
486                                proc_macro2::Spacing::Joint,
487                            )),
488                            proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
489                                ':',
490                                proc_macro2::Spacing::Alone,
491                            )),
492                        ]);
493                        tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
494                        tokens.extend([proc_macro2::TokenTree::Group(proc_macro2::Group::new(
495                            proc_macro2::Delimiter::Brace,
496                            def,
497                        ))]);
498                        quote::quote! {
499                            #text2 => { return Ok(#tokens); }
500                        }
501                    }
502                    syn::Fields::Unnamed(f) => {
503                        let mut def: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
504                        let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
505                        for (i, f) in f.unnamed.iter().enumerate() {
506                            if i != 0 {
507                                tokens.extend([proc_macro2::TokenTree::Punct(
508                                    proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
509                                )]);
510                            }
511                            tokens.extend([proc_macro2::TokenTree::Literal(
512                                proc_macro2::Literal::usize_unsuffixed(i),
513                            )]);
514                            let ftype = &f.ty;
515                            let val = quote::quote!(<#ftype as userprompt::Prompting>::prompt(Some(&format!("{}", #i)))?);
516                            tokens.extend([proc_macro2::TokenTree::Punct(
517                                proc_macro2::Punct::new(':', proc_macro2::Spacing::Alone),
518                            )]);
519                            tokens.extend(val);
520                        }
521                        def.extend(tokens);
522
523                        let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
524                        tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
525                        tokens.extend([
526                            proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
527                                ':',
528                                proc_macro2::Spacing::Joint,
529                            )),
530                            proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
531                                ':',
532                                proc_macro2::Spacing::Alone,
533                            )),
534                        ]);
535                        tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
536                        tokens.extend([proc_macro2::TokenTree::Group(proc_macro2::Group::new(
537                            proc_macro2::Delimiter::Brace,
538                            def,
539                        ))]);
540                        quote::quote! {
541                            #text2 => { return Ok(#tokens); }
542                        }
543                    }
544                    syn::Fields::Unit => {
545                        let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
546                        tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
547                        tokens.extend([
548                            proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
549                                ':',
550                                proc_macro2::Spacing::Joint,
551                            )),
552                            proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
553                                ':',
554                                proc_macro2::Spacing::Alone,
555                            )),
556                        ]);
557                        tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
558                        quote::quote! {
559                            #text2 => { return Ok(#tokens); }
560                        }
561                    }
562                };
563                match_stuff.extend(q);
564            }
565
566            let match_else: proc_macro2::TokenStream =
567                quote::quote!(_ => println!("Invalid option"),);
568
569            let q_start: proc_macro2::TokenStream = quote::quote! {
570                match a.as_str() {
571                    #match_stuff
572                    #match_else
573                }
574            };
575            field_stuff.extend(q_start);
576
577            expanded = quote::quote! {
578                impl userprompt::Prompting for #sident {
579                    fn prompt(name: Option<&str>) -> Result<Self, userprompt::Error> {
580                        loop {
581                            #field_stuff
582                        }
583                    }
584                }
585            }
586            .into();
587        }
588        syn::Data::Struct(s) => {
589            let fields = &s.fields;
590            let mut field_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
591
592            let q: proc_macro2::TokenStream = quote::quote! {
593                if let Some(name) = name {
594                    println!("[{}]", name);
595                }
596            };
597            field_stuff.extend(q);
598
599            if let syn::Fields::Named(n) = fields {
600                for (i, n) in n.named.iter().enumerate() {
601                    let ftype = &n.ty;
602                    if let Some(ident) = &n.ident {
603                        let name = Ident::new(&format!("a_{}", i), proc_macro2::Span::call_site());
604                        let text = ident.to_string();
605                        let q: proc_macro2::TokenStream = quote::quote! {
606                            let #name = <#ftype as userprompt::Prompting>::prompt(Some(#text))?;
607                        };
608                        field_stuff.extend(q);
609                    }
610                }
611
612                let mut q2s: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
613
614                for (i, n) in n.named.iter().enumerate() {
615                    if let Some(ident) = &n.ident {
616                        let name = Ident::new(&format!("a_{}", i), proc_macro2::Span::call_site());
617                        let q2: proc_macro2::TokenStream = quote::quote! {
618                            #ident: #name,
619                        };
620                        q2s.extend(q2);
621                    }
622                }
623
624                let q: proc_macro2::TokenStream = quote::quote! {
625                    Ok(Self {
626                        #q2s
627                    })
628                };
629                field_stuff.extend(q);
630            }
631            expanded = quote::quote! {
632                impl userprompt::Prompting for #sident {
633                    fn prompt(name: Option<&str>) -> Result<Self, userprompt::Error> {
634                        #field_stuff
635                    }
636                }
637            }
638            .into();
639        }
640        _ => panic!("Unhandled object type"),
641    };
642    TokenStream::from(expanded)
643}