@@ -3,10 +3,28 @@ use proc_macro2::{Ident, Span};
33use std:: collections:: HashMap ;
44use syn:: { Data , DeriveInput , Fields , LitStr } ;
55
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+
624pub ( crate ) struct SettingsBuilder {
725 ident : Ident ,
826 type_name : String ,
9- sort_key_prefix : Option < String > ,
27+ sort_key_prefix : SortKeyPrefix ,
1028 sort_key_field : Option < String > ,
1129 partition_key_field : Option < String > ,
1230 protected_attributes : Vec < String > ,
@@ -33,7 +51,7 @@ impl SettingsBuilder {
3351 Self {
3452 ident : input. ident . clone ( ) ,
3553 type_name,
36- sort_key_prefix : None ,
54+ sort_key_prefix : SortKeyPrefix :: Default ,
3755 sort_key_field : None ,
3856 partition_key_field : None ,
3957 protected_attributes : Vec :: new ( ) ,
@@ -54,9 +72,22 @@ impl SettingsBuilder {
5472 match ident. as_deref ( ) {
5573 Some ( "sort_key_prefix" ) => {
5674 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 ( ( ) )
6091 }
6192 Some ( "partition_key" ) => {
6293 let value = meta. value ( ) ?;
@@ -85,25 +116,89 @@ impl SettingsBuilder {
85116 . flat_map ( |x| x. ident . as_ref ( ) . map ( |x| x. to_string ( ) ) )
86117 . collect ( ) ;
87118
119+ let explicit_pk = all_field_names. contains ( & String :: from ( "pk" ) ) ;
120+ let explicit_sk = all_field_names. contains ( & String :: from ( "sk" ) ) ;
121+
88122 let mut compound_indexes: HashMap < String , Vec < ( String , String , Span ) > > =
89123 Default :: default ( ) ;
90124
91125 for field in & fields_named. named {
92126 let ident = & field. ident ;
93127 let mut attr_mode = AttributeMode :: Protected ;
94128
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+
95187 // Parse the meta for the field
96188 for attr in & field. attrs {
97189 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+ }
107202
108203 if let Some ( f) = & self . sort_key_field {
109204 return Err ( syn:: Error :: new_spanned (
@@ -112,19 +207,16 @@ impl SettingsBuilder {
112207 ) ) ;
113208 }
114209
115- self . sort_key_field = Some ( field_name) ;
210+ self . sort_key_field = Some ( field_name. clone ( ) ) ;
116211 }
117212
118213 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+ }
128220
129221 if let Some ( f) = & self . partition_key_field {
130222 return Err ( syn:: Error :: new_spanned (
@@ -133,7 +225,7 @@ impl SettingsBuilder {
133225 ) ) ;
134226 }
135227
136- self . partition_key_field = Some ( field_name) ;
228+ self . partition_key_field = Some ( field_name. clone ( ) ) ;
137229 }
138230
139231 if attr. path ( ) . is_ident ( "cryptonamo" ) {
@@ -255,7 +347,7 @@ impl SettingsBuilder {
255347 indexes,
256348 } = self ;
257349
258- let sort_key_prefix = sort_key_prefix. unwrap_or ( type_name) ;
350+ let sort_key_prefix = sort_key_prefix. into_prefix ( & type_name) ;
259351
260352 let partition_key = partition_key. ok_or_else ( || {
261353 syn:: Error :: new (
@@ -267,6 +359,7 @@ impl SettingsBuilder {
267359 Ok ( Settings {
268360 ident,
269361 sort_key_prefix,
362+ type_name,
270363 sort_key_field,
271364 partition_key_field : Some ( partition_key) , // TODO: Remove the Some
272365 protected_attributes,
@@ -277,7 +370,7 @@ impl SettingsBuilder {
277370 }
278371
279372 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) ;
281374 Ok ( ( ) )
282375 }
283376
0 commit comments