@@ -3,50 +3,177 @@ extern crate proc_macro;
33use proc_macro:: TokenStream ;
44use proc_macro2:: TokenStream as TokenStream2 ;
55use quote:: quote;
6- use syn:: { Data , DeriveInput , Field , Fields } ;
6+ use syn:: { Attribute , Data , DeriveInput , Expr , Field , Fields , Ident , Lit , Meta , NestedMeta } ;
77
8- #[ proc_macro_derive( FromArgs , attributes( positional , keyword ) ) ]
8+ #[ proc_macro_derive( FromArgs , attributes( pyarg ) ) ]
99pub fn derive_from_args ( input : TokenStream ) -> TokenStream {
1010 let ast: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
1111
1212 let gen = impl_from_args ( & ast) ;
1313 gen. to_string ( ) . parse ( ) . unwrap ( )
1414}
1515
16- enum ArgType {
16+ enum ArgKind {
1717 Positional ,
1818 PositionalKeyword ,
1919 Keyword ,
2020}
2121
22- fn generate_field ( field : & Field ) -> TokenStream2 {
23- let arg_type = if let Some ( attr) = field. attrs . first ( ) {
24- if attr. path . is_ident ( "positional" ) {
25- ArgType :: Positional
26- } else if attr. path . is_ident ( "keyword" ) {
27- ArgType :: Keyword
22+ impl ArgKind {
23+ fn from_ident ( ident : & Ident ) -> ArgKind {
24+ if ident == "positional" {
25+ ArgKind :: Positional
26+ } else if ident == "positional_keyword" {
27+ ArgKind :: PositionalKeyword
28+ } else if ident == "keyword" {
29+ ArgKind :: Keyword
2830 } else {
2931 panic ! ( "Unrecognised attribute" )
3032 }
33+ }
34+ }
35+
36+ struct ArgAttribute {
37+ kind : ArgKind ,
38+ default : Option < Expr > ,
39+ optional : bool ,
40+ }
41+
42+ impl ArgAttribute {
43+ fn from_attribute ( attr : & Attribute ) -> Option < ArgAttribute > {
44+ if !attr. path . is_ident ( "pyarg" ) {
45+ return None ;
46+ }
47+
48+ match attr. parse_meta ( ) . unwrap ( ) {
49+ Meta :: List ( list) => {
50+ let mut iter = list. nested . iter ( ) ;
51+ let first_arg = iter. next ( ) . expect ( "at least one argument in pyarg list" ) ;
52+ let kind = match first_arg {
53+ NestedMeta :: Meta ( Meta :: Word ( ident) ) => ArgKind :: from_ident ( ident) ,
54+ _ => panic ! ( "Bad syntax for first pyarg attribute argument" ) ,
55+ } ;
56+
57+ let mut attribute = ArgAttribute {
58+ kind,
59+ default : None ,
60+ optional : false ,
61+ } ;
62+
63+ while let Some ( arg) = iter. next ( ) {
64+ attribute. parse_argument ( arg) ;
65+ }
66+
67+ assert ! (
68+ attribute. default . is_none( ) || !attribute. optional,
69+ "Can't set both a default value and optional"
70+ ) ;
71+
72+ Some ( attribute)
73+ }
74+ _ => panic ! ( "Bad syntax for pyarg attribute" ) ,
75+ }
76+ }
77+
78+ fn parse_argument ( & mut self , arg : & NestedMeta ) {
79+ match arg {
80+ NestedMeta :: Meta ( Meta :: Word ( ident) ) => {
81+ if ident == "default" {
82+ assert ! ( self . default . is_none( ) , "Default already set" ) ;
83+ let expr = syn:: parse_str :: < Expr > ( "Default::default()" ) . unwrap ( ) ;
84+ self . default = Some ( expr) ;
85+ } else if ident == "optional" {
86+ self . optional = true ;
87+ } else {
88+ panic ! ( "Unrecognised pyarg attribute '{}'" , ident) ;
89+ }
90+ }
91+ NestedMeta :: Meta ( Meta :: NameValue ( name_value) ) => {
92+ if name_value. ident == "default" {
93+ assert ! ( self . default . is_none( ) , "Default already set" ) ;
94+
95+ match name_value. lit {
96+ Lit :: Str ( ref val) => {
97+ let expr = val
98+ . parse :: < Expr > ( )
99+ . expect ( "a valid expression for default argument" ) ;
100+ self . default = Some ( expr) ;
101+ }
102+ _ => panic ! ( "Expected string value for default argument" ) ,
103+ }
104+ } else if name_value. ident == "optional" {
105+ match name_value. lit {
106+ Lit :: Bool ( ref val) => {
107+ self . optional = val. value ;
108+ }
109+ _ => panic ! ( "Expected boolean value for optional argument" ) ,
110+ }
111+ } else {
112+ panic ! ( "Unrecognised pyarg attribute '{}'" , name_value. ident) ;
113+ }
114+ }
115+ _ => panic ! ( "Bad syntax for first pyarg attribute argument" ) ,
116+ } ;
117+ }
118+ }
119+
120+ fn generate_field ( field : & Field ) -> TokenStream2 {
121+ let mut pyarg_attrs = field
122+ . attrs
123+ . iter ( )
124+ . filter_map ( ArgAttribute :: from_attribute)
125+ . collect :: < Vec < _ > > ( ) ;
126+ let attr = if pyarg_attrs. is_empty ( ) {
127+ ArgAttribute {
128+ kind : ArgKind :: PositionalKeyword ,
129+ default : None ,
130+ optional : false ,
131+ }
132+ } else if pyarg_attrs. len ( ) == 1 {
133+ pyarg_attrs. remove ( 0 )
31134 } else {
32- ArgType :: PositionalKeyword
135+ panic ! (
136+ "Multiple pyarg attributes on field '{}'" ,
137+ field. ident. as_ref( ) . unwrap( )
138+ ) ;
33139 } ;
34140
35141 let name = & field. ident ;
36- match arg_type {
37- ArgType :: Positional => {
142+ let middle = quote ! {
143+ . map( |x| crate :: pyobject:: TryFromObject :: try_from_object( vm, x) ) . transpose( ) ?
144+ } ;
145+ let ending = if let Some ( default) = attr. default {
146+ quote ! {
147+ . unwrap_or_else( || #default )
148+ }
149+ } else {
150+ let err = match attr. kind {
151+ ArgKind :: Positional | ArgKind :: PositionalKeyword => {
152+ quote ! ( crate :: function:: ArgumentError :: TooFewArgs )
153+ }
154+ ArgKind :: Keyword => quote ! ( crate :: function:: ArgumentError :: RequiredKeywordArgument (
155+ stringify!( #name)
156+ ) ) ,
157+ } ;
158+ quote ! {
159+ . ok_or_else( || #err) ?
160+ }
161+ } ;
162+
163+ match attr. kind {
164+ ArgKind :: Positional => {
38165 quote ! {
39- #name: args. take_positional( vm ) ? ,
166+ #name: args. take_positional( ) #middle#ending ,
40167 }
41168 }
42- ArgType :: PositionalKeyword => {
169+ ArgKind :: PositionalKeyword => {
43170 quote ! {
44- #name: args. take_positional_keyword( vm , stringify!( #name) ) ? ,
171+ #name: args. take_positional_keyword( stringify!( #name) ) #middle#ending ,
45172 }
46173 }
47- ArgType :: Keyword => {
174+ ArgKind :: Keyword => {
48175 quote ! {
49- #name: args. take_keyword( vm , stringify!( #name) ) ? ,
176+ #name: args. take_keyword( stringify!( #name) ) #middle#ending ,
50177 }
51178 }
52179 }
0 commit comments