1
- use std:: collections:: HashSet ;
2
-
3
1
use convert_case:: { Case , Casing } ;
4
- use proc_macro2:: TokenTree ;
2
+ use proc_macro2:: Ident ;
5
3
use quote:: quote;
4
+ use structmeta:: Flag ;
5
+ use structmeta:: StructMeta ;
6
6
use syn:: parse_macro_input;
7
7
use syn:: spanned:: Spanned ;
8
8
9
+ #[ derive( Clone , StructMeta , Default ) ]
10
+ struct FieldAttrs {
11
+ primary_key : Flag ,
12
+ displayed : Flag ,
13
+ searchable : Flag ,
14
+ distinct : Flag ,
15
+ filterable : Flag ,
16
+ sortable : Flag ,
17
+ }
18
+
9
19
#[ proc_macro_derive( IndexConfig , attributes( index_config) ) ]
10
20
pub fn generate_index_settings ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
11
21
let ast = parse_macro_input ! ( input as syn:: DeriveInput ) ;
@@ -28,63 +38,72 @@ pub fn generate_index_settings(input: proc_macro::TokenStream) -> proc_macro::To
28
38
}
29
39
30
40
fn get_index_config_implementation (
31
- struct_ident : & syn :: Ident ,
41
+ struct_ident : & Ident ,
32
42
fields : & syn:: Fields ,
33
43
) -> proc_macro2:: TokenStream {
34
- let mut attribute_set: std:: collections:: HashSet < String > = std:: collections:: HashSet :: new ( ) ;
35
44
let mut primary_key_attribute: String = "" . to_string ( ) ;
36
45
let mut distinct_key_attribute: String = "" . to_string ( ) ;
37
46
let mut displayed_attributes: Vec < String > = vec ! [ ] ;
38
47
let mut searchable_attributes: Vec < String > = vec ! [ ] ;
39
48
let mut filterable_attributes: Vec < String > = vec ! [ ] ;
40
49
let mut sortable_attributes: Vec < String > = vec ! [ ] ;
41
- let valid_attribute_names = std:: collections:: HashSet :: from ( [
42
- "displayed" ,
43
- "searchable" ,
44
- "filterable" ,
45
- "sortable" ,
46
- "primary_key" ,
47
- "distinct" ,
48
- ] ) ;
49
50
50
51
let index_name = struct_ident
51
52
. to_string ( )
52
53
. from_case ( Case :: UpperCamel )
53
54
. to_case ( Case :: Snake ) ;
54
55
55
- for field in fields {
56
- let attribute_list_result =
57
- extract_all_attr_values ( & field. attrs , & mut attribute_set, & valid_attribute_names) ;
56
+ let mut primary_key_found = false ;
57
+ let mut distinct_found = false ;
58
58
59
- match attribute_list_result {
60
- Ok ( attribute_list) => {
61
- for attribute in attribute_list {
62
- match attribute. as_str ( ) {
63
- "displayed" => {
64
- displayed_attributes. push ( field. ident . clone ( ) . unwrap ( ) . to_string ( ) )
65
- }
66
- "searchable" => {
67
- searchable_attributes. push ( field. ident . clone ( ) . unwrap ( ) . to_string ( ) )
68
- }
69
- "filterable" => {
70
- filterable_attributes. push ( field. ident . clone ( ) . unwrap ( ) . to_string ( ) )
71
- }
72
- "sortable" => {
73
- sortable_attributes. push ( field. ident . clone ( ) . unwrap ( ) . to_string ( ) )
74
- }
75
- "primary_key" => {
76
- primary_key_attribute = field. ident . clone ( ) . unwrap ( ) . to_string ( )
77
- }
78
- "distinct" => {
79
- distinct_key_attribute = field. ident . clone ( ) . unwrap ( ) . to_string ( )
80
- }
81
- _ => { }
82
- }
83
- }
59
+ for field in fields {
60
+ let attrs = field
61
+ . attrs
62
+ . iter ( )
63
+ . filter ( |attr| attr. path ( ) . is_ident ( "index_config" ) )
64
+ . map ( |attr| attr. parse_args :: < FieldAttrs > ( ) . unwrap ( ) )
65
+ . collect :: < Vec < _ > > ( )
66
+ . first ( )
67
+ . cloned ( )
68
+ . unwrap_or_default ( ) ;
69
+
70
+ // Check if the primary key field is unique
71
+ if attrs. primary_key . value ( ) {
72
+ if primary_key_found {
73
+ return syn:: Error :: new (
74
+ field. span ( ) ,
75
+ "Only one field can be marked as primary key" ,
76
+ )
77
+ . to_compile_error ( ) ;
84
78
}
85
- Err ( e) => {
86
- return e;
79
+ primary_key_attribute = field. ident . clone ( ) . unwrap ( ) . to_string ( ) ;
80
+ primary_key_found = true ;
81
+ }
82
+
83
+ // Check if the distinct field is unique
84
+ if attrs. distinct . value ( ) {
85
+ if distinct_found {
86
+ return syn:: Error :: new ( field. span ( ) , "Only one field can be marked as distinct" )
87
+ . to_compile_error ( ) ;
87
88
}
89
+ distinct_key_attribute = field. ident . clone ( ) . unwrap ( ) . to_string ( ) ;
90
+ distinct_found = true ;
91
+ }
92
+
93
+ if attrs. displayed . value ( ) {
94
+ displayed_attributes. push ( field. ident . clone ( ) . unwrap ( ) . to_string ( ) ) ;
95
+ }
96
+
97
+ if attrs. searchable . value ( ) {
98
+ searchable_attributes. push ( field. ident . clone ( ) . unwrap ( ) . to_string ( ) ) ;
99
+ }
100
+
101
+ if attrs. filterable . value ( ) {
102
+ filterable_attributes. push ( field. ident . clone ( ) . unwrap ( ) . to_string ( ) ) ;
103
+ }
104
+
105
+ if attrs. sortable . value ( ) {
106
+ sortable_attributes. push ( field. ident . clone ( ) . unwrap ( ) . to_string ( ) ) ;
88
107
}
89
108
}
90
109
@@ -123,122 +142,17 @@ fn get_index_config_implementation(
123
142
#distinct_attr_token
124
143
}
125
144
126
- async fn generate_index( client: & :: meilisearch_sdk:: client:: Client ) -> std :: result :: Result <:: meilisearch_sdk:: indexes:: Index , :: meilisearch_sdk:: tasks:: Task > {
145
+ async fn generate_index( client: & :: meilisearch_sdk:: client:: Client ) -> Result <:: meilisearch_sdk:: indexes:: Index , :: meilisearch_sdk:: tasks:: Task > {
127
146
return client. create_index( #index_name, #primary_key_token)
128
147
. await . unwrap( )
129
- . wait_for_completion( & client, :: std :: option :: Option :: None , :: std :: option :: Option :: None )
148
+ . wait_for_completion( & client, None , None )
130
149
. await . unwrap( )
131
150
. try_make_index( & client) ;
132
151
}
133
152
}
134
153
}
135
154
}
136
155
137
- fn extract_all_attr_values (
138
- attrs : & [ syn:: Attribute ] ,
139
- attribute_set : & mut std:: collections:: HashSet < String > ,
140
- valid_attribute_names : & std:: collections:: HashSet < & str > ,
141
- ) -> std:: result:: Result < Vec < String > , proc_macro2:: TokenStream > {
142
- let mut attribute_names: Vec < String > = vec ! [ ] ;
143
- let mut local_attribute_set: std:: collections:: HashSet < String > = HashSet :: new ( ) ;
144
- for attr in attrs {
145
- match attr. parse_meta ( ) {
146
- std:: result:: Result :: Ok ( syn:: Meta :: List ( list) ) => {
147
- if !list. path . is_ident ( "index_config" ) {
148
- continue ;
149
- }
150
- for token_stream in attr. tokens . clone ( ) . into_iter ( ) {
151
- if let TokenTree :: Group ( group) = token_stream {
152
- for token in group. stream ( ) {
153
- match token {
154
- TokenTree :: Punct ( punct) => validate_punct ( & punct) ?,
155
- TokenTree :: Ident ( ident) => {
156
- if ident == "primary_key"
157
- && attribute_set. contains ( "primary_key" )
158
- {
159
- return std:: result:: Result :: Err (
160
- syn:: Error :: new (
161
- ident. span ( ) ,
162
- "`primary_key` already exists" ,
163
- )
164
- . to_compile_error ( ) ,
165
- ) ;
166
- }
167
- if ident == "distinct" && attribute_set. contains ( "distinct" ) {
168
- return std:: result:: Result :: Err (
169
- syn:: Error :: new (
170
- ident. span ( ) ,
171
- "`distinct` already exists" ,
172
- )
173
- . to_compile_error ( ) ,
174
- ) ;
175
- }
176
-
177
- if local_attribute_set. contains ( ident. to_string ( ) . as_str ( ) ) {
178
- return std:: result:: Result :: Err (
179
- syn:: Error :: new (
180
- ident. span ( ) ,
181
- format ! ( "`{ident}` already exists for this field" ) ,
182
- )
183
- . to_compile_error ( ) ,
184
- ) ;
185
- }
186
-
187
- if !valid_attribute_names. contains ( ident. to_string ( ) . as_str ( ) ) {
188
- return std:: result:: Result :: Err (
189
- syn:: Error :: new (
190
- ident. span ( ) ,
191
- format ! (
192
- "Property `{ident}` does not exist for type `index_config`"
193
- ) ,
194
- )
195
- . to_compile_error ( ) ,
196
- ) ;
197
- }
198
- attribute_names. push ( ident. to_string ( ) ) ;
199
- attribute_set. insert ( ident. to_string ( ) ) ;
200
- local_attribute_set. insert ( ident. to_string ( ) ) ;
201
- }
202
- _ => {
203
- return std:: result:: Result :: Err (
204
- syn:: Error :: new ( attr. span ( ) , "Invalid parsing" . to_string ( ) )
205
- . to_compile_error ( ) ,
206
- ) ;
207
- }
208
- }
209
- }
210
- }
211
- }
212
- }
213
- std:: result:: Result :: Err ( e) => {
214
- for token_stream in attr. tokens . clone ( ) . into_iter ( ) {
215
- if let TokenTree :: Group ( group) = token_stream {
216
- for token in group. stream ( ) {
217
- if let TokenTree :: Punct ( punct) = token {
218
- validate_punct ( & punct) ?
219
- }
220
- }
221
- }
222
- }
223
- return std:: result:: Result :: Err (
224
- syn:: Error :: new ( attr. span ( ) , e. to_string ( ) ) . to_compile_error ( ) ,
225
- ) ;
226
- }
227
- _ => { }
228
- }
229
- }
230
- std:: result:: Result :: Ok ( attribute_names)
231
- }
232
-
233
- fn validate_punct ( punct : & proc_macro2:: Punct ) -> std:: result:: Result < ( ) , proc_macro2:: TokenStream > {
234
- if punct. as_char ( ) == ',' && punct. spacing ( ) == proc_macro2:: Spacing :: Alone {
235
- return std:: result:: Result :: Ok ( ( ) ) ;
236
- }
237
- std:: result:: Result :: Err (
238
- syn:: Error :: new ( punct. span ( ) , "`,` expected" . to_string ( ) ) . to_compile_error ( ) ,
239
- )
240
- }
241
-
242
156
fn get_settings_token_for_list (
243
157
field_name_list : & Vec < String > ,
244
158
method_name : & str ,
0 commit comments