Skip to content

Commit 935947b

Browse files
author
Bennett Hardwick
committed
Support sk field
1 parent 00e41c3 commit 935947b

File tree

6 files changed

+55
-39
lines changed

6 files changed

+55
-39
lines changed

cryptonamo-derive/src/settings/builder.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ impl SortKeyPrefix {
1919
}
2020
}
2121

22-
const RESERVED_FIELD_NAMES: &'static [&'static str] = &["term", "sk"];
22+
const RESERVED_FIELD_NAMES: &'static [&'static str] = &["term"];
2323

2424
pub(crate) struct SettingsBuilder {
2525
ident: Ident,
@@ -117,7 +117,7 @@ impl SettingsBuilder {
117117
.collect();
118118

119119
let explicit_pk = all_field_names.contains(&String::from("pk"));
120-
// let explicit_sk = all_field_names.contains(&String::from("sk"));
120+
let explicit_sk = all_field_names.contains(&String::from("sk"));
121121

122122
let mut compound_indexes: HashMap<String, Vec<(String, String, Span)>> =
123123
Default::default();
@@ -172,6 +172,19 @@ impl SettingsBuilder {
172172
// Parse the meta for the field
173173
for attr in &field.attrs {
174174
if attr.path().is_ident("sort_key") {
175+
if explicit_sk && field_name != "sk" {
176+
return Err(syn::Error::new_spanned(
177+
field,
178+
format!("field '{field_name}' cannot be used as sort key as struct contains field named 'sk' which must be used")
179+
));
180+
}
181+
182+
if explicit_sk {
183+
// if the 'sk' field is set then there should be no prefix
184+
// otherwise when deserialising the sk value would be incorrect
185+
self.sort_key_prefix = SortKeyPrefix::None;
186+
}
187+
175188
if let Some(f) = &self.sort_key_field {
176189
return Err(syn::Error::new_spanned(
177190
field,
@@ -182,7 +195,7 @@ impl SettingsBuilder {
182195
self.sort_key_field = Some(field_name.clone());
183196
}
184197

185-
if attr.path().is_ident("partition_key"){
198+
if attr.path().is_ident("partition_key") {
186199
if explicit_pk && field_name != "pk" {
187200
return Err(syn::Error::new_spanned(
188201
field,

src/crypto/sealed.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ impl Sealed {
4040
let ciphertexts = T::decryptable_attributes()
4141
.into_iter()
4242
.map(|name| {
43-
let attribute = if name == "pk" {
44-
self.inner().attributes.get("__pk")
45-
} else {
46-
self.inner().attributes.get(name)
47-
};
43+
let attribute = self.inner().attributes.get(match name {
44+
"pk" => "__pk",
45+
"sk" => "__sk",
46+
_ => name,
47+
});
4848

4949
attribute
5050
.ok_or_else(|| SealError::MissingAttribute(name.to_string()))?

src/crypto/sealer.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,14 @@ impl<T> Sealer<T> {
9090
.zip(T::protected_attributes().into_iter())
9191
.for_each(|(enc, name)| {
9292
if let Some(e) = enc {
93-
if name == "pk" {
94-
table_entry.add_attribute("__pk", e.into());
95-
} else {
96-
table_entry.add_attribute(name, e.into());
97-
}
93+
table_entry.add_attribute(
94+
match name {
95+
"pk" => "__pk",
96+
"sk" => "__sk",
97+
_ => name,
98+
},
99+
e.into(),
100+
);
98101
}
99102
});
100103

src/traits/primary_key.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::Encryptable;
22

33
pub struct PrimaryKeyParts {
44
pub(crate) pk: String,
5-
pub(crate) sk: String
5+
pub(crate) sk: String,
66
}
77

88
pub trait PrimaryKey: private::Sealed {
@@ -17,20 +17,20 @@ impl Pk {
1717
}
1818
}
1919

20-
impl From<String> for Pk {
21-
fn from(value: String) -> Self {
22-
Self(value)
23-
}
24-
}
25-
26-
impl From<&str> for Pk {
27-
fn from(value: &str) -> Self {
20+
impl<P: Into<String>> From<P> for Pk {
21+
fn from(value: P) -> Self {
2822
Self::new(value)
2923
}
3024
}
3125

3226
pub struct PkSk(String, String);
3327

28+
impl<Pk: Into<String>, Sk: Into<String>> From<(Pk, Sk)> for PkSk {
29+
fn from(value: (Pk, Sk)) -> Self {
30+
Self::new(value.0, value.1)
31+
}
32+
}
33+
3434
impl PkSk {
3535
pub fn new(pk: impl Into<String>, sk: impl Into<String>) -> Self {
3636
Self(pk.into(), sk.into())

tests/pk_tests.rs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,27 @@ use std::future::Future;
77
mod common;
88

99
#[derive(Encryptable, Decryptable, Searchable, Debug, PartialEq, Ord, PartialOrd, Eq)]
10-
#[cryptonamo(sort_key_prefix = "user")]
1110
pub struct User {
12-
#[cryptonamo(query = "exact", compound = "pk#name")]
11+
#[cryptonamo(query = "exact", compound = "pk#sk")]
1312
#[cryptonamo(query = "exact")]
1413
#[partition_key]
1514
pub pk: String,
1615

17-
#[cryptonamo(query = "prefix", compound = "pk#name")]
16+
#[cryptonamo(query = "prefix", compound = "pk#sk")]
1817
#[cryptonamo(query = "prefix")]
19-
pub name: String,
18+
#[sort_key]
19+
pub sk: String,
2020

2121
#[cryptonamo(plaintext)]
2222
pub tag: String,
23-
24-
#[cryptonamo(skip)]
25-
pub temp: bool,
2623
}
2724

2825
impl User {
2926
pub fn new(email: impl Into<String>, name: impl Into<String>, tag: impl Into<String>) -> Self {
3027
Self {
31-
name: name.into(),
3228
pk: email.into(),
29+
sk: name.into(),
3330
tag: tag.into(),
34-
temp: false,
3531
}
3632
}
3733
}
@@ -44,7 +40,7 @@ async fn run_test<F: Future<Output = ()>>(mut f: impl FnMut(EncryptedTable) -> F
4440

4541
let client = aws_sdk_dynamodb::Client::new(&config);
4642

47-
let table_name = "test-users";
43+
let table_name = "pk-sk-users";
4844

4945
common::create_table(&client, table_name).await;
5046

@@ -95,7 +91,7 @@ async fn test_query_single_prefix() {
9591
run_test(|table| async move {
9692
let res: Vec<User> = table
9793
.query()
98-
.starts_with("name", "Dan")
94+
.starts_with("sk", "Dan")
9995
.send()
10096
.await
10197
.expect("Failed to query")
@@ -120,7 +116,7 @@ async fn test_query_compound() {
120116
run_test(|table| async move {
121117
let res: Vec<User> = table
122118
.query()
123-
.starts_with("name", "Dan")
119+
.starts_with("sk", "Dan")
124120
.eq("pk", "[email protected]")
125121
.send()
126122
.await
@@ -138,7 +134,10 @@ async fn test_query_compound() {
138134
#[serial]
139135
async fn test_get_by_partition_key() {
140136
run_test(|table| async move {
141-
let res: Option<User> = table.get("[email protected]").await.expect("Failed to send");
137+
let res: Option<User> = table
138+
.get(("[email protected]", "Dan Draper"))
139+
.await
140+
.expect("Failed to send");
142141
assert_eq!(
143142
res,
144143
Some(User::new("[email protected]", "Dan Draper", "blue"))
@@ -152,19 +151,19 @@ async fn test_get_by_partition_key() {
152151
async fn test_delete() {
153152
run_test(|table| async move {
154153
table
155-
.delete::<User>("[email protected]")
154+
.delete::<User>(("[email protected]", "Dan Draper"))
156155
.await
157156
.expect("Failed to send");
158157

159158
let res = table
160-
.get::<User>("[email protected]")
159+
.get::<User>(("[email protected]", "Dan Draper"))
161160
.await
162161
.expect("Failed to send");
163162
assert_eq!(res, None);
164163

165164
let res = table
166165
.query::<User>()
167-
.starts_with("name", "Dan")
166+
.starts_with("sk", "Dan")
168167
.send()
169168
.await
170169
.expect("Failed to send");
@@ -184,7 +183,7 @@ async fn test_delete() {
184183
let res = table
185184
.query::<User>()
186185
.eq("pk", "[email protected]")
187-
.starts_with("name", "Dan")
186+
.starts_with("sk", "Dan")
188187
.send()
189188
.await
190189
.expect("Failed to send");

tests/ui/using-pk-instead-of-pk-sk.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ error[E0277]: the trait bound `PkSk: From<&str>` is not satisfied
66
| |
77
| required by a bound introduced by this call
88
|
9+
= help: the trait `From<(Pk, Sk)>` is implemented for `PkSk`
910
= note: required for `&str` to implement `Into<PkSk>`
1011
note: required by a bound in `EncryptedTable::get`
1112
--> src/encrypted_table/mod.rs

0 commit comments

Comments
 (0)