Skip to content

Commit b340495

Browse files
author
Bennett Hardwick
committed
Add spans for better errors
1 parent 10c6274 commit b340495

9 files changed

+104
-55
lines changed

cryptonamo-derive/src/cryptonamo.rs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub(crate) fn derive_cryptonamo(
4242
.flat_map(|x| x.ident.as_ref().map(|x| x.to_string()))
4343
.collect();
4444

45-
let mut compound_indexes: HashMap<String, Vec<(String, String)>> = Default::default();
45+
let mut compound_indexes: HashMap<String, Vec<(String, String, Span)>> = Default::default();
4646

4747
for field in &fields_named.named {
4848
let ident = &field.ident;
@@ -51,10 +51,10 @@ pub(crate) fn derive_cryptonamo(
5151

5252
// Parse the meta for the field
5353
for attr in &field.attrs {
54-
let mut query: Option<(String, String)> = None;
55-
let mut compound_index_name: Option<(String, Span)> = None;
56-
5754
if attr.path().is_ident("cryptonamo") {
55+
let mut query: Option<(String, String, Span)> = None;
56+
let mut compound_index_name: Option<(String, Span)> = None;
57+
5858
attr.parse_nested_meta(|meta| {
5959
let directive = meta.path.get_ident().map(|i| i.to_string());
6060
match directive.as_deref() {
@@ -70,13 +70,14 @@ pub(crate) fn derive_cryptonamo(
7070
}
7171
Some("query") => {
7272
let value = meta.value()?;
73+
let index_type_span = value.span();
7374
let index_type = value.parse::<LitStr>()?.value();
7475
let index_name = ident
7576
.as_ref()
7677
.ok_or(meta.error("no index type specified"))?
7778
.to_string();
7879

79-
query = Some(( index_name, index_type ));
80+
query = Some(( index_name, index_type, index_type_span ));
8081

8182
Ok(())
8283
}
@@ -112,26 +113,26 @@ pub(crate) fn derive_cryptonamo(
112113
_ => Err(meta.error("unsupported field attribute")),
113114
}
114115
})?;
115-
}
116116

117-
match (query, compound_index_name) {
118-
(Some((index_name, index_type)), Some((compound_index_name, _))) => {
119-
compound_indexes
120-
.entry(compound_index_name)
121-
.or_default()
122-
.push((index_name, index_type));
123-
}
117+
match (query, compound_index_name) {
118+
(Some((index_name, index_type, span)), Some((compound_index_name, _))) => {
119+
compound_indexes
120+
.entry(compound_index_name)
121+
.or_default()
122+
.push((index_name, index_type, span));
123+
}
124124

125-
(Some((index_name, index_type)), None) => {
126-
settings.add_index(index_name, index_type.as_ref())?;
127-
}
125+
(Some((index_name, index_type, span)), None) => {
126+
settings.add_index(index_name, index_type.as_ref(), span)?;
127+
}
128128

129-
(None, Some((compound_index_name, span))) => {
130-
return Err(syn::Error::new(span, format!("Compound attribute was specified but no query options were. Specify how this field should be queried with the attribute #[cryptonamo(query = <option>, compound = \"{compound_index_name}\")]")));
131-
}
129+
(None, Some((compound_index_name, span))) => {
130+
return Err(syn::Error::new(span, format!("Compound attribute was specified but no query options were. Specify how this field should be queried with the attribute #[cryptonamo(query = <option>, compound = \"{compound_index_name}\")]")));
131+
}
132132

133-
(None, None) => {}
134-
};
133+
(None, None) => {}
134+
};
135+
}
135136
}
136137

137138
if !skip {

cryptonamo-derive/src/settings.rs

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use proc_macro2::Span;
12
use quote::format_ident;
23
use std::collections::HashMap;
34
use syn::LitStr;
@@ -100,32 +101,22 @@ impl Settings {
100101
}
101102
}
102103

103-
fn create_index(name: String, index_type: &str) -> Result<IndexType, syn::Error> {
104-
match index_type {
105-
"exact" | "prefix" => Ok(IndexType::single(name, index_type.to_string())),
106-
_ => Err(syn::Error::new_spanned(
107-
index_type,
108-
format!("Unsupported index type: {}", index_type),
109-
)),
110-
}
111-
}
112-
113104
pub(crate) fn add_compound_index(
114105
&mut self,
115106
name: String,
116-
parts: Vec<(String, String)>,
107+
parts: Vec<(String, String, Span)>,
117108
) -> Result<(), syn::Error> {
118109
let name_parts = name.split("#").collect::<Vec<_>>();
119110

120111
if name_parts.len() > parts.len() {
121112
let missing_fields = name_parts
122113
.iter()
123-
.filter(|x| !parts.iter().find(|(y, _)| x == &y).is_some())
114+
.filter(|x| !parts.iter().find(|(y, _, _)| x == &y).is_some())
124115
.cloned()
125116
.collect::<Vec<_>>();
126117

127-
return Err(syn::Error::new_spanned(
128-
"",
118+
return Err(syn::Error::new(
119+
Span::call_site(),
129120
format!(
130121
"Not all fields were annotated with the #[cryptonamo(compound)] attribute. Missing fields: {}",
131122
missing_fields.join(",")
@@ -136,13 +127,13 @@ impl Settings {
136127
if parts.len() > name_parts.len() {
137128
let extra_fields = parts
138129
.iter()
139-
.map(|(x, _)| x)
130+
.map(|(x, _, _)| x)
140131
.filter(|x| !name_parts.iter().find(|y| x == y).is_some())
141132
.cloned()
142133
.collect::<Vec<_>>();
143134

144-
return Err(syn::Error::new_spanned(
145-
"",
135+
return Err(syn::Error::new(
136+
Span::call_site(),
146137
format!(
147138
"Too many fields were annotated with the #[cryptonamo(compound)] attribute. Extra fields: {}",
148139
extra_fields.join(",")
@@ -154,32 +145,38 @@ impl Settings {
154145

155146
let field = name_parts_iter.next().unwrap();
156147

148+
let (field, index_type, index_type_span) = parts
149+
.iter()
150+
.find(|x| x.0 == field)
151+
.ok_or_else(|| {
152+
syn::Error::new(
153+
Span::call_site(),
154+
format!("Internal error: index was not specified for field \"{field}\""),
155+
)
156+
})?
157+
.clone();
158+
159+
Self::validate_index_type(index_type.as_str(), index_type_span)?;
160+
157161
let mut index = IndexType::Compound1 {
158162
name: name.clone(),
159-
index: parts
160-
.iter()
161-
.find(|x| x.0 == field)
162-
.ok_or_else(|| {
163-
syn::Error::new_spanned(
164-
"",
165-
format!("Internal error: index was not specified for field \"{field}\""),
166-
)
167-
})?
168-
.clone(),
163+
index: (field, index_type),
169164
};
170165

171166
while let Some(field) = name_parts_iter.next() {
172-
let (field, index_type) = parts
167+
let (field, index_type, index_type_span) = parts
173168
.iter()
174169
.find(|x| x.0 == field)
175170
.ok_or_else(|| {
176-
syn::Error::new_spanned(
177-
"",
171+
syn::Error::new(
172+
Span::call_site(),
178173
format!("Internal error: index was not specified for field \"{field}\""),
179174
)
180175
})?
181176
.clone();
182177

178+
Self::validate_index_type(index_type.as_str(), index_type_span)?;
179+
183180
index = index.and(field, index_type)?;
184181
}
185182

@@ -188,15 +185,32 @@ impl Settings {
188185
Ok(())
189186
}
190187

188+
fn validate_index_type(index_type: &str, index_type_span: Span) -> Result<(), syn::Error> {
189+
if !matches!(index_type.as_ref(), "exact" | "prefix") {
190+
Err(syn::Error::new(
191+
index_type_span,
192+
format!("Unsupported index type: {}", index_type),
193+
))
194+
} else {
195+
Ok(())
196+
}
197+
}
198+
191199
// TODO: Add an IndexOptions enum so we can pass those through as well
192200
pub(crate) fn add_index(
193201
&mut self,
194202
name: impl Into<String>,
195203
index_type: &str,
204+
index_type_span: Span,
196205
) -> Result<(), syn::Error> {
197206
let name = name.into();
198-
self.indexes
199-
.insert(name.clone(), Self::create_index(name, index_type)?);
207+
208+
Self::validate_index_type(index_type, index_type_span)?;
209+
210+
self.indexes.insert(
211+
name.clone(),
212+
IndexType::single(name, index_type.to_string()),
213+
);
200214

201215
Ok(())
202216
}

tests/compile_tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ fn ui_tests() {
55
t.compile_fail("tests/ui/compound-index-missing-field.rs");
66
t.compile_fail("tests/ui/compound-index-missing-config.rs");
77
t.compile_fail("tests/ui/compound-index-too-many-fields.rs");
8+
t.compile_fail("tests/ui/index-unsupported.rs");
9+
t.compile_fail("tests/ui/compound-index-unsupported.rs");
810
}

tests/ui/compound-index-missing-config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use cryptonamo::Cryptonamo;
22

33
#[derive(Cryptonamo)]
4+
#[cryptonamo(partition_key = "email")]
45
struct User {
56
#[cryptonamo(compound = "email#name")]
67
email: String,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: Compound attribute was specified but no query options were. Specify how this field should be queried with the attribute #[cryptonamo(query = <option>, compound = "email#name")]
2-
--> tests/ui/compound-index-missing-config.rs:5:41
2+
--> tests/ui/compound-index-missing-config.rs:6:41
33
|
4-
5 | #[cryptonamo(compound = "email#name")]
4+
6 | #[cryptonamo(compound = "email#name")]
55
| ^
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use cryptonamo::Cryptonamo;
2+
3+
#[derive(Debug, Cryptonamo)]
4+
#[cryptonamo(partition_key = "email")]
5+
struct User {
6+
#[cryptonamo(query = "prefix", compound = "email#name")]
7+
email: String,
8+
#[cryptonamo(query = "blah", compound = "email#name")]
9+
name: String,
10+
}
11+
12+
fn main() {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: Unsupported index type: blah
2+
--> tests/ui/compound-index-unsupported.rs:8:26
3+
|
4+
8 | #[cryptonamo(query = "blah", compound = "email#name")]
5+
| ^^^^^^

tests/ui/index-unsupported.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use cryptonamo::Cryptonamo;
2+
3+
#[derive(Cryptonamo)]
4+
struct User {
5+
#[cryptonamo(query = "blah")]
6+
email: String,
7+
}
8+
9+
fn main() {}

tests/ui/index-unsupported.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: Unsupported index type: blah
2+
--> tests/ui/index-unsupported.rs:5:26
3+
|
4+
5 | #[cryptonamo(query = "blah")]
5+
| ^^^^^^

0 commit comments

Comments
 (0)