1
1
use proc_macro2:: TokenStream ;
2
2
use quote:: quote;
3
3
use syn:: { DeriveInput , Ident , Result } ;
4
+ use syn_ext:: ext:: GetIdent ;
4
5
5
- fn field_names ( input : & DeriveInput ) -> Result < Vec < & Ident > > {
6
- let fields = if let syn:: Data :: Struct ( ref struc) = input. data {
7
- & struc. fields
8
- } else {
6
+ struct Field {
7
+ ident : Ident ,
8
+ skip : bool ,
9
+ }
10
+
11
+ fn collect_fields ( input : & mut DeriveInput ) -> Result < Vec < Field > > {
12
+ let syn:: Data :: Struct ( struc) = & mut input. data else {
9
13
bail_span ! (
10
14
input,
11
15
"#[pystruct_sequence] can only be on a struct declaration"
12
16
)
13
17
} ;
14
18
15
- let field_names: Vec < _ > = match fields {
16
- syn:: Fields :: Named ( fields) => fields
17
- . named
18
- . iter ( )
19
- . map ( |field| field. ident . as_ref ( ) . unwrap ( ) )
20
- . collect ( ) ,
21
- _ => bail_span ! (
19
+ let syn:: Fields :: Named ( fields) = & mut struc. fields else {
20
+ bail_span ! (
22
21
input,
23
22
"#[pystruct_sequence] can only be on a struct with named fields"
24
- ) ,
23
+ ) ;
25
24
} ;
26
25
27
- Ok ( field_names)
26
+ let mut fields_info = Vec :: with_capacity ( fields. named . len ( ) ) ;
27
+ for field in & mut fields. named {
28
+ let mut removed_indices = Vec :: new ( ) ;
29
+ let mut skip = false ;
30
+ for ( i, attr) in field. attrs . iter ( ) . enumerate ( ) . rev ( ) {
31
+ if !attr. path . is_ident ( "pystruct" ) {
32
+ continue ;
33
+ }
34
+ removed_indices. push ( i) ;
35
+ let Ok ( meta) = attr. parse_meta ( ) else {
36
+ continue ;
37
+ } ;
38
+ let syn:: Meta :: List ( l) = meta else {
39
+ bail_span ! ( input, "Only #[pystruct(...)] form is allowed" ) ;
40
+ } ;
41
+ let idents: Vec < _ > = l
42
+ . nested
43
+ . iter ( )
44
+ . filter_map ( |n| n. get_ident ( ) )
45
+ . cloned ( )
46
+ . collect ( ) ;
47
+ // Follow #[serde(skip)] convention.
48
+ // Consider to add skip_serializing and skip_deserializing if required.
49
+ for ident in idents {
50
+ match ident. to_string ( ) . as_str ( ) {
51
+ "skip" => {
52
+ skip = true ;
53
+ }
54
+ _ => {
55
+ bail_span ! ( ident, "Unknown item for #[pystruct(...)]" )
56
+ }
57
+ }
58
+ }
59
+ }
60
+ eprintln ! ( "removed indices: {:?}" , removed_indices) ;
61
+ for index in removed_indices {
62
+ field. attrs . remove ( index) ;
63
+ }
64
+ let ident = field. ident . clone ( ) . unwrap ( ) ;
65
+ fields_info. push ( Field { ident, skip } ) ;
66
+ }
67
+
68
+ Ok ( fields_info)
28
69
}
29
70
30
- pub ( crate ) fn impl_pystruct_sequence ( input : DeriveInput ) -> Result < TokenStream > {
31
- let field_names = field_names ( & input) ?;
71
+ pub ( crate ) fn impl_pystruct_sequence ( mut input : DeriveInput ) -> Result < TokenStream > {
72
+ let fields = collect_fields ( & mut input) ?;
73
+ let field_names: Vec < _ > = fields. iter ( ) . map ( |f| f. ident . clone ( ) ) . collect ( ) ;
32
74
let ty = & input. ident ;
33
75
let ret = quote ! {
34
76
impl :: rustpython_vm:: types:: PyStructSequence for #ty {
@@ -50,9 +92,20 @@ pub(crate) fn impl_pystruct_sequence(input: DeriveInput) -> Result<TokenStream>
50
92
Ok ( ret)
51
93
}
52
94
53
- pub ( crate ) fn impl_pystruct_sequence_try_from_object ( input : DeriveInput ) -> Result < TokenStream > {
54
- let field_names = field_names ( & input) ?;
95
+ pub ( crate ) fn impl_pystruct_sequence_try_from_object (
96
+ mut input : DeriveInput ,
97
+ ) -> Result < TokenStream > {
98
+ let fields = collect_fields ( & mut input) ?;
55
99
let ty = & input. ident ;
100
+ let field_exprs: Vec < _ > = fields. into_iter ( ) . map ( |f| {
101
+ let name = f. ident . clone ( ) ;
102
+ if f. skip {
103
+ // FIXME: expected to be customizable
104
+ quote ! { #name: vm. ctx. none( ) . clone( ) . into( ) }
105
+ } else {
106
+ quote ! { #name: iter. next( ) . unwrap( ) . clone( ) . try_into_value( vm) ? }
107
+ }
108
+ } ) . collect ( ) ;
56
109
let ret = quote ! {
57
110
impl :: rustpython_vm:: TryFromObject for #ty {
58
111
fn try_from_object( vm: & :: rustpython_vm:: VirtualMachine , seq: :: rustpython_vm:: PyObjectRef ) -> :: rustpython_vm:: PyResult <Self > {
@@ -61,7 +114,7 @@ pub(crate) fn impl_pystruct_sequence_try_from_object(input: DeriveInput) -> Resu
61
114
// TODO: this is possible to be written without iterator
62
115
let mut iter = seq. into_iter( ) ;
63
116
Ok ( Self { #(
64
- #field_names : iter . next ( ) . unwrap ( ) . clone ( ) . try_into_value ( vm ) ?
117
+ #field_exprs
65
118
) , * } )
66
119
}
67
120
}
0 commit comments