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#[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#[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")]
272#[proc_macro_derive(EguiPrompting, attributes(PromptComment))]
273pub fn derive_egui_prompting(input: TokenStream) -> TokenStream {
274 use std::any::Any;
275
276 let input = syn::parse_macro_input!(input as DeriveInput);
277 let sident = input.ident;
278 let expanded: TokenStream;
279
280 match &input.data {
281 syn::Data::Enum(e) => {
282 let mut field_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
283 let q: proc_macro2::TokenStream = quote::quote! {
284 let combobox = if let Some(name) = name {
285 let mut s = "Select a ".to_string();
286 s.push_str(&name);
287 egui::ComboBox::from_label(s)
288 } else {
289 egui::ComboBox::from_label("Select")
290 };
291 };
292 field_stuff.extend(q);
293
294 let mut user_info: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
295 for v in &e.variants {
296 let a = get_comment_from_attrs(&v.attrs);
297 if let Some(a) = a {
298 let (_q, t) = build_enum_variant_to_string(v);
299 user_info.extend(quote::quote! {
300 ui.label(format!("{} - {}", #t, #a));
301 });
302 }
303 }
304 field_stuff.extend(user_info);
305
306 let mut match_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
307 for v in &e.variants {
308 let (q, t) = build_enum_variant_to_string(v);
309 match_stuff.extend(quote::quote! {
310 #q => #t,
311 });
312 }
313
314 let q_start: proc_macro2::TokenStream = quote::quote! {
315 let val = match self {
316 #match_stuff
317 };
318 };
319 field_stuff.extend(q_start);
320
321 let mut combo_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
322 for v in &e.variants {
323 let text = v.ident.to_string();
324 let assign = build_enum_variant_builder(v);
325 let t = v.type_id();
326 let q: proc_macro2::TokenStream = quote::quote! {
327 if ui.selectable_label(false, #text).clicked() {
328 *self = #assign;
329 }
330 };
331 combo_stuff.extend(q);
332 }
333
334 let mut option_prompt = proc_macro2::TokenStream::new();
335 let mut checks = proc_macro2::TokenStream::new();
336 for v in &e.variants {
337 let (q, f) = build_enum_variant_to_fields(v);
338 let a = get_comment_from_attrs(&v.attrs);
339 let mut option_code: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
340 let mut check_item = proc_macro2::TokenStream::new();
341 if let Some(a) = a {
342 let q = quote::quote! {
343 ui.label(#a);
344 };
345 option_code.extend(q);
346 }
347 if !f.is_empty() {
348 for (i, f) in f.iter().enumerate() {
349 let a = get_comment(f);
350 if let Some(ident) = &f.ident {
351 let varname = quote::format_ident!("{}", ident);
352 let text = ident.to_string();
353 let q = match a {
354 Some(a) => quote::quote! {
355 let subname = format!("{}/{}", name.unwrap_or(""), #text);
356 #varname.build_gui(ui, Some(&subname), Some(#a))?;
357 ui.separator();
358 },
359 None => quote::quote! {
360 let subname = format!("{}/{}", name.unwrap_or(""), #text);
361 #varname.build_gui(ui, Some(&subname), None)?;
362 ui.separator();
363 },
364 };
365 option_code.extend(q);
366
367 let q2 = quote::quote! {
368 let subname = format!("{}/{}", name.unwrap_or(""), #text);
369 #varname.check(Some(&subname))?;
370 };
371 check_item.extend(q2);
372 } else {
373 let varname = quote::format_ident!("a_{}", i);
374 let text = format!("{}", i);
375 let q = match a {
376 Some(a) => quote::quote! {
377 let subname = format!("{}/{}", name.unwrap_or(""), #text);
378 #varname.build_gui(ui, Some(&subname), Some(#a))?;
379 ui.separator();
380 },
381 None => quote::quote! {
382 let subname = format!("{}/{}", name.unwrap_or(""), #text);
383 #varname.build_gui(ui, Some(&subname), None)?;
384 ui.separator();
385 },
386 };
387 option_code.extend(q);
388
389 let q2 = quote::quote! {
390 let subname = format!("{}/{}", name.unwrap_or(""), #text);
391 #varname.check(Some(&subname))?;
392 };
393 check_item.extend(q2);
394 }
395 }
396 option_prompt.extend(quote::quote! {
397 #q => { #option_code },
398 });
399 checks.extend(quote::quote! {
400 #q => { #check_item },
401 });
402 }
403 }
404
405 expanded = quote::quote! {
406 impl userprompt::EguiPrompting for #sident {
407 fn build_gui(&mut self, ui: &mut egui::Ui, name: Option<&str>, comment: Option<&str>) -> Result<(), String> {
408 #field_stuff
409 combobox.selected_text(val)
410 .show_ui(ui, |ui| { #combo_stuff });
411 match self {
412 #option_prompt
413 _ => {}
414 }
415 self.check(name)
416 }
417
418 fn check(&self, name: Option<&str>) -> Result<(), String> {
419 match self {
420 #checks
421 _ => {}
422 }
423 Ok(())
424 }
425 }
426 }
427 .into();
428 }
429 syn::Data::Struct(s) => {
430 let fields = &s.fields;
431 let mut field_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
432 let mut checks = proc_macro2::TokenStream::new();
433
434 let q: proc_macro2::TokenStream = quote::quote! {
435 if let Some(name) = name {
436 ui.label(name);
437 }
438 };
439 field_stuff.extend(q);
440
441 if let syn::Fields::Named(n) = fields {
442 for n in n.named.iter() {
443 let a = get_comment(n);
444 if let Some(ident) = &n.ident {
445 let text = ident.to_string();
446 let varname = quote::format_ident!("{}", ident);
447 let q = match a {
448 Some(a) => quote::quote! {
449 let subname = format!("{}/{}", name.unwrap_or(""), #text);
450 self.#varname.build_gui(ui, Some(&subname), Some(#a))?;
451 ui.separator();
452 },
453 None => quote::quote! {
454 let subname = format!("{}/{}", name.unwrap_or(""), #text);
455 self.#varname.build_gui(ui, Some(&subname), None)?;
456 ui.separator();
457 },
458 };
459 field_stuff.extend(q);
460 let q2 = quote::quote! {
461 let subname = format!("{}/{}", name.unwrap_or(""), #text);
462 self.#varname.check(Some(&subname))?;
463 };
464 checks.extend(q2);
465 }
466 }
467
468 let mut q2s: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
469
470 for (i, n) in n.named.iter().enumerate() {
471 if let Some(ident) = &n.ident {
472 let name = Ident::new(&format!("a_{}", i), proc_macro2::Span::call_site());
473 let q2: proc_macro2::TokenStream = quote::quote! {
474 #ident: #name,
475 };
476 q2s.extend(q2);
477 }
478 }
479
480 let q: proc_macro2::TokenStream = quote::quote! {
481 Ok(())
482 };
483 field_stuff.extend(q);
484 }
485 expanded = quote::quote! {
486 impl userprompt::EguiPrompting for #sident {
487 fn build_gui(&mut self, ui: &mut egui::Ui, name: Option<&str>, comment: Option<&str>) -> Result<(), String> {
488 #field_stuff
489 }
490
491 fn check(&self, name: Option<&str>) -> Result<(), String> {
492 #checks
493 Ok(())
494 }
495 }
496 }
497 .into();
498 }
499 _ => panic!("Unhandled object type"),
500 };
501 TokenStream::from(expanded)
502}
503
504fn get_comment(field: &syn::Field) -> Option<syn::Expr> {
506 get_comment_from_attrs(&field.attrs)
507}
508
509fn get_comment_from_attrs(attrs: &Vec<syn::Attribute>) -> Option<syn::Expr> {
511 attrs
512 .iter()
513 .filter(|p| p.path().is_ident("PromptComment"))
514 .take(1)
515 .next()
516 .map(|a| a.meta.require_name_value().unwrap().value.clone())
517}
518
519#[proc_macro_derive(Prompting, attributes(PromptComment))]
522pub fn derive_prompting(input: TokenStream) -> TokenStream {
523 let input = syn::parse_macro_input!(input as DeriveInput);
524 let sident = input.ident;
525 let expanded: TokenStream;
526 match &input.data {
527 syn::Data::Enum(e) => {
528 let mut field_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
529 let q: proc_macro2::TokenStream = quote::quote! {
530 if let Some(name) = name {
531 println!("[{}]", name);
532 }
533 };
534 field_stuff.extend(q);
535
536 let q: proc_macro2::TokenStream = quote::quote! {
537 println!("Enter the variant type, valid options are listed below");
538 };
539 field_stuff.extend(q);
540
541 for v in &e.variants {
542 let text = v.ident.to_string();
543 let a = get_comment_from_attrs(&v.attrs);
544 let q = match a {
545 Some(a) => quote::quote! {
546 println!("\t{} - {}", #text, #a);
547 },
548 None => quote::quote! {
549 println!("\t{}", #text);
550 },
551 };
552 field_stuff.extend(q);
553 }
554
555 let name = Ident::new(&format!("a"), proc_macro2::Span::call_site());
556 let q: proc_macro2::TokenStream = quote::quote! {
557 let #name = <String as userprompt::Prompting>::prompt(None, None)?;
558 };
559 field_stuff.extend(q);
560
561 let mut match_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
562 for v in &e.variants {
563 let text2 = v.ident.to_string();
564 let q: proc_macro2::TokenStream = match &v.fields {
565 syn::Fields::Named(f) => {
566 let mut def: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
567 let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
568 for (i, f) in f.named.iter().enumerate() {
569 if i != 0 {
570 tokens.extend([proc_macro2::TokenTree::Punct(
571 proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
572 )]);
573 }
574 let a = get_comment(f);
575 tokens.extend([proc_macro2::TokenTree::Ident(
576 f.ident.as_ref().unwrap().clone(),
577 )]);
578 let ftype = &f.ty;
579 let text = f.ident.as_ref().unwrap().to_string();
580 let val = match a {
581 Some(a) => {
582 quote::quote!(<#ftype as userprompt::Prompting>::prompt(Some(#text), Some(#a))?)
583 }
584 None => {
585 quote::quote!(<#ftype as userprompt::Prompting>::prompt(Some(#text), None)?)
586 }
587 };
588 tokens.extend([proc_macro2::TokenTree::Punct(
589 proc_macro2::Punct::new(':', proc_macro2::Spacing::Alone),
590 )]);
591 tokens.extend(val);
592 }
593 def.extend(tokens);
594
595 let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
596 tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
597 tokens.extend([
598 proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
599 ':',
600 proc_macro2::Spacing::Joint,
601 )),
602 proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
603 ':',
604 proc_macro2::Spacing::Alone,
605 )),
606 ]);
607 tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
608 tokens.extend([proc_macro2::TokenTree::Group(proc_macro2::Group::new(
609 proc_macro2::Delimiter::Brace,
610 def,
611 ))]);
612 quote::quote! {
613 #text2 => { return Ok(#tokens); }
614 }
615 }
616 syn::Fields::Unnamed(f) => {
617 let mut def: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
618 let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
619 for (i, f) in f.unnamed.iter().enumerate() {
620 if i != 0 {
621 tokens.extend([proc_macro2::TokenTree::Punct(
622 proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone),
623 )]);
624 }
625 tokens.extend([proc_macro2::TokenTree::Literal(
626 proc_macro2::Literal::usize_unsuffixed(i),
627 )]);
628 let a = get_comment(f);
629 let ftype = &f.ty;
630 let val = match a {
631 Some(a) => {
632 quote::quote!(<#ftype as userprompt::Prompting>::prompt(Some(&format!("{}", #i)), Some(#a))?)
633 }
634 None => {
635 quote::quote!(<#ftype as userprompt::Prompting>::prompt(Some(&format!("{}", #i)), None)?)
636 }
637 };
638 tokens.extend([proc_macro2::TokenTree::Punct(
639 proc_macro2::Punct::new(':', proc_macro2::Spacing::Alone),
640 )]);
641 tokens.extend(val);
642 }
643 def.extend(tokens);
644
645 let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
646 tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
647 tokens.extend([
648 proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
649 ':',
650 proc_macro2::Spacing::Joint,
651 )),
652 proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
653 ':',
654 proc_macro2::Spacing::Alone,
655 )),
656 ]);
657 tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
658 tokens.extend([proc_macro2::TokenTree::Group(proc_macro2::Group::new(
659 proc_macro2::Delimiter::Brace,
660 def,
661 ))]);
662 quote::quote! {
663 #text2 => { return Ok(#tokens); }
664 }
665 }
666 syn::Fields::Unit => {
667 let mut tokens: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
668 tokens.extend([proc_macro2::TokenTree::Ident(sident.clone())]);
669 tokens.extend([
670 proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
671 ':',
672 proc_macro2::Spacing::Joint,
673 )),
674 proc_macro2::TokenTree::Punct(proc_macro2::Punct::new(
675 ':',
676 proc_macro2::Spacing::Alone,
677 )),
678 ]);
679 tokens.extend([proc_macro2::TokenTree::Ident(v.ident.clone())]);
680 quote::quote! {
681 #text2 => { return Ok(#tokens); }
682 }
683 }
684 };
685 match_stuff.extend(q);
686 }
687
688 let match_else: proc_macro2::TokenStream =
689 quote::quote!(_ => println!("Invalid option"),);
690
691 let q_start: proc_macro2::TokenStream = quote::quote! {
692 match a.as_str() {
693 #match_stuff
694 #match_else
695 }
696 };
697 field_stuff.extend(q_start);
698
699 expanded = quote::quote! {
700 impl userprompt::Prompting for #sident {
701 fn prompt(name: Option<&str>, comment: Option<&str>) -> Result<Self, userprompt::Error> {
702 loop {
703 #field_stuff
704 }
705 }
706 }
707 }
708 .into();
709 }
710 syn::Data::Struct(s) => {
711 let fields = &s.fields;
712 let mut field_stuff: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
713
714 let q: proc_macro2::TokenStream = quote::quote! {
715 if let Some(name) = name {
716 println!("[{}]", name);
717 }
718 };
719 field_stuff.extend(q);
720
721 if let syn::Fields::Named(n) = fields {
722 for (i, n) in n.named.iter().enumerate() {
723 let ftype = &n.ty;
724 if let Some(ident) = &n.ident {
725 let name = Ident::new(&format!("a_{}", i), proc_macro2::Span::call_site());
726 let text = ident.to_string();
727 let a = get_comment_from_attrs(&n.attrs);
728 let q: proc_macro2::TokenStream = match a {
729 Some(a) => {
730 quote::quote! {
731 let #name = <#ftype as userprompt::Prompting>::prompt(Some(#text), Some(#a))?;
732 }
733 }
734 None => {
735 quote::quote! {
736 let #name = <#ftype as userprompt::Prompting>::prompt(Some(#text), None)?;
737 }
738 }
739 };
740 field_stuff.extend(q);
741 }
742 }
743
744 let mut q2s: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
745
746 for (i, n) in n.named.iter().enumerate() {
747 if let Some(ident) = &n.ident {
748 let name = Ident::new(&format!("a_{}", i), proc_macro2::Span::call_site());
749 let q2: proc_macro2::TokenStream = quote::quote! {
750 #ident: #name,
751 };
752 q2s.extend(q2);
753 }
754 }
755
756 let q: proc_macro2::TokenStream = quote::quote! {
757 Ok(Self {
758 #q2s
759 })
760 };
761 field_stuff.extend(q);
762 }
763 expanded = quote::quote! {
764 impl userprompt::Prompting for #sident {
765 fn prompt(name: Option<&str>, comment: Option<&str>) -> Result<Self, userprompt::Error> {
766 #field_stuff
767 }
768 }
769 }
770 .into();
771 }
772 _ => panic!("Unhandled object type"),
773 };
774 TokenStream::from(expanded)
775}