#![allow(clippy::too_many_lines)]
#![allow(clippy::match_same_arms)]
use crate::msg::MsgType;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CommandClass {
Argz,
Arg0,
Arg1,
Arg2,
Arg3,
ArgN,
ArgX,
ArgKvX,
ArgUpto1,
ArgEval,
}
impl CommandClass {
#[must_use]
pub fn is_argz(self) -> bool {
matches!(self, Self::Argz)
}
#[must_use]
pub fn is_argx(self) -> bool {
matches!(self, Self::ArgX)
}
#[must_use]
pub fn is_argkvx(self) -> bool {
matches!(self, Self::ArgKvX)
}
#[must_use]
pub fn is_argeval(self) -> bool {
matches!(self, Self::ArgEval)
}
}
#[must_use]
pub fn classify(ty: MsgType) -> CommandClass {
use MsgType as M;
match ty {
M::ReqRedisPing | M::ReqRedisQuit | M::ReqRedisScriptFlush | M::ReqRedisScriptKill => {
CommandClass::Argz
}
M::ReqRedisPersist
| M::ReqRedisPttl
| M::ReqRedisTtl
| M::ReqRedisType
| M::ReqRedisDump
| M::ReqRedisDecr
| M::ReqRedisGet
| M::ReqRedisIncr
| M::ReqRedisStrlen
| M::ReqRedisHgetall
| M::ReqRedisHkeys
| M::ReqRedisHlen
| M::ReqRedisHvals
| M::ReqRedisLlen
| M::ReqRedisLpop
| M::ReqRedisRpop
| M::ReqRedisScard
| M::ReqRedisSmembers
| M::ReqRedisSrandmember
| M::ReqRedisZcard
| M::ReqRedisKeys
| M::ReqRedisPfcount => CommandClass::Arg0,
M::ReqRedisExpire
| M::ReqRedisExpireat
| M::ReqRedisPexpire
| M::ReqRedisPexpireat
| M::ReqRedisAppend
| M::ReqRedisDecrby
| M::ReqRedisGetbit
| M::ReqRedisGetset
| M::ReqRedisIncrby
| M::ReqRedisIncrbyfloat
| M::ReqRedisSetnx
| M::ReqRedisHexists
| M::ReqRedisHget
| M::ReqRedisHstrlen
| M::ReqRedisLindex
| M::ReqRedisLpushx
| M::ReqRedisRpoplpush
| M::ReqRedisRpushx
| M::ReqRedisSismember
| M::ReqRedisZrank
| M::ReqRedisZrevrank
| M::ReqRedisZscore
| M::ReqRedisSlaveof
| M::ReqRedisConfig
| M::ReqRedisScriptLoad
| M::ReqRedisScriptExists => CommandClass::Arg1,
M::ReqRedisGetrange
| M::ReqRedisPsetex
| M::ReqRedisSetbit
| M::ReqRedisSetex
| M::ReqRedisSetrange
| M::ReqRedisHincrby
| M::ReqRedisHincrbyfloat
| M::ReqRedisHsetnx
| M::ReqRedisLrange
| M::ReqRedisLrem
| M::ReqRedisLset
| M::ReqRedisLtrim
| M::ReqRedisSmove
| M::ReqRedisZcount
| M::ReqRedisZincrby
| M::ReqRedisZlexcount
| M::ReqRedisZremrangebylex
| M::ReqRedisZremrangebyrank
| M::ReqRedisZremrangebyscore
| M::ReqRedisRestore => CommandClass::Arg2,
M::ReqRedisLinsert => CommandClass::Arg3,
M::ReqRedisSort
| M::ReqRedisBitcount
| M::ReqRedisBitpos
| M::ReqRedisSet
| M::ReqRedisScan
| M::ReqRedisHdel
| M::ReqRedisHmget
| M::ReqRedisHmset
| M::ReqRedisHscan
| M::ReqRedisHset
| M::ReqRedisLpush
| M::ReqRedisRpush
| M::ReqRedisSadd
| M::ReqRedisSdiff
| M::ReqRedisSdiffstore
| M::ReqRedisSinter
| M::ReqRedisSinterstore
| M::ReqRedisSrem
| M::ReqRedisSunion
| M::ReqRedisSunionstore
| M::ReqRedisSscan
| M::ReqRedisSpop
| M::ReqRedisZadd
| M::ReqRedisZinterstore
| M::ReqRedisZrange
| M::ReqRedisZrangebyscore
| M::ReqRedisZrem
| M::ReqRedisZrevrange
| M::ReqRedisZrangebylex
| M::ReqRedisZrevrangebylex
| M::ReqRedisZrevrangebyscore
| M::ReqRedisZunionstore
| M::ReqRedisZscan
| M::ReqRedisPfadd
| M::ReqRedisGeoadd
| M::ReqRedisGeoradius
| M::ReqRedisGeodist
| M::ReqRedisGeohash
| M::ReqRedisGeopos
| M::ReqRedisGeoradiusbymember
| M::ReqRedisJsonset
| M::ReqRedisJsonget
| M::ReqRedisJsondel
| M::ReqRedisJsontype
| M::ReqRedisJsonmget
| M::ReqRedisJsonarrappend
| M::ReqRedisJsonarrinsert
| M::ReqRedisJsonarrlen
| M::ReqRedisJsonobjkeys
| M::ReqRedisJsonobjlen
| M::ReqRedisUnlink => CommandClass::ArgN,
M::ReqRedisMget | M::ReqRedisDel | M::ReqRedisExists => CommandClass::ArgX,
M::ReqRedisMset => CommandClass::ArgKvX,
M::ReqRedisInfo => CommandClass::ArgUpto1,
M::ReqRedisEval | M::ReqRedisEvalsha => CommandClass::ArgEval,
M::ReqRedisFtCreate | M::ReqRedisFtSearch | M::ReqRedisFtDropindex | M::ReqRedisFtRegex => {
CommandClass::ArgN
}
M::ReqRedisFtInfo => CommandClass::Arg0,
M::ReqRedisFtList => CommandClass::Argz,
M::ReqRedisFtUnknown => CommandClass::ArgN,
_ => CommandClass::Arg0,
}
}
#[must_use]
pub fn is_redis_error(ty: MsgType) -> bool {
use MsgType as M;
matches!(
ty,
M::RspRedisError
| M::RspRedisErrorErr
| M::RspRedisErrorOom
| M::RspRedisErrorBusy
| M::RspRedisErrorNoauth
| M::RspRedisErrorLoading
| M::RspRedisErrorBusykey
| M::RspRedisErrorMisconf
| M::RspRedisErrorNoscript
| M::RspRedisErrorReadonly
| M::RspRedisErrorWrongtype
| M::RspRedisErrorExecabort
| M::RspRedisErrorMasterdown
| M::RspRedisErrorNoreplicas
)
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct CommandTraits {
pub is_read: bool,
pub quit: bool,
pub routing: RoutingOverride,
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub enum RoutingOverride {
#[default]
None,
LocalNodeOnly,
TokenOwnerLocalRackOnly,
AllNodesAllRacksAllDcs,
}
#[must_use]
pub fn lookup(keyword: &[u8]) -> Option<(MsgType, CommandTraits)> {
if keyword.is_empty() || keyword.len() > 32 {
return None;
}
let mut buf = [0u8; 32];
for (i, &b) in keyword.iter().enumerate() {
buf[i] = b.to_ascii_lowercase();
}
let key = &buf[..keyword.len()];
if key.starts_with(b"ft.")
&& !matches!(
key,
b"ft.create"
| b"ft.search"
| b"ft.info"
| b"ft.list"
| b"ft._list"
| b"ft.dropindex"
| b"ft.regex"
)
{
return Some((
MsgType::ReqRedisFtUnknown,
CommandTraits {
is_read: true,
quit: false,
routing: RoutingOverride::LocalNodeOnly,
},
));
}
let (ty, is_read, quit, routing) = match key {
b"get" => (MsgType::ReqRedisGet, true, false, RoutingOverride::None),
b"set" => (MsgType::ReqRedisSet, false, false, RoutingOverride::None),
b"ttl" => (MsgType::ReqRedisTtl, false, false, RoutingOverride::None),
b"del" => (MsgType::ReqRedisDel, false, false, RoutingOverride::None),
b"pttl" => (MsgType::ReqRedisPttl, true, false, RoutingOverride::None),
b"decr" => (MsgType::ReqRedisDecr, false, false, RoutingOverride::None),
b"dump" => (MsgType::ReqRedisDump, true, false, RoutingOverride::None),
b"hdel" => (MsgType::ReqRedisHdel, false, false, RoutingOverride::None),
b"hget" => (MsgType::ReqRedisHget, true, false, RoutingOverride::None),
b"hlen" => (MsgType::ReqRedisHlen, true, false, RoutingOverride::None),
b"hset" => (MsgType::ReqRedisHset, false, false, RoutingOverride::None),
b"incr" => (MsgType::ReqRedisIncr, false, false, RoutingOverride::None),
b"keys" => (
MsgType::ReqRedisKeys,
true,
false,
RoutingOverride::LocalNodeOnly,
),
b"info" => (
MsgType::ReqRedisInfo,
true,
false,
RoutingOverride::LocalNodeOnly,
),
b"llen" => (MsgType::ReqRedisLlen, true, false, RoutingOverride::None),
b"lpop" => (MsgType::ReqRedisLpop, false, false, RoutingOverride::None),
b"lrem" => (MsgType::ReqRedisLrem, false, false, RoutingOverride::None),
b"lset" => (MsgType::ReqRedisLset, false, false, RoutingOverride::None),
b"mget" => (MsgType::ReqRedisMget, true, false, RoutingOverride::None),
b"mset" => (MsgType::ReqRedisMset, false, false, RoutingOverride::None),
b"ping" => (
MsgType::ReqRedisPing,
true,
false,
RoutingOverride::LocalNodeOnly,
),
b"rpop" => (MsgType::ReqRedisRpop, false, false, RoutingOverride::None),
b"sadd" => (MsgType::ReqRedisSadd, false, false, RoutingOverride::None),
b"scan" => (
MsgType::ReqRedisScan,
true,
false,
RoutingOverride::LocalNodeOnly,
),
b"spop" => (MsgType::ReqRedisSpop, false, false, RoutingOverride::None),
b"srem" => (MsgType::ReqRedisSrem, false, false, RoutingOverride::None),
b"type" => (MsgType::ReqRedisType, true, false, RoutingOverride::None),
b"zadd" => (MsgType::ReqRedisZadd, false, false, RoutingOverride::None),
b"zrem" => (MsgType::ReqRedisZrem, false, false, RoutingOverride::None),
b"eval" => (MsgType::ReqRedisEval, false, false, RoutingOverride::None),
b"sort" => (MsgType::ReqRedisSort, true, false, RoutingOverride::None),
b"quit" => (MsgType::ReqRedisQuit, false, true, RoutingOverride::None),
b"load" => (
MsgType::ReqRedisScriptLoad,
false,
false,
RoutingOverride::AllNodesAllRacksAllDcs,
),
b"kill" => (
MsgType::ReqRedisScriptKill,
false,
false,
RoutingOverride::AllNodesAllRacksAllDcs,
),
b"hkeys" => (
MsgType::ReqRedisHkeys,
true,
false,
RoutingOverride::TokenOwnerLocalRackOnly,
),
b"hmget" => (MsgType::ReqRedisHmget, true, false, RoutingOverride::None),
b"hmset" => (MsgType::ReqRedisHmset, false, false, RoutingOverride::None),
b"hvals" => (
MsgType::ReqRedisHvals,
true,
false,
RoutingOverride::TokenOwnerLocalRackOnly,
),
b"hscan" => (
MsgType::ReqRedisHscan,
true,
false,
RoutingOverride::TokenOwnerLocalRackOnly,
),
b"lpush" => (MsgType::ReqRedisLpush, false, false, RoutingOverride::None),
b"ltrim" => (MsgType::ReqRedisLtrim, false, false, RoutingOverride::None),
b"rpush" => (MsgType::ReqRedisRpush, false, false, RoutingOverride::None),
b"scard" => (MsgType::ReqRedisScard, true, false, RoutingOverride::None),
b"sdiff" => (MsgType::ReqRedisSdiff, true, false, RoutingOverride::None),
b"setex" => (MsgType::ReqRedisSetex, false, false, RoutingOverride::None),
b"setnx" => (MsgType::ReqRedisSetnx, false, false, RoutingOverride::None),
b"smove" => (MsgType::ReqRedisSmove, false, false, RoutingOverride::None),
b"sscan" => (
MsgType::ReqRedisSscan,
true,
false,
RoutingOverride::TokenOwnerLocalRackOnly,
),
b"zcard" => (MsgType::ReqRedisZcard, true, false, RoutingOverride::None),
b"zrank" => (MsgType::ReqRedisZrank, true, false, RoutingOverride::None),
b"zscan" => (
MsgType::ReqRedisZscan,
true,
false,
RoutingOverride::TokenOwnerLocalRackOnly,
),
b"pfadd" => (MsgType::ReqRedisPfadd, false, false, RoutingOverride::None),
b"flush" => (
MsgType::ReqRedisScriptFlush,
false,
false,
RoutingOverride::AllNodesAllRacksAllDcs,
),
b"append" => (MsgType::ReqRedisAppend, false, false, RoutingOverride::None),
b"decrby" => (MsgType::ReqRedisDecrby, false, false, RoutingOverride::None),
b"exists" => (MsgType::ReqRedisExists, true, false, RoutingOverride::None),
b"expire" => (MsgType::ReqRedisExpire, false, false, RoutingOverride::None),
b"getbit" => (MsgType::ReqRedisGetbit, true, false, RoutingOverride::None),
b"getset" => (MsgType::ReqRedisGetset, false, false, RoutingOverride::None),
b"psetex" => (MsgType::ReqRedisPsetex, false, false, RoutingOverride::None),
b"hsetnx" => (MsgType::ReqRedisHsetnx, false, false, RoutingOverride::None),
b"incrby" => (MsgType::ReqRedisIncrby, false, false, RoutingOverride::None),
b"lindex" => (MsgType::ReqRedisLindex, true, false, RoutingOverride::None),
b"lpushx" => (MsgType::ReqRedisLpushx, false, false, RoutingOverride::None),
b"lrange" => (MsgType::ReqRedisLrange, true, false, RoutingOverride::None),
b"rpushx" => (MsgType::ReqRedisRpushx, false, false, RoutingOverride::None),
b"setbit" => (MsgType::ReqRedisSetbit, false, false, RoutingOverride::None),
b"sinter" => (MsgType::ReqRedisSinter, true, false, RoutingOverride::None),
b"strlen" => (MsgType::ReqRedisStrlen, true, false, RoutingOverride::None),
b"sunion" => (MsgType::ReqRedisSunion, true, false, RoutingOverride::None),
b"zcount" => (MsgType::ReqRedisZcount, true, false, RoutingOverride::None),
b"zrange" => (MsgType::ReqRedisZrange, true, false, RoutingOverride::None),
b"zscore" => (MsgType::ReqRedisZscore, true, false, RoutingOverride::None),
b"config" => (
MsgType::ReqRedisConfig,
true,
false,
RoutingOverride::LocalNodeOnly,
),
b"geoadd" => (MsgType::ReqRedisGeoadd, false, false, RoutingOverride::None),
b"geopos" => (MsgType::ReqRedisGeopos, true, false, RoutingOverride::None),
b"unlink" => (MsgType::ReqRedisUnlink, false, false, RoutingOverride::None),
b"script" => (MsgType::ReqRedisScript, false, false, RoutingOverride::None),
b"bitpos" => (MsgType::ReqRedisBitpos, true, false, RoutingOverride::None),
b"persist" => (
MsgType::ReqRedisPersist,
false,
false,
RoutingOverride::None,
),
b"pexpire" => (
MsgType::ReqRedisPexpire,
false,
false,
RoutingOverride::None,
),
b"hexists" => (MsgType::ReqRedisHexists, true, false, RoutingOverride::None),
b"hgetall" => (
MsgType::ReqRedisHgetall,
true,
false,
RoutingOverride::TokenOwnerLocalRackOnly,
),
b"hincrby" => (
MsgType::ReqRedisHincrby,
false,
false,
RoutingOverride::None,
),
b"linsert" => (
MsgType::ReqRedisLinsert,
false,
false,
RoutingOverride::None,
),
b"zincrby" => (
MsgType::ReqRedisZincrby,
false,
false,
RoutingOverride::None,
),
b"evalsha" => (
MsgType::ReqRedisEvalsha,
false,
false,
RoutingOverride::None,
),
b"restore" => (
MsgType::ReqRedisRestore,
false,
false,
RoutingOverride::None,
),
b"slaveof" => (
MsgType::ReqRedisSlaveof,
false,
false,
RoutingOverride::LocalNodeOnly,
),
b"pfcount" => (
MsgType::ReqRedisPfcount,
false,
false,
RoutingOverride::None,
),
b"geohash" => (MsgType::ReqRedisGeohash, true, false, RoutingOverride::None),
b"geodist" => (MsgType::ReqRedisGeodist, true, false, RoutingOverride::None),
b"hstrlen" => (MsgType::ReqRedisHstrlen, true, false, RoutingOverride::None),
b"expireat" => (
MsgType::ReqRedisExpireat,
false,
false,
RoutingOverride::None,
),
b"bitcount" => (
MsgType::ReqRedisBitcount,
true,
false,
RoutingOverride::None,
),
b"getrange" => (
MsgType::ReqRedisGetrange,
true,
false,
RoutingOverride::None,
),
b"setrange" => (
MsgType::ReqRedisSetrange,
false,
false,
RoutingOverride::None,
),
b"smembers" => (
MsgType::ReqRedisSmembers,
true,
false,
RoutingOverride::None,
),
b"zrevrank" => (
MsgType::ReqRedisZrevrank,
true,
false,
RoutingOverride::None,
),
b"json.set" => (
MsgType::ReqRedisJsonset,
false,
false,
RoutingOverride::None,
),
b"json.get" => (MsgType::ReqRedisJsonget, true, false, RoutingOverride::None),
b"json.del" => (
MsgType::ReqRedisJsondel,
false,
false,
RoutingOverride::None,
),
b"pexpireat" => (
MsgType::ReqRedisPexpireat,
false,
false,
RoutingOverride::None,
),
b"rpoplpush" => (
MsgType::ReqRedisRpoplpush,
false,
false,
RoutingOverride::None,
),
b"sismember" => (
MsgType::ReqRedisSismember,
true,
false,
RoutingOverride::None,
),
b"zlexcount" => (
MsgType::ReqRedisZlexcount,
true,
false,
RoutingOverride::None,
),
b"zrevrange" => (
MsgType::ReqRedisZrevrange,
true,
false,
RoutingOverride::None,
),
b"georadius" => (
MsgType::ReqRedisGeoradius,
true,
false,
RoutingOverride::None,
),
b"json.type" => (
MsgType::ReqRedisJsontype,
true,
false,
RoutingOverride::None,
),
b"json.mget" => (
MsgType::ReqRedisJsonmget,
true,
false,
RoutingOverride::None,
),
b"sdiffstore" => (
MsgType::ReqRedisSdiffstore,
false,
false,
RoutingOverride::None,
),
b"incrbyfloat" => (
MsgType::ReqRedisIncrbyfloat,
false,
false,
RoutingOverride::None,
),
b"sinterstore" => (
MsgType::ReqRedisSinterstore,
false,
false,
RoutingOverride::None,
),
b"srandmember" => (
MsgType::ReqRedisSrandmember,
true,
false,
RoutingOverride::None,
),
b"sunionstore" => (
MsgType::ReqRedisSunionstore,
true,
false,
RoutingOverride::None,
),
b"zinterstore" => (
MsgType::ReqRedisZinterstore,
true,
false,
RoutingOverride::None,
),
b"zrangebylex" => (
MsgType::ReqRedisZrangebylex,
true,
false,
RoutingOverride::None,
),
b"zunionstore" => (
MsgType::ReqRedisZunionstore,
true,
false,
RoutingOverride::None,
),
b"json.arrlen" => (
MsgType::ReqRedisJsonarrlen,
true,
false,
RoutingOverride::None,
),
b"json.objlen" => (
MsgType::ReqRedisJsonobjlen,
true,
false,
RoutingOverride::None,
),
b"hincrbyfloat" => (
MsgType::ReqRedisHincrbyfloat,
false,
false,
RoutingOverride::None,
),
b"json.objkeys" => (
MsgType::ReqRedisJsonobjkeys,
true,
false,
RoutingOverride::None,
),
b"zrangebyscore" => (
MsgType::ReqRedisZrangebyscore,
true,
false,
RoutingOverride::None,
),
b"zremrangebylex" => (
MsgType::ReqRedisZremrangebylex,
false,
false,
RoutingOverride::None,
),
b"zrevrangebylex" => (
MsgType::ReqRedisZrevrangebylex,
true,
false,
RoutingOverride::None,
),
b"json.arrappend" => (
MsgType::ReqRedisJsonarrappend,
false,
false,
RoutingOverride::None,
),
b"json.arrinsert" => (
MsgType::ReqRedisJsonarrinsert,
false,
false,
RoutingOverride::None,
),
b"zremrangebyrank" => (
MsgType::ReqRedisZremrangebyrank,
false,
false,
RoutingOverride::None,
),
b"zremrangebyscore" => (
MsgType::ReqRedisZremrangebyscore,
false,
false,
RoutingOverride::None,
),
b"zrevrangebyscore" => (
MsgType::ReqRedisZrevrangebyscore,
true,
false,
RoutingOverride::None,
),
b"georadiusbymember" => (
MsgType::ReqRedisGeoradiusbymember,
true,
false,
RoutingOverride::None,
),
b"ft.create" => (
MsgType::ReqRedisFtCreate,
false,
false,
RoutingOverride::LocalNodeOnly,
),
b"ft.search" => (
MsgType::ReqRedisFtSearch,
true,
false,
RoutingOverride::LocalNodeOnly,
),
b"ft.info" => (
MsgType::ReqRedisFtInfo,
true,
false,
RoutingOverride::LocalNodeOnly,
),
b"ft.list" | b"ft._list" => (
MsgType::ReqRedisFtList,
true,
false,
RoutingOverride::LocalNodeOnly,
),
b"ft.dropindex" => (
MsgType::ReqRedisFtDropindex,
false,
false,
RoutingOverride::LocalNodeOnly,
),
b"ft.regex" => (
MsgType::ReqRedisFtRegex,
true,
false,
RoutingOverride::LocalNodeOnly,
),
b"dyno_config:conn_consistency" => (
MsgType::HackSettingConnConsistency,
false,
false,
RoutingOverride::None,
),
_ => return None,
};
Some((
ty,
CommandTraits {
is_read,
quit,
routing,
},
))
}
#[must_use]
pub fn error_lookup(token: &[u8]) -> Option<MsgType> {
match token {
b"-ERR" => Some(MsgType::RspRedisErrorErr),
b"-OOM" => Some(MsgType::RspRedisErrorOom),
b"-BUSY" => Some(MsgType::RspRedisErrorBusy),
b"-NOAUTH" => Some(MsgType::RspRedisErrorNoauth),
b"-LOADING" => Some(MsgType::RspRedisErrorLoading),
b"-BUSYKEY" => Some(MsgType::RspRedisErrorBusykey),
b"-MISCONF" => Some(MsgType::RspRedisErrorMisconf),
b"-NOSCRIPT" => Some(MsgType::RspRedisErrorNoscript),
b"-READONLY" => Some(MsgType::RspRedisErrorReadonly),
b"-WRONGTYPE" => Some(MsgType::RspRedisErrorWrongtype),
b"-EXECABORT" => Some(MsgType::RspRedisErrorExecabort),
b"-MASTERDOWN" => Some(MsgType::RspRedisErrorMasterdown),
b"-NOREPLICAS" => Some(MsgType::RspRedisErrorNoreplicas),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lookup_get_set() {
let (ty, t) = lookup(b"GET").unwrap();
assert_eq!(ty, MsgType::ReqRedisGet);
assert!(t.is_read);
let (ty, t) = lookup(b"set").unwrap();
assert_eq!(ty, MsgType::ReqRedisSet);
assert!(!t.is_read);
}
#[test]
fn lookup_unknown() {
assert!(lookup(b"NOTACOMMAND").is_none());
}
#[test]
fn classify_get_is_arg0() {
assert_eq!(classify(MsgType::ReqRedisGet), CommandClass::Arg0);
}
#[test]
fn classify_mset_is_argkvx() {
assert_eq!(classify(MsgType::ReqRedisMset), CommandClass::ArgKvX);
}
#[test]
fn error_lookup_wrongtype() {
assert_eq!(
error_lookup(b"-WRONGTYPE"),
Some(MsgType::RspRedisErrorWrongtype)
);
}
}