Skip to content

Commit 190e7f2

Browse files
author
Bennett Hardwick
committed
Fix, add some tests
1 parent 8a22a4c commit 190e7f2

19 files changed

+518
-156
lines changed

Cargo.lock

Lines changed: 70 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,7 @@ env_logger = "0.10.0"
4545
itertools = "0.11.0"
4646
thiserror = "1.0.50"
4747
tokio-stream = "0.1.14"
48+
49+
[dev-dependencies]
50+
serial_test = "2.0.0"
51+
trybuild = "1.0.85"

cryptonamo-derive/src/cryptonamo.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use std::collections::HashSet;
1+
use std::collections::HashMap;
22

33
use crate::settings::{IndexType, Settings};
4-
use proc_macro2::TokenStream;
4+
use proc_macro2::{Span, TokenStream};
55
use quote::{format_ident, quote};
66
use syn::{Data, DeriveInput, Fields, LitStr};
77

@@ -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 = HashSet::<String>::new();
45+
let mut compound_indexes: HashMap<String, Vec<(String, String)>> = Default::default();
4646

4747
for field in &fields_named.named {
4848
let ident = &field.ident;
@@ -51,6 +51,9 @@ 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+
5457
if attr.path().is_ident("cryptonamo") {
5558
attr.parse_nested_meta(|meta| {
5659
let directive = meta.path.get_ident().map(|i| i.to_string());
@@ -73,7 +76,7 @@ pub(crate) fn derive_cryptonamo(
7376
.ok_or(meta.error("no index type specified"))?
7477
.to_string();
7578

76-
settings.add_index(index_name, index_type.as_ref())?;
79+
query = Some(( index_name, index_type ));
7780

7881
Ok(())
7982
}
@@ -102,14 +105,33 @@ pub(crate) fn derive_cryptonamo(
102105
return Err(meta.error(format!("Compound index '{index_name}' does not include current field '{field_name}'.")));
103106
}
104107

105-
compound_indexes.insert(index_name);
108+
compound_index_name = Some(( index_name, meta.input.span() ));
106109

107110
Ok(())
108111
}
109112
_ => Err(meta.error("unsupported field attribute")),
110113
}
111114
})?;
112115
}
116+
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+
}
124+
125+
(Some((index_name, index_type)), None) => {
126+
settings.add_index(index_name, index_type.as_ref())?;
127+
}
128+
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+
}
132+
133+
(None, None) => {}
134+
};
113135
}
114136

115137
if !skip {
@@ -126,8 +148,8 @@ pub(crate) fn derive_cryptonamo(
126148
}
127149
}
128150

129-
for index in compound_indexes.into_iter() {
130-
settings.add_compound_index(index)?;
151+
for (name, parts) in compound_indexes.into_iter() {
152+
settings.add_compound_index(name, parts)?;
131153
}
132154
}
133155
}
@@ -253,4 +275,3 @@ pub(crate) fn derive_cryptonamo(
253275

254276
Ok(expanded)
255277
}
256-

cryptonamo-derive/src/settings.rs

Lines changed: 68 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,6 @@ impl IndexType {
2121
IndexType::Single(name, index_type)
2222
}
2323

