11use proc_macro:: TokenStream ;
22use quote:: quote;
3- use syn:: { self , DataStruct } ;
3+ use syn:: { self } ;
44
55/// Automatically implement "Prompt" for all fields in a struct.
66#[ proc_macro_derive( Prompt ) ]
@@ -11,30 +11,70 @@ pub fn prompt(input: TokenStream) -> TokenStream {
1111
1212fn impl_prompt ( ast : & syn:: DeriveInput ) -> TokenStream {
1313 let name = & ast. ident ;
14- let fields: Vec < String > = match & ast. data {
14+
15+ let fields_to_prompt: Vec < _ > = match & ast. data {
1516 syn:: Data :: Struct ( data) => match & data. fields {
1617 syn:: Fields :: Named ( fields) => fields
1718 . named
1819 . iter ( )
19- . map ( |f| f. ident . clone ( ) . unwrap ( ) . to_string ( ) )
20+ . filter_map ( |f| {
21+ // Skip fields with #[serde(flatten)]
22+ let is_flattened = f. attrs . iter ( ) . any ( |attr| {
23+ attr. path ( ) . is_ident ( "serde" )
24+ && attr
25+ . parse_args :: < syn:: Ident > ( )
26+ . map ( |i| i == "flatten" )
27+ . unwrap_or ( false )
28+ } ) ;
29+
30+ if is_flattened {
31+ None
32+ } else {
33+ Some ( ( f. ident . clone ( ) . unwrap ( ) , f. ty . clone ( ) ) )
34+ }
35+ } )
2036 . collect ( ) ,
21- _ => panic ! ( ) ,
37+ _ => panic ! ( "Prompt derive only works on structs with named fields" ) ,
2238 } ,
23- _ => panic ! ( ) ,
39+ _ => panic ! ( "Prompt derive only works on structs" ) ,
2440 } ;
2541
42+ let prompt_calls = fields_to_prompt. iter ( ) . map ( |( field, ty) | {
43+ // Check if the type is an Option
44+ if is_option_type ( ty) {
45+ quote ! {
46+ if let Some ( ref mut value) = self . #field {
47+ value. prompt( foundry) ?;
48+ }
49+ }
50+ } else {
51+ quote ! {
52+ self . #field. prompt( foundry) ?;
53+ }
54+ }
55+ } ) ;
56+
2657 let syntax = quote ! {
27- impl Prompt for #name {
58+ impl crate :: cli :: prompt :: Prompt for #name {
2859 fn prompt(
2960 & mut self ,
30- _: & Foundry ,
31- theme: impl dialoguer:: theme:: Theme ,
61+ foundry: & crate :: foundry:: Foundry ,
3262 ) -> anyhow:: Result <( ) > {
33- todo!( )
63+ #( #prompt_calls) *
64+ Ok ( ( ) )
3465 }
3566 }
3667 } ;
3768 syntax. into ( )
3869}
3970
71+ fn is_option_type ( ty : & syn:: Type ) -> bool {
72+ if let syn:: Type :: Path ( type_path) = ty {
73+ if let Some ( segment) = type_path. path . segments . last ( ) {
74+ return segment. ident == "Option" ;
75+ }
76+ }
77+ false
78+ }
79+
4080// TODO probably need a macro for ImageMold and Fabricator
0 commit comments