11use proc_macro2:: TokenStream ;
22use quote:: quote;
33use syn:: { DeriveInput , Ident , Result } ;
4+ use syn_ext:: ext:: { AttributeExt , GetIdent } ;
5+ use syn_ext:: types:: Meta ;
46
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 {
7+ // returning a pair of not-skipped and skipped field names
8+ fn field_names ( input : & mut DeriveInput ) -> Result < ( Vec < Ident > , Vec < Ident > ) > {
9+ let syn:: Data :: Struct ( struc) = & mut input. data else {
910 bail_span ! (
1011 input,
1112 "#[pystruct_sequence] can only be on a struct declaration"
1213 )
1314 } ;
1415
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 ! (
16+ let syn:: Fields :: Named ( fields) = & mut struc. fields else {
17+ bail_span ! (
2218 input,
2319 "#[pystruct_sequence] can only be on a struct with named fields"
24- ) ,
20+ ) ;
2521 } ;
2622
27- Ok ( field_names)
23+ let mut not_skipped = Vec :: with_capacity ( fields. named . len ( ) ) ;
24+ let mut skipped = Vec :: with_capacity ( fields. named . len ( ) ) ;
25+ for field in & mut fields. named {
26+ let mut skip = false ;
27+ // Collect all attributes with pystruct and their indices
28+ let mut attrs_to_remove = Vec :: new ( ) ;
29+
30+ for ( i, attr) in field. attrs . iter ( ) . enumerate ( ) {
31+ if !attr. path ( ) . is_ident ( "pystruct" ) {
32+ continue ;
33+ }
34+
35+ let Ok ( meta) = attr. parse_meta ( ) else {
36+ continue ;
37+ } ;
38+
39+ let Meta :: List ( l) = meta else {
40+ bail_span ! ( input, "Only #[pystruct(...)] form is allowed" ) ;
41+ } ;
42+
43+ let idents: Vec < _ > = l
44+ . nested
45+ . iter ( )
46+ . filter_map ( |n| n. get_ident ( ) )
47+ . cloned ( )
48+ . collect ( ) ;
49+
50+ // Follow #[serde(skip)] convention.
51+ // Consider to add skip_serializing and skip_deserializing if required.
52+ for ident in idents {
53+ match ident. to_string ( ) . as_str ( ) {
54+ "skip" => {
55+ skip = true ;
56+ }
57+ _ => {
58+ bail_span ! ( ident, "Unknown item for #[pystruct(...)]" )
59+ }
60+ }
61+ }
62+
63+ attrs_to_remove. push ( i) ;
64+ }
65+
66+ // Remove attributes in reverse order to maintain valid indices
67+ attrs_to_remove. sort_unstable_by ( |a, b| b. cmp ( a) ) ; // Sort in descending order
68+ for index in attrs_to_remove {
69+ field. attrs . remove ( index) ;
70+ }
71+ let ident = field. ident . clone ( ) . unwrap ( ) ;
72+ if skip {
73+ skipped. push ( ident. clone ( ) ) ;
74+ } else {
75+ not_skipped. push ( ident. clone ( ) ) ;
76+ }
77+ }
78+
79+ Ok ( ( not_skipped, skipped) )
2880}
2981
30- pub ( crate ) fn impl_pystruct_sequence ( input : DeriveInput ) -> Result < TokenStream > {
31- let field_names = field_names ( & input) ?;
82+ pub ( crate ) fn impl_pystruct_sequence ( mut input : DeriveInput ) -> Result < TokenStream > {
83+ let ( not_skipped_fields , _skipped_fields ) = field_names ( & mut input) ?;
3284 let ty = & input. ident ;
3385 let ret = quote ! {
3486 impl :: rustpython_vm:: types:: PyStructSequence for #ty {
35- const FIELD_NAMES : & ' static [ & ' static str ] = & [ #( stringify!( #field_names ) ) , * ] ;
87+ const FIELD_NAMES : & ' static [ & ' static str ] = & [ #( stringify!( #not_skipped_fields ) ) , * ] ;
3688 fn into_tuple( self , vm: & :: rustpython_vm:: VirtualMachine ) -> :: rustpython_vm:: builtins:: PyTuple {
3789 let items = vec![ #( :: rustpython_vm:: convert:: ToPyObject :: to_pyobject(
38- self . #field_names ,
90+ self . #not_skipped_fields ,
3991 vm,
4092 ) ) , * ] ;
4193 :: rustpython_vm:: builtins:: PyTuple :: new_unchecked( items. into_boxed_slice( ) )
@@ -50,8 +102,10 @@ pub(crate) fn impl_pystruct_sequence(input: DeriveInput) -> Result<TokenStream>
50102 Ok ( ret)
51103}
52104
53- pub ( crate ) fn impl_pystruct_sequence_try_from_object ( input : DeriveInput ) -> Result < TokenStream > {
54- let field_names = field_names ( & input) ?;
105+ pub ( crate ) fn impl_pystruct_sequence_try_from_object (
106+ mut input : DeriveInput ,
107+ ) -> Result < TokenStream > {
108+ let ( not_skipped_fields, skipped_fields) = field_names ( & mut input) ?;
55109 let ty = & input. ident ;
56110 let ret = quote ! {
57111 impl :: rustpython_vm:: TryFromObject for #ty {
@@ -60,9 +114,14 @@ pub(crate) fn impl_pystruct_sequence_try_from_object(input: DeriveInput) -> Resu
60114 let seq = Self :: try_elements_from:: <LEN >( seq, vm) ?;
61115 // TODO: this is possible to be written without iterator
62116 let mut iter = seq. into_iter( ) ;
63- Ok ( Self { #(
64- #field_names: iter. next( ) . unwrap( ) . clone( ) . try_into_value( vm) ?
65- ) , * } )
117+ Ok ( Self {
118+ #(
119+ #not_skipped_fields: iter. next( ) . unwrap( ) . clone( ) . try_into_value( vm) ?,
120+ ) *
121+ #(
122+ #skipped_fields: vm. ctx. none( ) ,
123+ ) *
124+ } )
66125 }
67126 }
68127 } ;
0 commit comments