24-
fn to_single(self) -> Option<(String, String)> {
25-
match self {
26-
IndexType::Single(a, b) => Some((a, b)),
27-
_ => None,
28-
}
29-
}
30-
31-
fn compound(compound_name: String, field_name: String, index_type: String) -> Self {
32-
IndexType::Compound1 {
33-
name: compound_name,
34-
index: (field_name, index_type),
35-
}
36-
}
37-
3824
fn and(self, field: String, index_type: String) -> Result<Self, syn::Error> {
3925
match self {
4026
IndexType::Single(_, _) => unimplemented!(),
@@ -114,46 +100,85 @@ impl Settings {
114100
}
115101
}
116102

117-
pub(crate) fn add_compound_index(&mut self, name: String) -> Result<(), syn::Error> {
118-
let mut parts = name.split("#");
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+
113+
pub(crate) fn add_compound_index(
114+
&mut self,
115+
name: String,
116+
parts: Vec<(String, String)>,
117+
) -> Result<(), syn::Error> {
118+
let name_parts = name.split("#").collect::<Vec<_>>();
119+
120+
if name_parts.len() > parts.len() {
121+
let missing_fields = name_parts
122+
.iter()
123+
.filter(|x| !parts.iter().find(|(y, _)| x == &y).is_some())
124+
.cloned()
125+
.collect::<Vec<_>>();
126+
127+
return Err(syn::Error::new_spanned(
128+
"",
129+
format!(
130+
"Not all fields were annotated with the #[cryptonamo(compound)] attribute. Missing fields: {}",
131+
missing_fields.join(",")
132+
),
133+
));
134+
}
135+
136+
if parts.len() > name_parts.len() {
137+
let extra_fields = parts
138+
.iter()
139+
.map(|(x, _)| x)
140+
.filter(|x| !name_parts.iter().find(|y| x == y).is_some())
141+
.cloned()
142+
.collect::<Vec<_>>();
143+
144+
return Err(syn::Error::new_spanned(
145+
"",
146+
format!(
147+
"Too many fields were annotated with the #[cryptonamo(compound)] attribute. Extra fields: {}",
148+
extra_fields.join(",")
149+
),
150+
));
151+
}
152+
153+
let mut name_parts_iter = name_parts.into_iter();
119154

120-
let first = parts.next().ok_or_else(|| {
121-
syn::Error::new_spanned("", format!("Expected compound index name to have parts"))
122-
})?;
155+
let field = name_parts_iter.next().unwrap();
123156

124157
let mut index = IndexType::Compound1 {
125158
name: name.clone(),
126-
index: self
127-
.indexes
128-
.get(first)
129-
.ok_or_else(|| {
130-
syn::Error::new_spanned("", format!("Expected index {first} to exist"))
131-
})?
132-
.clone()
133-
.to_single()
159+
index: parts
160+
.iter()
161+
.find(|x| x.0 == field)
134162
.ok_or_else(|| {
135163
syn::Error::new_spanned(
136164
"",
137-
format!("Expected index {first} to not be compound index"),
165+
format!("Internal error: index was not specified for field \"{field}\""),
138166
)
139-
})?,
167+
})?
168+
.clone(),
140169
};
141170

142-
while let Some(part) = parts.next() {
143-
let (field, index_type) = self
144-
.indexes
145-
.get(part)
146-
.ok_or_else(|| {
147-
syn::Error::new_spanned("", format!("Expected index {part} to exist"))
148-
})?
149-
.clone()
150-
.to_single()
171+
while let Some(field) = name_parts_iter.next() {
172+
let (field, index_type) = parts
173+
.iter()
174+
.find(|x| x.0 == field)
151175
.ok_or_else(|| {
152176
syn::Error::new_spanned(
153177
"",
154-
format!("Expected index {first} to not be compound index"),
178+
format!("Internal error: index was not specified for field \"{field}\""),
155179
)
156-
})?;
180+
})?
181+
.clone();
157182

158183
index = index.and(field, index_type)?;
159184
}
@@ -169,19 +194,10 @@ impl Settings {
169194
name: impl Into<String>,
170195
index_type: &str,
171196
) -> Result<(), syn::Error> {
172-
let name: String = name.into();
173-
match index_type {
174-
"exact" | "prefix" => self.indexes.insert(
175-
name.to_string(),
176-
IndexType::single(name, index_type.to_string()),
177-
),
178-
_ => Err(syn::Error::new_spanned(
179-
index_type,
180-
format!("Unsupported index type: {}", index_type),
181-
))?,
182-
};
197+
let name = name.into();
198+
self.indexes
199+
.insert(name.clone(), Self::create_index(name, index_type)?);
183200

184201
Ok(())
185202
}
186203
}
187-

0 commit comments

Comments
 (0)