1
1
use crate :: syn;
2
2
3
+ use std:: collections:: HashMap ;
4
+
3
5
use darling:: { ast, FromDeriveInput , FromField } ;
4
6
use proc_macro2:: TokenStream ;
5
7
use quote:: { quote, ToTokens } ;
@@ -14,11 +16,12 @@ pub struct WorldOpts {
14
16
}
15
17
16
18
#[ derive( FromField ) ]
17
- #[ darling( attributes( split ) ) ]
19
+ #[ darling( attributes( groups ) , forward_attrs ) ]
18
20
struct FieldOpts {
19
21
ident : Option < syn:: Ident > ,
20
22
// ty: syn::Type,
21
- // groups: Option<Vec<()>>,
23
+ attrs : Vec < syn:: Attribute > ,
24
+ // groups: Option<Vec<syn::LitStr>>,
22
25
}
23
26
24
27
struct Struct {
@@ -31,7 +34,7 @@ struct Struct {
31
34
struct Field {
32
35
name : syn:: Ident ,
33
36
// ty: syn::Type,
34
- // groups: Vec<() >,
37
+ groups : Vec < syn :: LitStr > ,
35
38
}
36
39
37
40
#[ derive( thiserror:: Error , Debug ) ]
@@ -40,6 +43,8 @@ enum ParseError {
40
43
NotAStruct ,
41
44
#[ error( "field has no name" ) ]
42
45
NamelessField ,
46
+ #[ error( "`groups` attribute accepts only a list of string literals: {0}" ) ]
47
+ InvalidGroups ( syn:: Error ) ,
43
48
}
44
49
45
50
impl TryFrom < WorldOpts > for Struct {
@@ -58,6 +63,7 @@ impl TryFrom<WorldOpts> for Struct {
58
63
Ok ( Field {
59
64
name,
60
65
// ty: field.ty
66
+ groups : extract_groups ( & field. attrs ) ?,
61
67
} )
62
68
} )
63
69
. collect :: < Result < Vec < Field > , ParseError > > ( ) ?;
@@ -70,6 +76,38 @@ impl TryFrom<WorldOpts> for Struct {
70
76
}
71
77
}
72
78
79
+ struct Groups ( syn:: punctuated:: Punctuated < syn:: LitStr , syn:: Token ![ , ] > ) ;
80
+
81
+ impl syn:: parse:: Parse for Groups {
82
+ fn parse ( input : syn:: parse:: ParseStream ) -> syn:: Result < Self > {
83
+ let ident: syn:: Ident = input. parse ( ) ?;
84
+ if ident != "groups" {
85
+ return Err ( syn:: Error :: new_spanned ( ident, "unexpected field" ) ) ;
86
+ }
87
+
88
+ input. parse :: < syn:: Token ![ =] > ( ) ?;
89
+
90
+ let groups;
91
+ syn:: bracketed!( groups in input) ;
92
+
93
+ let groups =
94
+ syn:: punctuated:: Punctuated :: < syn:: LitStr , syn:: Token ![ , ] > :: parse_terminated ( & groups) ?;
95
+
96
+ Ok ( Self ( groups) )
97
+ }
98
+ }
99
+
100
+ fn extract_groups ( attrs : & [ syn:: Attribute ] ) -> Result < Vec < syn:: LitStr > , ParseError > {
101
+ let mut result = Vec :: new ( ) ;
102
+ for attr in attrs {
103
+ let groups = attr
104
+ . parse_args :: < Groups > ( )
105
+ . map_err ( ParseError :: InvalidGroups ) ?;
106
+ result. extend ( groups. 0 . into_iter ( ) ) ;
107
+ }
108
+ Ok ( result)
109
+ }
110
+
73
111
impl WorldOpts {
74
112
pub fn derive ( self ) -> TokenStream {
75
113
let query = Struct :: try_from ( self ) . unwrap_or_else ( |err| panic ! ( "{err}" ) ) ;
@@ -102,9 +140,24 @@ impl Struct {
102
140
let all_fields = self . fields . iter ( ) . map ( |field| & field. name ) ;
103
141
let query_all = generate_query ( quote ! { query_all } , all_fields) ;
104
142
143
+ let query_groups = {
144
+ let mut groups: HashMap < String , Vec < & syn:: Ident > > = HashMap :: new ( ) ;
145
+ for field in & self . fields {
146
+ for group in & field. groups {
147
+ groups. entry ( group. value ( ) ) . or_default ( ) . push ( & field. name ) ;
148
+ }
149
+ }
150
+ groups. into_iter ( ) . map ( |( group, fields) | {
151
+ let name =
152
+ syn:: Ident :: new ( & format ! ( "query_{}" , group) , proc_macro2:: Span :: call_site ( ) ) ;
153
+ generate_query ( name, fields)
154
+ } )
155
+ } ;
156
+
105
157
quote ! {
106
158
#default
107
159
#query_all
160
+ #( #query_groups) *
108
161
}
109
162
}
110
163
}
0 commit comments