pub(super) fn scope_positions(
spec: &kevy_index::IndexSpec,
scope: &[Vec<u8>],
) -> Result<Vec<usize>, Vec<u8>> {
let mut out = Vec::with_capacity(scope.len());
for want in scope {
match spec.fields.iter().position(|f| f.name == *want) {
Some(i) => out.push(i),
None => {
let declared: Vec<&[u8]> = spec.fields.iter().map(|f| f.name.as_slice()).collect();
return Err(clause_error("IN", want, "index", &declared));
}
}
}
Ok(out)
}
fn clause_error(clause: &str, bad: &[u8], verb: &str, offered: &[&[u8]]) -> Vec<u8> {
let mut chunk = vec![crate::cmd_index_query::ST_CLAUSE];
chunk.extend_from_slice(clause_text(clause, bad, verb, offered).as_bytes());
chunk
}
fn nofield_error(clause: &str, bad: &[u8], offered: &[&[u8]]) -> Vec<u8> {
let mut chunk = vec![crate::cmd_index_query::ST_NOFIELD];
let f = &bad[..bad.len().min(255)];
chunk.push(f.len() as u8);
chunk.extend_from_slice(f);
chunk.extend_from_slice(clause_text(clause, bad, "store", offered).as_bytes());
chunk
}
fn clause_text(clause: &str, bad: &[u8], verb: &str, offered: &[&[u8]]) -> String {
format!(
"{clause} names field '{}', which this index does not {verb} — it {}: {}",
String::from_utf8_lossy(bad),
if verb.ends_with(['s', 'x', 'z']) { format!("{verb}es") } else { format!("{verb}s") },
String::from_utf8_lossy(&offered.join(&b", "[..])),
)
}
type ValuePred = Box<dyn Fn(&[u8]) -> bool>;
type BoxedPred = (usize, ValuePred);
pub(super) fn boxed_preds(
spec: &kevy_index::IndexSpec,
filters: &[super::args::FilterArg],
now: i64,
) -> Result<Vec<BoxedPred>, Vec<u8>> {
Ok(filter_tests(spec, filters, now)?
.into_iter()
.map(|(field, t)| {
let f: ValuePred = Box::new(move |v: &[u8]| t.passes(v));
(field, f)
})
.collect())
}
pub(super) fn filter_tests(
spec: &kevy_index::IndexSpec,
filters: &[super::args::FilterArg],
now: i64,
) -> Result<Vec<(usize, kevy_index::ValueTest)>, Vec<u8>> {
use super::args::FilterShape;
let mut out = Vec::with_capacity(filters.len());
for f in filters {
let Some(pos) = spec.values.iter().position(|v| v.name == f.field) else {
let stored: Vec<&[u8]> = spec.values.iter().map(|v| v.name.as_slice()).collect();
return Err(nofield_error("FILTER", &f.field, &stored));
};
let ty = spec.values[pos].ty;
let (test, raw) = match &f.shape {
FilterShape::Range { min, max } => {
(kevy_index::ValueTest::range_at(ty, min, max, now), min)
}
FilterShape::Eq { value } => (kevy_index::ValueTest::eq_at(ty, value, now), value),
};
let Some(test) = test else {
let mut chunk = vec![crate::cmd_index_query::ST_CLAUSE];
chunk.extend_from_slice(
format!(
"FILTER bound '{}' is not a valid {}, which is how this index declares '{}'",
String::from_utf8_lossy(raw),
ty.tag(),
String::from_utf8_lossy(&f.field),
)
.as_bytes(),
);
return Err(chunk);
};
out.push((pos, test));
}
Ok(out)
}
pub(super) fn distinct_field(
spec: &kevy_index::IndexSpec,
distinct: &Option<Vec<u8>>,
) -> Result<Option<(usize, kevy_index::ValType)>, Vec<u8>> {
let Some(field) = distinct else { return Ok(None) };
let Some(pos) = spec.values.iter().position(|v| v.name == *field) else {
let stored: Vec<&[u8]> = spec.values.iter().map(|v| v.name.as_slice()).collect();
return Err(nofield_error("DISTINCT", field, &stored));
};
Ok(Some((pos, spec.values[pos].ty)))
}
pub(super) fn facet_fields(
spec: &kevy_index::IndexSpec,
facets: &[Vec<u8>],
) -> Result<Vec<(usize, kevy_index::ValType)>, Vec<u8>> {
let mut out = Vec::with_capacity(facets.len());
for field in facets {
let Some(pos) = spec.values.iter().position(|v| v.name == *field) else {
let stored: Vec<&[u8]> = spec.values.iter().map(|v| v.name.as_slice()).collect();
return Err(nofield_error("FACET", field, &stored));
};
out.push((pos, spec.values[pos].ty));
}
Ok(out)
}
pub(super) fn sort_field(
spec: &kevy_index::IndexSpec,
sort: &Option<(Vec<u8>, bool)>,
) -> Result<Option<(usize, bool, kevy_index::ValType)>, Vec<u8>> {
let Some((field, desc)) = sort else { return Ok(None) };
let Some(pos) = spec.values.iter().position(|v| v.name == *field) else {
let stored: Vec<&[u8]> = spec.values.iter().map(|v| v.name.as_slice()).collect();
return Err(nofield_error("SORT", field, &stored));
};
Ok(Some((pos, *desc, spec.values[pos].ty)))
}