@@ -2,7 +2,7 @@ use proc_macro2::{Span, TokenStream};
22use quote:: quote;
33use syn:: Ident ;
44
5- use crate :: ir:: { self , Expr , FieldKind , LeafKind } ;
5+ use crate :: ir:: { self , Expr , FieldKind , LeafKind , MapKey } ;
66
77
88
@@ -21,7 +21,7 @@ pub(super) fn gen(input: &ir::Input) -> TokenStream {
2121 let name = f. name . to_string ( ) ;
2222 let doc = & f. doc ;
2323 let kind = match & f. kind {
24- FieldKind :: Nested { ty } => {
24+ FieldKind :: Nested { ty } => {
2525 quote ! {
2626 confique:: meta:: FieldKind :: Nested { meta: & <#ty as confique:: Config >:: META }
2727 }
@@ -73,27 +73,50 @@ pub(super) fn gen(input: &ir::Input) -> TokenStream {
7373 }
7474}
7575
76+ /// Helper macro to deduplicate logic for literals. Only used in the function
77+ /// below.
78+ macro_rules! match_literals {
79+ ( $v: expr, $ty: expr, $ns: ident, { $( $other_arms: tt) * } ) => {
80+ match $v {
81+ $ns:: Bool ( v) => quote! { confique:: meta:: $ns:: Bool ( #v) } ,
82+ $ns:: Str ( s) => quote! { confique:: meta:: $ns:: Str ( #s) } ,
83+ $ns:: Int ( i) => {
84+ let variant = infer_type( i. suffix( ) , $ty, "I32" , int_type_to_variant) ;
85+ quote! { confique:: meta:: $ns:: Integer ( confique:: meta:: Integer :: #variant( #i) ) }
86+ }
87+ $ns:: Float ( f) => {
88+ let variant = infer_type( f. suffix( ) , $ty, "F64" , float_type_to_variant) ;
89+ quote! { confique:: meta:: $ns:: Float ( confique:: meta:: Float :: #variant( #f) ) }
90+ }
91+ $( $other_arms) *
92+ }
93+ } ;
94+ }
95+
7696/// Generates the meta expression of type `meta::Expr` to be used for the
7797/// `default` field. `ty` is the type of the field that is used to better infer
7898/// the exact type of the default value.
7999fn default_value_to_meta_expr ( default : & Expr , ty : Option < & syn:: Type > ) -> TokenStream {
80- match default {
81- Expr :: Bool ( v) => quote ! { confique:: meta:: Expr :: Bool ( #v) } ,
82- Expr :: Str ( s) => quote ! { confique:: meta:: Expr :: Str ( #s) } ,
83- Expr :: Int ( i) => {
84- let variant = infer_type ( i. suffix ( ) , ty, "I32" , int_type_to_variant) ;
85- quote ! { confique:: meta:: Expr :: Integer ( confique:: meta:: Integer :: #variant( #i) ) }
86- }
87- Expr :: Float ( f) => {
88- let variant = infer_type ( f. suffix ( ) , ty, "F64" , float_type_to_variant) ;
89- quote ! { confique:: meta:: Expr :: Float ( confique:: meta:: Float :: #variant( #f) ) }
90- }
100+ match_literals ! ( default , ty, Expr , {
91101 Expr :: Array ( items) => {
92- let item_type = ty. and_then ( get_item_ty ) ;
102+ let item_type = ty. and_then( get_array_item_type ) ;
93103 let items = items. iter( ) . map( |item| default_value_to_meta_expr( item, item_type) ) ;
94104 quote! { confique:: meta:: Expr :: Array ( & [ #( #items ) , * ] ) }
95105 }
96- }
106+ Expr :: Map ( entries) => {
107+ // TODO: use `Option::unzip` once stable
108+ let types = ty. and_then( get_map_entry_types) ;
109+ let key_type = types. map( |( t, _) | t) ;
110+ let value_type = types. map( |( _, v) | v) ;
111+
112+ let pairs = entries. iter( ) . map( |e| {
113+ let key = match_literals!( & e. key, key_type, MapKey , { } ) ;
114+ let value = default_value_to_meta_expr( & e. value, value_type) ;
115+ quote! { confique:: meta:: MapEntry { key: #key, value: #value } }
116+ } ) ;
117+ quote! { confique:: meta:: Expr :: Map ( & [ #( #pairs ) , * ] ) }
118+ }
119+ } )
97120}
98121
99122/// Maps an integer type to the `meta::Expr` variant (e.g. `u32` -> `U32`).
@@ -149,10 +172,9 @@ fn infer_type(
149172 Ident :: new ( variant, Span :: call_site ( ) )
150173}
151174
152-
153175/// Tries to extract the type of the item of a field with an array default
154176/// value. Examples: `&[u32]` -> `u32`, `Vec<String>` -> `String`.
155- fn get_item_ty ( ty : & syn:: Type ) -> Option < & syn:: Type > {
177+ fn get_array_item_type ( ty : & syn:: Type ) -> Option < & syn:: Type > {
156178 match ty {
157179 // The easy types.
158180 syn:: Type :: Slice ( slice) => Some ( & slice. elem ) ,
@@ -184,9 +206,40 @@ fn get_item_ty(ty: &syn::Type) -> Option<&syn::Type> {
184206 } ,
185207
186208 // Just recurse on inner type.
187- syn:: Type :: Reference ( r) => get_item_ty ( & r. elem ) ,
188- syn:: Type :: Group ( g) => get_item_ty ( & g. elem ) ,
189- syn:: Type :: Paren ( p) => get_item_ty ( & p. elem ) ,
209+ syn:: Type :: Reference ( r) => get_array_item_type ( & r. elem ) ,
210+ syn:: Type :: Group ( g) => get_array_item_type ( & g. elem ) ,
211+ syn:: Type :: Paren ( p) => get_array_item_type ( & p. elem ) ,
212+
213+ _ => None ,
214+ }
215+ }
216+
217+ /// Tries to extract the key and value types from a map value. Examples:
218+ /// `HashMap<String, u32>` -> `(String, u32)`.
219+ fn get_map_entry_types ( ty : & syn:: Type ) -> Option < ( & syn:: Type , & syn:: Type ) > {
220+ match ty {
221+ // We simply check if the last element in the path has exactly two
222+ // generic type arguments, in which case we use those. Otherwise we
223+ // can't really know.
224+ syn:: Type :: Path ( p) => {
225+ let args = match & p. path . segments . last ( ) . expect ( "empty type path" ) . arguments {
226+ syn:: PathArguments :: AngleBracketed ( args) => & args. args ,
227+ _ => return None ,
228+ } ;
229+
230+ if args. len ( ) != 2 {
231+ return None ;
232+ }
233+
234+ match ( & args[ 0 ] , & args[ 1 ] ) {
235+ ( syn:: GenericArgument :: Type ( k) , syn:: GenericArgument :: Type ( v) ) => Some ( ( k, v) ) ,
236+ _ => None ,
237+ }
238+ } ,
239+
240+ // Just recurse on inner type.
241+ syn:: Type :: Group ( g) => get_map_entry_types ( & g. elem ) ,
242+ syn:: Type :: Paren ( p) => get_map_entry_types ( & p. elem ) ,
190243
191244 _ => None ,
192245 }
0 commit comments