mod bitmap;
mod hash;
#[cfg(feature = "index")]
mod idx;
#[cfg(feature = "index")]
mod idx_create;
#[cfg(feature = "index")]
mod idx_compose;
#[cfg(feature = "index")]
mod idx_query;
mod keyspace;
mod list;
mod misc;
mod set;
mod strings;
mod util;
#[cfg(feature = "index")]
mod table;
#[cfg(feature = "index")]
mod view;
mod zset;
mod zset_algebra;
use crate::store::Store;
const UPPER_STACK: usize = 16;
pub(crate) fn dispatch(s: &Store, argv: &[Vec<u8>], out: &mut Vec<u8>) {
let Some(verb) = argv.first() else {
return util::err(out, "ERR empty command");
};
let mut vbuf = [0u8; UPPER_STACK];
let vheap;
let up: &[u8] = if verb.len() <= UPPER_STACK {
for (slot, &b) in vbuf.iter_mut().zip(verb.iter()) {
*slot = b.to_ascii_uppercase();
}
&vbuf[..verb.len()]
} else {
vheap = verb.to_ascii_uppercase();
&vheap
};
let handled = strings::dispatch(s, up, argv, out)
|| hash::dispatch(s, up, argv, out)
|| list::dispatch(s, up, argv, out)
|| set::dispatch(s, up, argv, out)
|| zset::dispatch(s, up, argv, out)
|| zset_algebra::dispatch(s, up, argv, out)
|| bitmap::dispatch(s, up, argv, out)
|| keyspace::dispatch(s, up, argv, out)
|| misc::dispatch(s, up, argv, out)
|| dispatch_index(s, up, argv, out);
if !handled {
let shown = String::from_utf8_lossy(verb);
util::err(out, &format!("ERR unknown command '{shown}'"));
}
}
#[cfg(feature = "index")]
fn dispatch_index(s: &Store, up: &[u8], argv: &[Vec<u8>], out: &mut Vec<u8>) -> bool {
idx::dispatch(s, up, argv, out)
|| idx_query::dispatch(s, up, argv, out)
|| view::dispatch(s, up, argv, out)
|| table::dispatch(s, up, argv, out)
}
#[cfg(not(feature = "index"))]
fn dispatch_index(_s: &Store, _up: &[u8], _argv: &[Vec<u8>], _out: &mut Vec<u8>) -> bool {
false
}
fn parse_match_count(argv: &[Vec<u8>], start: usize) -> Option<Option<Vec<u8>>> {
let mut pat: Option<Vec<u8>> = None;
let mut i = start;
while i < argv.len() {
let tok = &argv[i];
if tok.eq_ignore_ascii_case(b"MATCH") {
pat = Some(argv.get(i + 1)?.clone());
i += 2;
} else if tok.eq_ignore_ascii_case(b"COUNT") {
util::arg_i64(argv.get(i + 1)?)?;
i += 2;
} else {
return None;
}
}
Some(pat)
}
fn emit_scan_page(out: &mut Vec<u8>, cursor: &[u8], elems: &[Vec<u8>]) {
util::arr(out, 2);
util::bulk(out, cursor);
util::arr(out, elems.len());
for e in elems {
util::bulk(out, e);
}
}
#[cfg(test)]
pub(crate) const DISPATCH_VERBS: &[&str] = &[
"APPEND", "DECR", "DECRBY", "GET", "GETDEL", "GETEX", "GETRANGE", "GETSET", "INCR", "INCRBY",
"INCRBYFLOAT", "MGET", "MSET", "SET", "SETNX", "SETRANGE", "STRLEN",
"BITCOUNT", "BITOP", "BITPOS", "GETBIT", "SETBIT",
"HDEL", "HEXISTS", "HGET", "HGETALL", "HINCRBY", "HINCRBYFLOAT", "HKEYS", "HLEN", "HMGET",
"HSCAN", "HSET", "HSETNX", "HEXPIRE", "HPEXPIRE", "HPEXPIREAT", "HTTL", "HPTTL", "HPERSIST",
"HVALS",
"LINDEX", "LINSERT", "LLEN", "LPOP", "LPUSH", "LRANGE", "LREM", "LSET", "LTRIM", "RPOP",
"RPUSH",
"SADD", "SCARD", "SDIFF", "SDIFFSTORE", "SINTER", "SINTERSTORE", "SISMEMBER", "SMEMBERS",
"SPOP", "SRANDMEMBER", "SREM", "SUNION", "SUNIONSTORE",
"ZADD", "ZCARD", "ZCOUNT", "ZDIFFSTORE", "ZINCRBY", "ZINTERCARD", "ZINTERSTORE", "ZPOPMIN",
"ZPOPMIN.BELOW", "ZRANGE", "ZRANGEBYSCORE", "ZRANK", "ZREM", "ZREMRANGEBYRANK",
"ZREMRANGEBYSCORE", "ZREVRANGE", "ZREVRANGEBYSCORE", "ZSCAN", "ZSCORE", "ZUNIONSTORE",
"COPY", "DBSIZE", "DEL", "EXISTS", "EXPIRE", "EXPIREAT", "FLUSHALL", "KEYS", "PERSIST",
"PEXPIRE", "PEXPIREAT", "PTTL", "RANDOMKEY", "RENAME", "RENAMENX", "SCAN", "TIME", "TOUCH",
"TTL", "TYPE", "UNLINK",
"FEED.READ", "FEED.SHARDS", "FEED.TAIL", "PREFIX.DIGEST", "PREFIX.STATS",
"IDX.COUNT", "IDX.CREATE", "IDX.DROP", "IDX.LIST", "IDX.QUERY", "VIEW.CREATE", "VIEW.DROP",
"VIEW.LIST", "VIEW.QUERY", "TABLE.DECLARE", "TABLE.ENSURE", "TABLE.REPLACE", "TABLE.DROP", "TABLE.LIST", "TABLE.VERIFY",
"ECHO", "PING", "PUBLISH",
];
#[cfg(test)]
#[path = "dispatch_tests.rs"]
mod tests;