@@ -2,7 +2,7 @@ use proc_macro2::TokenStream;
22use quote:: { ToTokens , quote} ;
33use syn:: ext:: IdentExt ;
44use syn:: meta:: ParseNestedMeta ;
5- use syn:: { Attribute , Data , DeriveInput , Expr , Field , Ident , Result , Token , parse_quote} ;
5+ use syn:: { Attribute , Data , DeriveInput , Expr , Field , Ident , Lit , Result , Token , parse_quote} ;
66
77/// The kind of the python parameter, this corresponds to the value of Parameter.kind
88/// (https://docs.python.org/3/library/inspect.html#inspect.Parameter.kind)
@@ -229,28 +229,104 @@ fn compute_arity_bounds(field_attrs: &[ArgAttribute]) -> (usize, usize) {
229229 ( min_arity, max_arity)
230230}
231231
232+ fn python_default_repr ( default : Option < & DefaultValue > ) -> Option < String > {
233+ match default {
234+ None => None ,
235+ Some ( None ) => Some ( "None" . to_owned ( ) ) ,
236+ Some ( Some ( expr) ) => match expr {
237+ Expr :: Lit ( syn:: ExprLit { lit, .. } ) => match lit {
238+ Lit :: Bool ( b) => Some ( if b. value {
239+ "True" . to_owned ( )
240+ } else {
241+ "False" . to_owned ( )
242+ } ) ,
243+ Lit :: Int ( i) => Some ( i. base10_digits ( ) . to_owned ( ) ) ,
244+ Lit :: Float ( f) => Some ( f. base10_digits ( ) . to_owned ( ) ) ,
245+ Lit :: Str ( s) => Some ( format ! ( "'{}'" , s. value( ) ) ) ,
246+ _ => Some ( "None" . to_owned ( ) ) ,
247+ } ,
248+ _ => Some ( "None" . to_owned ( ) ) ,
249+ } ,
250+ }
251+ }
252+
232253pub ( crate ) fn impl_from_args ( input : DeriveInput ) -> Result < TokenStream > {
233- let ( fields, field_attrs) = match input. data {
234- Data :: Struct ( syn:: DataStruct { fields, .. } ) => (
235- fields
236- . iter ( )
237- . enumerate ( )
238- . map ( generate_field)
239- . collect :: < Result < TokenStream > > ( ) ?,
240- fields
241- . iter ( )
242- . filter_map ( |field| field. try_into ( ) . ok ( ) )
243- . collect :: < Vec < ArgAttribute > > ( ) ,
244- ) ,
254+ let struct_fields = match input. data {
255+ Data :: Struct ( syn:: DataStruct { fields, .. } ) => fields,
245256 _ => bail_span ! ( input, "FromArgs input must be a struct" ) ,
246257 } ;
247258
259+ let generated_fields = struct_fields
260+ . iter ( )
261+ . enumerate ( )
262+ . map ( generate_field)
263+ . collect :: < Result < TokenStream > > ( ) ?;
264+ let field_attrs = struct_fields
265+ . iter ( )
266+ . filter_map ( |field| field. try_into ( ) . ok ( ) )
267+ . collect :: < Vec < ArgAttribute > > ( ) ;
268+
248269 let ( min_arity, max_arity) = compute_arity_bounds ( & field_attrs) ;
249270
271+ let takes_keywords_named = field_attrs. iter ( ) . any ( |attr| {
272+ matches ! (
273+ attr. kind,
274+ ParameterKind :: KeywordOnly | ParameterKind :: PositionalOrKeyword
275+ )
276+ } ) ;
277+ let flatten_tys = struct_fields
278+ . iter ( )
279+ . zip ( & field_attrs)
280+ . filter ( |( _, attr) | attr. kind == ParameterKind :: Flatten )
281+ . map ( |( field, _) | field. ty . clone ( ) )
282+ . collect :: < Vec < _ > > ( ) ;
283+ let takes_keywords = quote ! {
284+ #takes_keywords_named #( || <#flatten_tys as :: rustpython_vm:: function:: FromArgs >:: TAKES_KEYWORDS ) *
285+ } ;
286+ let variable_arity = {
287+ let from_bounds = min_arity != max_arity;
288+ quote ! {
289+ #from_bounds #( || <#flatten_tys as :: rustpython_vm:: function:: FromArgs >:: VARIABLE_ARITY ) *
290+ }
291+ } ;
292+
293+ let all_keyword_only = !field_attrs. is_empty ( )
294+ && field_attrs
295+ . iter ( )
296+ . all ( |attr| attr. kind == ParameterKind :: KeywordOnly ) ;
297+ let keyword_only_signature = if all_keyword_only {
298+ let mut parts = Vec :: new ( ) ;
299+ for ( field, attr) in struct_fields. iter ( ) . zip ( & field_attrs) {
300+ let Some ( name) = attr
301+ . name
302+ . clone ( )
303+ . or_else ( || field. ident . as_ref ( ) . map ( |ident| ident. unraw ( ) . to_string ( ) ) )
304+ else {
305+ continue ;
306+ } ;
307+ if let Some ( default) = python_default_repr ( attr. default . as_ref ( ) ) {
308+ parts. push ( format ! ( "{name}={default}" ) ) ;
309+ } else {
310+ parts. push ( name) ;
311+ }
312+ }
313+ let joined = parts. join ( ", " ) ;
314+ quote ! ( Some ( #joined) )
315+ } else {
316+ quote ! ( None )
317+ } ;
318+
319+ let max_positional = if all_keyword_only { 0 } else { max_arity } ;
320+
250321 let name = input. ident ;
251322 let ( impl_generics, ty_generics, where_clause) = input. generics . split_for_impl ( ) ;
252323 let output = quote ! {
253324 impl #impl_generics :: rustpython_vm:: function:: FromArgs for #name #ty_generics #where_clause {
325+ const TAKES_KEYWORDS : bool = #takes_keywords;
326+ const VARIABLE_ARITY : bool = #variable_arity;
327+ const MAX_POSITIONAL : usize = #max_positional;
328+ const KEYWORD_ONLY_SIGNATURE : Option <& ' static str > = #keyword_only_signature;
329+
254330 fn arity( ) -> :: std:: ops:: RangeInclusive <usize > {
255331 #min_arity..=#max_arity
256332 }
@@ -259,7 +335,7 @@ pub(crate) fn impl_from_args(input: DeriveInput) -> Result<TokenStream> {
259335 vm: & :: rustpython_vm:: VirtualMachine ,
260336 args: & mut :: rustpython_vm:: function:: FuncArgs
261337 ) -> :: core:: result:: Result <Self , :: rustpython_vm:: function:: ArgumentError > {
262- Ok ( Self { #fields } )
338+ Ok ( Self { #generated_fields } )
263339 }
264340 }
265341 } ;
0 commit comments