1use crate::Item;
52
53pub trait Key: Sized + serde::Serialize {
55 const DEFINITION: KeyDefinition;
57}
58
59pub trait IndexKeys: Sized {
61 const KEY_DEFINITIONS: &'static [SecondaryIndexDefinition];
63
64 type Serialize<'a>: serde::Serialize
66 where
67 Self: 'a;
68
69 fn to_serialize(&self) -> Self::Serialize<'_>;
71
72 fn into_key(self) -> Item {
74 crate::codec::to_item(self.to_serialize()).unwrap()
75 }
76}
77
78pub trait PrimaryKey: Sized + serde::Serialize {
80 const PRIMARY_KEY_DEFINITION: PrimaryKeyDefinition;
82
83 fn into_key(self) -> Item {
85 crate::codec::to_item(self).unwrap()
86 }
87}
88
89#[derive(Clone, Debug, serde::Serialize)]
91pub struct Primary {
92 #[serde(rename = "PK")]
94 pub hash: String,
95
96 #[serde(rename = "SK")]
98 pub range: String,
99}
100
101impl PrimaryKey for Primary {
102 const PRIMARY_KEY_DEFINITION: PrimaryKeyDefinition = PrimaryKeyDefinition {
103 hash_key: "PK",
104 range_key: Some("SK"),
105 };
106}
107
108impl Key for Primary {
109 const DEFINITION: KeyDefinition = KeyDefinition::Primary(Self::PRIMARY_KEY_DEFINITION);
110}
111
112pub trait IndexKey: Sized + serde::Serialize {
114 const INDEX_DEFINITION: SecondaryIndexDefinition;
116}
117
118impl<K: IndexKey> Key for K {
119 const DEFINITION: KeyDefinition = KeyDefinition::Secondary(K::INDEX_DEFINITION);
120}
121
122impl<K: IndexKey> IndexKey for Option<K> {
123 const INDEX_DEFINITION: SecondaryIndexDefinition = K::INDEX_DEFINITION;
124}
125
126#[derive(Clone, Debug, serde::Serialize)]
128pub struct FullKey<P, I>
129where
130 P: PrimaryKey,
131 I: IndexKeys,
132{
133 #[serde(flatten, serialize_with = "serialize_keys")]
135 pub indexes: I,
136
137 #[serde(flatten)]
139 pub primary: P,
140}
141
142impl<P, I> FullKey<P, I>
143where
144 P: PrimaryKey,
145 I: IndexKeys,
146{
147 pub fn into_key(self) -> Item {
149 crate::codec::to_item(self).unwrap()
150 }
151}
152
153impl<P> From<P> for FullKey<P, ()>
154where
155 P: PrimaryKey,
156{
157 #[inline]
158 fn from(primary: P) -> Self {
159 Self {
160 indexes: (),
161 primary,
162 }
163 }
164}
165
166fn serialize_keys<K, S>(keys: &K, serializer: S) -> Result<S::Ok, S::Error>
167where
168 K: IndexKeys,
169 S: serde::Serializer,
170{
171 serde::Serialize::serialize(&keys.to_serialize(), serializer)
172}
173
174macro_rules! gsi_key {
175 ($name:ident: $idx:literal, $pk:literal, $sk:literal) => {
176 #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, serde::Serialize)]
178 pub struct $name {
179 #[doc = "The partition key, with attribute name `"]
180 #[doc = $pk]
181 #[doc = "`"]
182 #[serde(rename = $pk)]
183 pub hash: String,
184
185 #[doc = "The sort key, with attribute name `"]
186 #[doc = $pk]
187 #[doc = "`"]
188 #[serde(rename = $sk)]
189 pub range: String,
190 }
191
192 impl IndexKey for $name {
193 const INDEX_DEFINITION: SecondaryIndexDefinition =
194 SecondaryIndexDefinition::Global(GlobalSecondaryIndexDefinition {
195 index_name: $idx,
196 hash_key: $pk,
197 range_key: Some($sk),
198 });
199 }
200 };
201}
202
203gsi_key!(Gsi1: "GSI1", "GSI1PK", "GSI1SK");
204gsi_key!(Gsi2: "GSI2", "GSI2PK", "GSI2SK");
205gsi_key!(Gsi3: "GSI3", "GSI3PK", "GSI3SK");
206gsi_key!(Gsi4: "GSI4", "GSI4PK", "GSI4SK");
207gsi_key!(Gsi5: "GSI5", "GSI5PK", "GSI5SK");
208gsi_key!(Gsi6: "GSI6", "GSI6PK", "GSI6SK");
209gsi_key!(Gsi7: "GSI7", "GSI7PK", "GSI7SK");
210gsi_key!(Gsi8: "GSI8", "GSI8PK", "GSI8SK");
211gsi_key!(Gsi9: "GSI9", "GSI9PK", "GSI9SK");
212gsi_key!(Gsi10: "GSI10", "GSI10PK", "GSI10SK");
213gsi_key!(Gsi11: "GSI11", "GSI11PK", "GSI11SK");
214gsi_key!(Gsi12: "GSI12", "GSI12PK", "GSI12SK");
215gsi_key!(Gsi13: "GSI13", "GSI13PK", "GSI13SK");
216gsi_key!(Gsi14: "GSI14", "GSI14PK", "GSI14SK");
217gsi_key!(Gsi15: "GSI15", "GSI15PK", "GSI15SK");
218gsi_key!(Gsi16: "GSI16", "GSI16PK", "GSI16SK");
219gsi_key!(Gsi17: "GSI17", "GSI17PK", "GSI17SK");
220gsi_key!(Gsi18: "GSI18", "GSI18PK", "GSI18SK");
221gsi_key!(Gsi19: "GSI19", "GSI19PK", "GSI19SK");
222gsi_key!(Gsi20: "GSI20", "GSI20PK", "GSI20SK");
223
224macro_rules! lsi_key {
225 ($name:ident: $idx:literal, $sk:literal) => {
226 #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, serde::Serialize)]
231 pub struct $name {
232 #[serde(rename = "PK")]
234 pub hash: String,
235
236 #[doc = "The sort key for the local secondary index, with attribute name `"]
237 #[doc = $sk]
238 #[doc = "`"]
239 #[serde(rename = $sk)]
240 pub range: String,
241 }
242
243 impl IndexKey for $name {
244 const INDEX_DEFINITION: SecondaryIndexDefinition =
245 SecondaryIndexDefinition::Local(LocalSecondaryIndexDefinition {
246 index_name: $idx,
247 hash_key: "PK",
248 range_key: $sk,
249 });
250 }
251 };
252}
253
254lsi_key!(Lsi1: "LSI1", "LSI1SK");
255lsi_key!(Lsi2: "LSI2", "LSI2SK");
256lsi_key!(Lsi3: "LSI3", "LSI3SK");
257lsi_key!(Lsi4: "LSI4", "LSI4SK");
258lsi_key!(Lsi5: "LSI5", "LSI5SK");
259
260macro_rules! impl_key_tuples {
261 ($i:ident; $($n:tt : $ty:ident),*$(,)?) => {
262 #[derive(Debug, serde::Serialize)]
264 #[allow(non_snake_case)]
265 pub struct $i<'a, $($ty),*> {
266 $(#[serde(flatten)] $ty: &'a $ty,)*
267 }
268
269 impl<$($ty: IndexKey),*> IndexKeys for ($($ty,)*)
270 where
271 $(
272 for<'a> $ty: 'a,
273 )*
274 {
275 const KEY_DEFINITIONS: &'static [$crate::keys::SecondaryIndexDefinition] = &[
276 $(
277 $ty::INDEX_DEFINITION,
278 )*
279 ];
280 type Serialize<'a> = $i<'a, $($ty),*>;
281 #[inline]
282 fn to_serialize(&self) -> Self::Serialize<'_> {
283 $i {
284 $($ty: &self.$n,)*
285 }
286 }
287 }
288 };
289}
290
291impl<T: IndexKey> IndexKeys for T {
292 const KEY_DEFINITIONS: &'static [SecondaryIndexDefinition] = &[T::INDEX_DEFINITION];
293 type Serialize<'a> = &'a T
294 where
295 T: 'a;
296 #[inline]
297 fn to_serialize(&self) -> Self::Serialize<'_> {
298 self
299 }
300}
301
302impl<K: Key> crate::ScanInput for K {
303 type Index = K;
304}
305
306mod hidden {
307 #[derive(Debug, serde::Serialize)]
308 pub struct Empty {}
309}
310
311impl IndexKeys for () {
312 const KEY_DEFINITIONS: &'static [SecondaryIndexDefinition] = &[];
313 type Serialize<'a> = hidden::Empty;
314 #[inline]
315 fn to_serialize(&self) -> Self::Serialize<'_> {
316 hidden::Empty {}
317 }
318}
319
320mod composite_keys {
321 use super::*;
322 impl_key_tuples! { CompositeK0; 0: K0 }
323 impl_key_tuples! { CompositeK1; 0: K0, 1: K1 }
324 impl_key_tuples! { CompositeK2; 0: K0, 1: K1, 2: K2 }
325 impl_key_tuples! { CompositeK3; 0: K0, 1: K1, 2: K2, 3: K3 }
326 impl_key_tuples! { CompositeK4; 0: K0, 1: K1, 2: K2, 3: K3, 4: K4 }
327 impl_key_tuples! { CompositeK5; 0: K0, 1: K1, 2: K2, 3: K3, 4: K4, 5: K5 }
328 impl_key_tuples! { CompositeK6; 0: K0, 1: K1, 2: K2, 3: K3, 4: K4, 5: K5, 6: K6 }
329 impl_key_tuples! { CompositeK7; 0: K0, 1: K1, 2: K2, 3: K3, 4: K4, 5: K5, 6: K6, 7: K7 }
330 impl_key_tuples! { CompositeK8; 0: K0, 1: K1, 2: K2, 3: K3, 4: K4, 5: K5, 6: K6, 7: K7, 8: K8 }
331 impl_key_tuples! { CompositeK9; 0: K0, 1: K1, 2: K2, 3: K3, 4: K4, 5: K5, 6: K6, 7: K7, 8: K8, 9: K9 }
332 impl_key_tuples! { CompositeK10; 0: K0, 1: K1, 2: K2, 3: K3, 4: K4, 5: K5, 6: K6, 7: K7, 8: K8, 9: K9, 10: K10 }
333 impl_key_tuples! { CompositeK11; 0: K0, 1: K1, 2: K2, 3: K3, 4: K4, 5: K5, 6: K6, 7: K7, 8: K8, 9: K9, 10: K10, 11: K11 }
334 impl_key_tuples! { CompositeK12; 0: K0, 1: K1, 2: K2, 3: K3, 4: K4, 5: K5, 6: K6, 7: K7, 8: K8, 9: K9, 10: K10, 11: K11, 12: K12 }
335}
336
337#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
339pub enum KeyDefinition {
340 Primary(PrimaryKeyDefinition),
342
343 Secondary(SecondaryIndexDefinition),
345}
346
347impl KeyDefinition {
348 #[inline]
350 pub const fn index_name(&self) -> Option<&'static str> {
351 match self {
352 Self::Primary(_) => None,
353 Self::Secondary(def) => Some(def.index_name()),
354 }
355 }
356
357 #[inline]
359 pub const fn hash_key(&self) -> &'static str {
360 match self {
361 Self::Primary(def) => def.hash_key,
362 Self::Secondary(def) => def.hash_key(),
363 }
364 }
365
366 #[inline]
368 pub const fn range_key(&self) -> Option<&'static str> {
369 match self {
370 Self::Primary(def) => def.range_key,
371 Self::Secondary(def) => def.range_key(),
372 }
373 }
374}
375
376impl From<PrimaryKeyDefinition> for KeyDefinition {
377 #[inline]
378 fn from(def: PrimaryKeyDefinition) -> Self {
379 Self::Primary(def)
380 }
381}
382
383impl From<SecondaryIndexDefinition> for KeyDefinition {
384 #[inline]
385 fn from(def: SecondaryIndexDefinition) -> Self {
386 Self::Secondary(def)
387 }
388}
389
390#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
392pub struct PrimaryKeyDefinition {
393 pub hash_key: &'static str,
395
396 pub range_key: Option<&'static str>,
398}
399
400impl PrimaryKeyDefinition {
401 #[inline]
403 pub const fn into_key_definition(self) -> KeyDefinition {
404 KeyDefinition::Primary(self)
405 }
406}
407
408#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
410pub enum SecondaryIndexDefinition {
411 Global(GlobalSecondaryIndexDefinition),
413
414 Local(LocalSecondaryIndexDefinition),
416}
417
418impl SecondaryIndexDefinition {
419 #[inline]
421 pub const fn index_name(&self) -> &'static str {
422 match self {
423 Self::Global(def) => def.index_name,
424 Self::Local(def) => def.index_name,
425 }
426 }
427
428 #[inline]
430 pub const fn hash_key(&self) -> &'static str {
431 match self {
432 Self::Global(def) => def.hash_key,
433 Self::Local(def) => def.hash_key,
434 }
435 }
436
437 #[inline]
439 pub const fn range_key(&self) -> Option<&'static str> {
440 match self {
441 Self::Global(def) => def.range_key,
442 Self::Local(def) => Some(def.range_key),
443 }
444 }
445
446 #[inline]
448 pub const fn into_key_definition(self) -> KeyDefinition {
449 KeyDefinition::Secondary(self)
450 }
451}
452
453#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
455pub struct GlobalSecondaryIndexDefinition {
456 pub index_name: &'static str,
458
459 pub hash_key: &'static str,
461
462 pub range_key: Option<&'static str>,
464}
465
466impl GlobalSecondaryIndexDefinition {
468 #[inline]
470 pub const fn into_index(self) -> SecondaryIndexDefinition {
471 SecondaryIndexDefinition::Global(self)
472 }
473}
474
475#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
477pub struct LocalSecondaryIndexDefinition {
478 pub index_name: &'static str,
480
481 pub hash_key: &'static str,
485
486 pub range_key: &'static str,
488}
489
490impl LocalSecondaryIndexDefinition {
492 #[inline]
494 pub const fn into_index(self) -> SecondaryIndexDefinition {
495 SecondaryIndexDefinition::Local(self)
496 }
497}
498
499#[cfg(test)]
500mod tests {
501 use aws_sdk_dynamodb::types::AttributeValue;
502
503 use super::*;
504
505 #[test]
506 fn test_primary_key() {
507 let key = Primary {
508 hash: "hash".to_string(),
509 range: "range".to_string(),
510 };
511 let serialized = key.into_key();
512 assert_eq!(serialized["PK"], AttributeValue::S("hash".to_string()));
513 assert_eq!(serialized["SK"], AttributeValue::S("range".to_string()));
514 }
515
516 #[test]
517 fn test_gsi_key() {
518 let key = Gsi1 {
519 hash: "hash".to_string(),
520 range: "range".to_string(),
521 };
522 let serialized = key.into_key();
523 assert_eq!(serialized["GSI1PK"], AttributeValue::S("hash".to_string()));
524 assert_eq!(serialized["GSI1SK"], AttributeValue::S("range".to_string()));
525 }
526
527 #[test]
528 fn test_lsi_key() {
529 let key = Lsi1 {
530 hash: "primary_key".to_string(),
531 range: "range".to_string(),
532 };
533 let serialized = key.into_key();
534 assert_eq!(
535 serialized["PK"],
536 AttributeValue::S("primary_key".to_string())
537 );
538 assert_eq!(serialized["LSI1SK"], AttributeValue::S("range".to_string()));
539 }
540
541 #[test]
542 fn test_composite_key() {
543 let primary = Primary {
544 hash: "PK".to_string(),
545 range: "SK".to_string(),
546 };
547
548 let gsi5 = Gsi5 {
549 hash: "GSI5PK".to_string(),
550 range: "GSI5SK".to_string(),
551 };
552
553 let lsi3 = Lsi3 {
554 hash: "LSI3PK".to_string(),
558 range: "LSI3SK".to_string(),
559 };
560
561 let serialized = FullKey {
562 primary,
563 indexes: (gsi5, lsi3),
564 }
565 .into_key();
566 assert_eq!(serialized["PK"], AttributeValue::S("PK".to_string()));
567 assert_eq!(serialized["SK"], AttributeValue::S("SK".to_string()));
568 assert_eq!(
569 serialized["GSI5PK"],
570 AttributeValue::S("GSI5PK".to_string())
571 );
572 assert_eq!(
573 serialized["GSI5SK"],
574 AttributeValue::S("GSI5SK".to_string())
575 );
576 assert_eq!(
577 serialized["LSI3SK"],
578 AttributeValue::S("LSI3SK".to_string())
579 );
580 }
581}