use crate::store::Store;
use super::kevy_err;
use kevy_index::{IndexSpec, IndexValue};
use kevy_resp::{encode_array_len, encode_bulk, encode_error, encode_integer};
pub(super) fn dispatch(s: &Store, up: &[u8], argv: &[Vec<u8>], out: &mut Vec<u8>) -> bool {
match up {
b"IDX.CREATE" => super::idx_create::cmd_idx_create(s, argv, out),
b"IDX.DROP" => {
if argv.len() != 2 {
encode_error(out, "ERR usage: IDX.DROP name");
} else {
match s.idx_drop(&argv[1]) {
Ok(hit) => encode_integer(out, i64::from(hit)),
Err(e) => kevy_err(out, &e),
}
}
}
b"IDX.LIST" => cmd_idx_list(s, out),
b"IDX.ADVISE" => {
if argv.len() != 1 {
encode_error(out, "ERR usage: IDX.ADVISE");
} else {
cmd_idx_advise(s, out);
}
}
_ => return false,
}
true
}
fn cmd_idx_advise(s: &Store, out: &mut Vec<u8>) {
let rows = s.idx_advise();
encode_array_len(out, rows.len() as i64);
for r in rows {
encode_array_len(out, 3);
encode_integer(out, r.count as i64);
encode_bulk(out, &r.name);
encode_bulk(out, r.advice.as_bytes());
}
}
pub(super) fn hex(b: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(b.len() * 2);
for x in b {
out.extend_from_slice(format!("{x:02x}").as_bytes());
}
out
}
pub(super) fn unhex(raw: &[u8]) -> Option<Vec<u8>> {
if !raw.len().is_multiple_of(2) {
return None;
}
let mut out = Vec::with_capacity(raw.len() / 2);
for pair in raw.chunks(2) {
out.push(u8::from_str_radix(std::str::from_utf8(pair).ok()?, 16).ok()?);
}
Some(out)
}
pub(super) fn encode_cursor(v: &IndexValue, k: &[u8]) -> Vec<u8> {
let mut payload = Vec::new();
v.encode(&mut payload);
payload.extend_from_slice(k);
hex(&payload)
}
pub(super) fn decode_cursor(raw: &[u8]) -> Option<(IndexValue, Vec<u8>)> {
let bytes = unhex(raw)?;
let mut pos = 0usize;
let value = IndexValue::decode(&bytes, &mut pos)?;
let key = bytes.get(pos..)?.to_vec();
Some((value, key))
}
pub(super) fn spec_of(s: &Store, name: &[u8]) -> Option<IndexSpec> {
let g = s.indexes.catalog.read().unwrap_or_else(std::sync::PoisonError::into_inner);
g.1.get(name).map(|(spec, _)| spec.clone())
}
pub(super) fn no_such_index(out: &mut Vec<u8>, name: &[u8]) {
let n = String::from_utf8_lossy(name);
encode_error(out, &format!("ERR no such index '{n}' (IDX.LIST enumerates them)"));
}
pub(super) fn arity_err(out: &mut Vec<u8>, verb: &str) {
encode_error(
out,
&format!("ERR wrong number of arguments for '{}' command", verb.to_lowercase()),
);
}
pub(super) fn badargs(out: &mut Vec<u8>, verb: &str, name: &[u8]) {
let n = String::from_utf8_lossy(name);
encode_error(
out,
&format!("ERR {verb} '{n}': bad arguments — run COMMAND DOCS {verb} for the syntax"),
);
}
fn cmd_idx_list(s: &Store, out: &mut Vec<u8>) {
let specs: Vec<IndexSpec> = {
let g = s.indexes.catalog.read().unwrap_or_else(std::sync::PoisonError::into_inner);
g.1.iter().map(|(spec, _)| spec.clone()).collect()
};
encode_array_len(out, specs.len() as i64);
for spec in &specs {
let stats = s.idx_stats(spec.name()).unwrap_or_default();
let (hits, last, _) = s.idx_usage(spec.name()).unwrap_or((0, 0, 0));
encode_array_len(out, 20);
encode_bulk(out, b"name");
encode_bulk(out, spec.name());
encode_bulk(out, b"prefix");
encode_bulk(out, spec.prefix());
encode_bulk(out, b"kind");
encode_bulk(out, spec.kind().tag().as_bytes());
encode_bulk(out, b"state");
encode_bulk(out, b"ready");
encode_bulk(out, b"entries");
encode_bulk(out, stats.entries.to_string().as_bytes());
encode_bulk(out, b"bytes");
encode_bulk(out, stats.approx_bytes.to_string().as_bytes());
encode_bulk(out, b"hits");
encode_bulk(out, hits.to_string().as_bytes());
encode_bulk(out, b"last_hit");
encode_bulk(out, last.to_string().as_bytes());
encode_bulk(out, b"auto");
encode_bulk(out, if s.is_auto_path(spec.name()) { b"1" } else { b"0" });
encode_bulk(out, b"partitioning");
encode_bulk(out, b"local");
}
}