1
1
use crate :: attributes:: { self , get_pyo3_options, CrateAttribute } ;
2
2
use crate :: utils:: Ctx ;
3
- use proc_macro2:: TokenStream ;
3
+ use proc_macro2:: { Span , TokenStream } ;
4
4
use quote:: quote;
5
+ use syn:: ext:: IdentExt ;
5
6
use syn:: parse:: { Parse , ParseStream } ;
6
7
use syn:: spanned:: Spanned as _;
7
- use syn:: { parse_quote, Attribute , DeriveInput , Fields , Result , Token } ;
8
+ use syn:: { parse_quote, Attribute , DeriveInput , Fields , Index , Result , Token } ;
8
9
9
10
/// Attributes for deriving FromPyObject scoped on containers.
10
11
enum ContainerPyO3Attribute {
@@ -80,7 +81,6 @@ struct IntoPyObjectImpl {
80
81
81
82
struct NamedStructField < ' a > {
82
83
ident : & ' a syn:: Ident ,
83
- ty : & ' a syn:: Type ,
84
84
}
85
85
86
86
struct TupleStructField { }
@@ -112,16 +112,14 @@ enum ContainerType<'a> {
112
112
///
113
113
/// Either describes a struct or an enum variant.
114
114
struct Container < ' a > {
115
- path : syn:: Path ,
116
115
ty : ContainerType < ' a > ,
117
- err_name : String ,
118
116
}
119
117
120
118
impl < ' a > Container < ' a > {
121
119
/// Construct a container based on fields, identifier and attributes.
122
120
///
123
121
/// Fails if the variant has no fields or incompatible attributes.
124
- fn new ( fields : & ' a Fields , path : syn :: Path , options : ContainerOptions ) -> Result < Self > {
122
+ fn new ( fields : & ' a Fields , options : ContainerOptions ) -> Result < Self > {
125
123
let style = match fields {
126
124
Fields :: Unnamed ( unnamed) if !unnamed. unnamed . is_empty ( ) => {
127
125
if unnamed. unnamed . iter ( ) . count ( ) == 1 {
@@ -161,9 +159,8 @@ impl<'a> Container<'a> {
161
159
. ident
162
160
. as_ref ( )
163
161
. expect ( "Named fields should have identifiers" ) ;
164
- let ty = & field. ty ;
165
162
166
- Ok ( NamedStructField { ident, ty } )
163
+ Ok ( NamedStructField { ident } )
167
164
} )
168
165
. collect :: < Result < Vec < _ > > > ( ) ?;
169
166
ContainerType :: Struct ( struct_fields)
@@ -173,35 +170,19 @@ impl<'a> Container<'a> {
173
170
fields. span( ) => "cannot derive `IntoPyObject` for empty structs and variants"
174
171
) ,
175
172
} ;
176
- let err_name = path. segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
177
173
178
- let v = Container {
179
- path,
180
- ty : style,
181
- err_name,
182
- } ;
174
+ let v = Container { ty : style } ;
183
175
Ok ( v)
184
176
}
185
177
186
- fn name ( & self ) -> String {
187
- let mut value = String :: new ( ) ;
188
- for segment in & self . path . segments {
189
- if !value. is_empty ( ) {
190
- value. push_str ( "::" ) ;
191
- }
192
- value. push_str ( & segment. ident . to_string ( ) ) ;
193
- }
194
- value
195
- }
196
-
197
178
/// Build derivation body for a struct.
198
179
fn build ( & self , ctx : & Ctx ) -> IntoPyObjectImpl {
199
180
match & self . ty {
200
181
ContainerType :: StructNewtype ( field) | ContainerType :: TupleNewtype ( field) => {
201
182
self . build_newtype_struct ( field, ctx)
202
183
}
203
- ContainerType :: Tuple ( tups ) => todo ! ( ) , // self.build_tuple_struct(tups , ctx),
204
- ContainerType :: Struct ( tups ) => todo ! ( ) , // self.build_struct(tups , ctx),
184
+ ContainerType :: Tuple ( fields ) => self . build_tuple_struct ( fields , ctx) ,
185
+ ContainerType :: Struct ( fields ) => self . build_struct ( fields , ctx) ,
205
186
}
206
187
}
207
188
@@ -221,6 +202,59 @@ impl<'a> Container<'a> {
221
202
body : quote ! { <#ty as #pyo3_path:: conversion:: IntoPyObject <' py>>:: into_pyobject( #ident, py) } ,
222
203
}
223
204
}
205
+
206
+ fn build_struct ( & self , fields : & [ NamedStructField < ' _ > ] , ctx : & Ctx ) -> IntoPyObjectImpl {
207
+ let Ctx { pyo3_path, .. } = ctx;
208
+
209
+ let setter = fields
210
+ . iter ( )
211
+ . map ( |f| {
212
+ let key = f. ident . unraw ( ) . to_string ( ) ;
213
+ let ident = f. ident ;
214
+ quote ! {
215
+ dict. set_item( #key, self . #ident) ?;
216
+ }
217
+ } )
218
+ . collect :: < TokenStream > ( ) ;
219
+
220
+ IntoPyObjectImpl {
221
+ target : quote ! ( #pyo3_path:: types:: PyDict ) ,
222
+ output : quote ! ( #pyo3_path:: Bound <' py, Self :: Target >) ,
223
+ error : quote ! ( #pyo3_path:: PyErr ) ,
224
+ body : quote ! {
225
+ let dict = #pyo3_path:: types:: PyDict :: new( py) ;
226
+ #setter
227
+ Ok ( dict)
228
+ } ,
229
+ }
230
+ }
231
+
232
+ fn build_tuple_struct ( & self , fields : & [ TupleStructField ] , ctx : & Ctx ) -> IntoPyObjectImpl {
233
+ let Ctx { pyo3_path, .. } = ctx;
234
+
235
+ let setter = fields
236
+ . iter ( )
237
+ . enumerate ( )
238
+ . map ( |( index, _) | {
239
+ let i = Index {
240
+ index : index as u32 ,
241
+ span : Span :: call_site ( ) ,
242
+ } ;
243
+ quote ! {
244
+ #pyo3_path:: conversion:: IntoPyObject :: into_pyobject( self . #i, py) ?,
245
+ }
246
+ } )
247
+ . collect :: < TokenStream > ( ) ;
248
+
249
+ IntoPyObjectImpl {
250
+ target : quote ! ( #pyo3_path:: types:: PyTuple ) ,
251
+ output : quote ! ( #pyo3_path:: Bound <' py, Self :: Target >) ,
252
+ error : quote ! ( #pyo3_path:: PyErr ) ,
253
+ body : quote ! {
254
+ Ok ( #pyo3_path:: types:: PyTuple :: new( py, [ #setter] ) )
255
+ } ,
256
+ }
257
+ }
224
258
}
225
259
226
260
pub fn build_derive_into_pyobject ( tokens : & DeriveInput ) -> Result < TokenStream > {
@@ -260,8 +294,7 @@ pub fn build_derive_into_pyobject(tokens: &DeriveInput) -> Result<TokenStream> {
260
294
todo ! ( )
261
295
}
262
296
syn:: Data :: Struct ( st) => {
263
- let ident = & tokens. ident ;
264
- let st = Container :: new ( & st. fields , parse_quote ! ( #ident) , options) ?;
297
+ let st = Container :: new ( & st. fields , options) ?;
265
298
st. build ( ctx)
266
299
}
267
300
syn:: Data :: Union ( _) => bail_spanned ! (
0 commit comments