use crate::store::Store;
use kevy_index::{IndexSpec, IndexValue};
use super::util::{arr, bulk, err, int};
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 {
err(out, "ERR usage: IDX.DROP name");
} else {
int(out, i64::from(s.idx_drop(&argv[1])));
}
}
b"IDX.LIST" => cmd_idx_list(s, out),
_ => return false,
}
true
}
pub(super) fn enc_value(out: &mut Vec<u8>, v: &IndexValue) {
match v {
IndexValue::I64(i) => {
out.push(0);
out.extend_from_slice(&i.to_le_bytes());
}
IndexValue::F64(f) => {
out.push(1);
out.extend_from_slice(&f.to_le_bytes());
}
IndexValue::Str(s) => {
out.push(2);
out.extend_from_slice(&(s.len() as u32).to_le_bytes());
out.extend_from_slice(s);
}
}
}
pub(super) fn dec_value(b: &[u8], pos: &mut usize) -> Option<IndexValue> {
let tag = *b.get(*pos)?;
*pos += 1;
match tag {
0 => {
let v = i64::from_le_bytes(b.get(*pos..*pos + 8)?.try_into().ok()?);
*pos += 8;
Some(IndexValue::I64(v))
}
1 => {
let v = f64::from_le_bytes(b.get(*pos..*pos + 8)?.try_into().ok()?);
*pos += 8;
Some(IndexValue::F64(v))
}
2 => {
let n = u32::from_le_bytes(b.get(*pos..*pos + 4)?.try_into().ok()?) as usize;
*pos += 4;
let s = b.get(*pos..*pos + n)?.to_vec();
*pos += n;
Some(IndexValue::Str(s))
}
_ => None,
}
}
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();
enc_value(&mut payload, v);
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 = dec_value(&bytes, &mut pos)?;
let key = bytes.get(pos..)?.to_vec();
Some((value, key))
}
pub(super) fn value_repr(v: &IndexValue) -> Vec<u8> {
match v {
IndexValue::I64(i) => i.to_string().into_bytes(),
IndexValue::F64(f) => format!("{f}").into_bytes(),
IndexValue::Str(s) => s.clone(),
}
}
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);
err(out, &format!("ERR no such index '{n}' (IDX.LIST enumerates them)"));
}
pub(super) fn badargs(out: &mut Vec<u8>, verb: &str, name: &[u8]) {
let n = String::from_utf8_lossy(name);
err(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()
};
arr(out, specs.len());
for spec in &specs {
let stats = s.idx_stats(&spec.name).unwrap_or_default();
arr(out, 12);
bulk(out, b"name");
bulk(out, &spec.name);
bulk(out, b"prefix");
bulk(out, &spec.prefix);
bulk(out, b"kind");
bulk(out, spec.kind.tag().as_bytes());
bulk(out, b"state");
bulk(out, b"ready");
bulk(out, b"entries");
bulk(out, stats.entries.to_string().as_bytes());
bulk(out, b"bytes");
bulk(out, stats.approx_bytes.to_string().as_bytes());
}
}