@@ -3,10 +3,28 @@ use proc_macro2::{Ident, Span};
3
3
use std:: collections:: HashMap ;
4
4
use syn:: { Data , DeriveInput , Fields , LitStr } ;
5
5
6
+ enum SortKeyPrefix {
7
+ Default ,
8
+ Value ( String ) ,
9
+ None ,
10
+ }
11
+
12
+ impl SortKeyPrefix {
13
+ fn into_prefix ( self , default : & str ) -> Option < String > {
14
+ match self {
15
+ Self :: Default => Some ( default. to_string ( ) ) ,
16
+ Self :: Value ( v) => Some ( v) ,
17
+ Self :: None => None ,
18
+ }
19
+ }
20
+ }
21
+
22
+ const RESERVED_FIELD_NAMES : & ' static [ & ' static str ] = & [ "term" ] ;
23
+
6
24
pub ( crate ) struct SettingsBuilder {
7
25
ident : Ident ,
8
26
type_name : String ,
9
- sort_key_prefix : Option < String > ,
27
+ sort_key_prefix : SortKeyPrefix ,
10
28
sort_key_field : Option < String > ,
11
29
partition_key_field : Option < String > ,
12
30
protected_attributes : Vec < String > ,
@@ -33,7 +51,7 @@ impl SettingsBuilder {
33
51
Self {
34
52
ident : input. ident . clone ( ) ,
35
53
type_name,
36
- sort_key_prefix : None ,
54
+ sort_key_prefix : SortKeyPrefix :: Default ,
37
55
sort_key_field : None ,
38
56
partition_key_field : None ,
39
57
protected_attributes : Vec :: new ( ) ,
@@ -54,9 +72,22 @@ impl SettingsBuilder {
54
72
match ident. as_deref ( ) {
55
73
Some ( "sort_key_prefix" ) => {
56
74
let value = meta. value ( ) ?;
57
- let t: LitStr = value. parse ( ) ?;
58
- let v = t. value ( ) . to_string ( ) ;
59
- self . set_sort_key_prefix ( v)
75
+
76
+ if let Ok ( t) = value. parse :: < LitStr > ( ) {
77
+ let v = t. value ( ) . to_string ( ) ;
78
+ self . set_sort_key_prefix ( v) ?;
79
+ return Ok ( ( ) ) ;
80
+ }
81
+
82
+ if let Ok ( t) = value. parse :: < Ident > ( ) {
83
+ let v = t. to_string ( ) ;
84
+
85
+ if v == "None" {
86
+ self . sort_key_prefix = SortKeyPrefix :: None ;
87
+ }
88
+ }
89
+
90
+ Ok ( ( ) )
60
91
}
61
92
Some ( "partition_key" ) => {
62
93
let value = meta. value ( ) ?;
@@ -85,25 +116,89 @@ impl SettingsBuilder {
85
116
. flat_map ( |x| x. ident . as_ref ( ) . map ( |x| x. to_string ( ) ) )
86
117
. collect ( ) ;
87
118
119
+ let explicit_pk = all_field_names. contains ( & String :: from ( "pk" ) ) ;
120
+ let explicit_sk = all_field_names. contains ( & String :: from ( "sk" ) ) ;
121
+
88
122
let mut compound_indexes: HashMap < String , Vec < ( String , String , Span ) > > =
89
123
Default :: default ( ) ;
90
124
91
125
for field in & fields_named. named {
92
126
let ident = & field. ident ;
93
127
let mut attr_mode = AttributeMode :: Protected ;
94
128
129
+ let field_name = ident
130
+ . as_ref ( )
131
+ . ok_or_else ( || {
132
+ syn:: Error :: new_spanned (
133
+ field,
134
+ "internal error: identifier was not Some" ,
135
+ )
136
+ } ) ?
137
+ . to_string ( ) ;
138
+
139
+ if field_name. starts_with ( "__" ) {
140
+ return Err ( syn:: Error :: new_spanned (
141
+ field,
142
+ format ! (
143
+ "Invalid field '{field_name}': fields must not be prefixed with __"
144
+ ) ,
145
+ ) ) ;
146
+ }
147
+
148
+ if RESERVED_FIELD_NAMES . contains ( & field_name. as_str ( ) ) {
149
+ return Err ( syn:: Error :: new_spanned (
150
+ field,
151
+ format ! (
152
+ "Invalid field '{field_name}': name is reserved for internal use"
153
+ ) ,
154
+ ) ) ;
155
+ }
156
+
157
+ if field_name == "pk" {
158
+ let has_partition_key_attr = field
159
+ . attrs
160
+ . iter ( )
161
+ . find ( |x| x. path ( ) . is_ident ( "partition_key" ) )
162
+ . is_some ( ) ;
163
+
164
+ if !has_partition_key_attr {
165
+ return Err ( syn:: Error :: new_spanned (
166
+ field,
167
+ format ! ( "field named 'pk' must be annotated with #[partition_key]" ) ,
168
+ ) ) ;
169
+ }
170
+ }
171
+
172
+ if field_name == "sk" {
173
+ let has_partition_key_attr = field
174
+ . attrs
175
+ . iter ( )
176
+ . find ( |x| x. path ( ) . is_ident ( "sort_key" ) )
177
+ . is_some ( ) ;
178
+
179
+ if !has_partition_key_attr {
180
+ return Err ( syn:: Error :: new_spanned (
181
+ field,
182
+ format ! ( "field named 'sk' must be annotated with #[sort_key]" ) ,
183
+ ) ) ;
184
+ }
185
+ }
186
+
95
187
// Parse the meta for the field
96
188
for attr in & field. attrs {
97
189
if attr. path ( ) . is_ident ( "sort_key" ) {
98
- let field_name = ident
99
- . as_ref ( )
100
- . ok_or_else ( || {
101
- syn:: Error :: new_spanned (
102
- field,
103
- "internal error: identifier was not Some" ,
104
- )
105
- } ) ?
106
- . to_string ( ) ;
190
+ if explicit_sk && field_name != "sk" {
191
+ return Err ( syn:: Error :: new_spanned (
192
+ field,
193
+ format ! ( "field '{field_name}' cannot be used as sort key as struct contains field named 'sk' which must be used" )
194
+ ) ) ;
195
+ }
196
+
197
+ if explicit_sk {
198
+ // if the 'sk' field is set then there should be no prefix
199
+ // otherwise when deserialising the sk value would be incorrect
200
+ self . sort_key_prefix = SortKeyPrefix :: None ;
201
+ }
107
202
108
203
if let Some ( f) = & self . sort_key_field {
109
204
return Err ( syn:: Error :: new_spanned (
@@ -112,19 +207,16 @@ impl SettingsBuilder {
112
207
) ) ;
113
208
}
114
209
115
- self . sort_key_field = Some ( field_name) ;
210
+ self . sort_key_field = Some ( field_name. clone ( ) ) ;
116
211
}
117
212
118
213
if attr. path ( ) . is_ident ( "partition_key" ) {
119
- let field_name = ident
120
- . as_ref ( )
121
- . ok_or_else ( || {
122
- syn:: Error :: new_spanned (
123
- field,
124
- "internal error: identifier was not Some" ,
125
- )
126
- } ) ?
127
- . to_string ( ) ;
214
+ if explicit_pk && field_name != "pk" {
215
+ return Err ( syn:: Error :: new_spanned (
216
+ field,
217
+ format ! ( "field '{field_name}' cannot be used as partition key as struct contains field named 'pk' which must be used" )
218
+ ) ) ;
219
+ }
128
220
129
221
if let Some ( f) = & self . partition_key_field {
130
222
return Err ( syn:: Error :: new_spanned (
@@ -133,7 +225,7 @@ impl SettingsBuilder {
133
225
) ) ;
134
226
}
135
227
136
- self . partition_key_field = Some ( field_name) ;
228
+ self . partition_key_field = Some ( field_name. clone ( ) ) ;
137
229
}
138
230
139
231
if attr. path ( ) . is_ident ( "cryptonamo" ) {
@@ -255,7 +347,7 @@ impl SettingsBuilder {
255
347
indexes,
256
348
} = self ;
257
349
258
- let sort_key_prefix = sort_key_prefix. unwrap_or ( type_name) ;
350
+ let sort_key_prefix = sort_key_prefix. into_prefix ( & type_name) ;
259
351
260
352
let partition_key = partition_key. ok_or_else ( || {
261
353
syn:: Error :: new (
@@ -267,6 +359,7 @@ impl SettingsBuilder {
267
359
Ok ( Settings {
268
360
ident,
269
361
sort_key_prefix,
362
+ type_name,
270
363
sort_key_field,
271
364
partition_key_field : Some ( partition_key) , // TODO: Remove the Some
272
365
protected_attributes,
@@ -277,7 +370,7 @@ impl SettingsBuilder {
277
370
}
278
371
279
372
pub ( crate ) fn set_sort_key_prefix ( & mut self , value : String ) -> Result < ( ) , syn:: Error > {
280
- self . sort_key_prefix = Some ( value) ;
373
+ self . sort_key_prefix = SortKeyPrefix :: Value ( value) ;
281
374
Ok ( ( ) )
282
375
}
283
376
0 commit comments