use super::parser::resp_ext::RespVecExt;
pub const RESP_OK: &[u8] = b"+OK\r\n";
pub const RESP_PONG: &[u8] = b"+PONG\r\n";
pub const RESP_EMPTYLIST: &[u8] = b"*0\r\n";
pub const RESP_RETURN_VAL_0: &[u8] = b":0\r\n";
pub const RESP_RETURN_VAL_1: &[u8] = b":1\r\n";
pub const RESP_RETURN_VAL_N1: &[u8] = b":-1\r\n";
pub const RESP_RETURN_VAL_N2: &[u8] = b":-2\r\n";
pub const RESP_ERR_NOAUTH: &str = "NOAUTH Authentication required.";
pub const RESP_ERR_WRONG_TYPE: &str =
"WRONGTYPE Operation against a key holding the wrong kind of value.";
pub const RESP_ERR_GENERIC_UNK_CMD: &str = "ERR unknown command";
pub const RESP_ERR_GENERIC_CLUSTER_DISABLED: &str =
"ERR This instance has cluster support disabled";
pub const RESP_ERR_GENERIC_NOSUCHKEY: &str = "ERR no such key";
pub const RESP_ERR_GENERIC_INVALIDEXP_IN_SET: &str = "ERR invalid expire time in 'set' command";
pub const RESP_ERR_GENERIC_SYNTAX_ERROR: &str = "ERR syntax error";
pub const RESP_ERR_GENERIC_NAN_INFINITY_INCR: &str = "ERR increment would produce NaN or Infinity";
pub const RESP_ERR_GENERIC_OFFSETOUTOFRANGE: &str = "ERR offset is out of range";
pub const RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER: &str =
"ERR value is not an integer or out of range.";
pub const RESP_ERR_GENERIC_VALUE_IS_OUT_OF_RANGE: &str =
"ERR value is out of range, must be positive.";
pub const RESP_ERR_GENERIC_BIT_IS_NOT_INTEGER: &str = "ERR bit is not an integer or out of range";
pub const RESP_ERR_GENERIC_BITOFFSET_IS_NOT_INTEGER: &str =
"ERR bit offset is not an integer or out of range";
pub const RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER_NO_PERIOD: &str =
"ERR value is not an integer or out of range";
pub const RESP_ERR_PROTOCOL_VALUE_IS_NOT_INTEGER: &str =
"ERR Protocol version is not an integer or out of range.";
pub const RESP_ERR_UNSUPPORTED_PROTOCOL_VERSION: &str = "ERR Unsupported protocol version";
pub const RESP_ERR_NOT_VALID_FLOAT: &str = "ERR value is not a valid float";
pub const RESP_ERR_TIMEOUT_NOT_VALID_FLOAT: &str = "ERR timeout is not a float or out of range";
pub const RESP_WRONGPASS_INVALID_PASSWORD: &str = "WRONGPASS Invalid password";
pub const RESP_WRONGPASS_INVALID_USERNAME_PASSWORD: &str =
"WRONGPASS Invalid username/password combination";
pub const RESP_ERR_BUSSYKEY: &str = "BUSYKEY Target key name already exists.";
pub const RESP_ERR_INVALID_EXPIRE_TIME: &str = "ERR invalid expire time, must be >= 0";
pub const RESP_ERR_HCOLLECT_ALREADY_IN_PROGRESS: &str = "ERR HCOLLECT scan already in progress";
pub const RESP_ERR_ZCOLLECT_ALREADY_IN_PROGRESS: &str = "ERR ZCOLLECT scan already in progress";
pub const RESP_ERR_OBJECT_FREQ_UNSUPPORTED: &str = "ERR OBJECT FREQ is not supported: Garnet does not track access frequency (no LFU maxmemory policy).";
pub const RESP_INVALID_COMMAND_SPECIFIED: &str = "Invalid command specified";
pub const RESP_COMMAND_HAS_NO_KEY_ARGS: &str = "The command has no key arguments";
pub const RESP_ERR_NOT_SUPPORTED_RESP2: &str = "ERR command not supported in RESP2";
pub const RESP_ERR_ASYNC_PROTOCOL_CHANGE: &str =
"ERR protocol change is not allowed with pending async operations";
pub const RESP_ERR_CANNOT_LIST_CLIENTS: &str = "ERR Clients cannot be listed.";
pub const RESP_ERR_UBLOCKING_CLINET: &str = "ERR Unable to unblock client because of error.";
pub const RESP_ERR_NO_SUCH_CLIENT: &str = "ERR No such client";
pub const RESP_ERR_INVALID_CLIENT_ID: &str = "ERR Invalid client ID";
pub const RESP_ERR_INVALID_CLIENT_NAME: &str =
"ERR Client names cannot contain spaces, newlines or special characters.";
pub const RESP_ERR_INVALID_CLIENT_UNBLOCK_REASON: &str =
"ERR CLIENT UNBLOCK reason should be TIMEOUT or ERROR";
pub const RESP_ERR_EXPDELSCAN_INVALID: &str =
"ERR Cannot execute EXPDELSCAN with background expired key deletion scan enabled";
pub const RESP_ERR_CHECKPOINT_ALREADY_IN_PROGRESS: &str = "ERR checkpoint already in progress";
pub const RESP_ERR_DB_INDEX_OUT_OF_RANGE: &str = "ERR DB index is out of range.";
pub const RESP_ERR_DB_ID_CLUSTER_MODE: &str =
"ERR specifying non-zero DBID is not allowed in cluster mode";
pub const RESP_ERR_FLUSHALL_READONLY_REPLICA: &str =
"ERR You can't write against a read only replica.";
pub const GENERIC_ERR_WRONG_NUM_ARGS: &str = "ERR wrong number of arguments for '{0}' command";
pub const RESP_ERR_GENERIC_NUMKEYS: &str = "ERR numkeys should be greater than 0";
pub const RESP_ERR_GENERIC_INVALIDCURSOR: &str = "ERR invalid cursor";
pub const RESP_ERR_GENERIC_UNSUPPORTED_OPERATION: &str = "ERR unsupported operation";
pub const GENERIC_ERR_UNSUPPORTED_OPTION: &str = "ERR Unsupported option {0}";
pub const GENERIC_ERR_UNKNOWN_SUB_COMMAND: &str = "ERR unknown subcommand '{0}'. Try {1} HELP";
pub const GENERIC_ERR_UNKNOWN_SUB_COMMAND_NO_HELP: &str = "ERR unknown subcommand '{0}'.";
pub const GENERIC_ERR_UNKNOWN_SUB_COMMAND_OR_WRONG_NUM_ARGS: &str =
"ERR unknown subcommand or wrong number of arguments for '{0}'. Try {1} HELP";
pub const GENERIC_ERR_COMMAND_DISALLOWED_WITH_OPTION: &str = "ERR {0} command not allowed. If the {1} option is set to \"local\", you can run it from a local connection, otherwise you need to set this option in the configuration file, and then restart the server.";
pub const GENERIC_UNKNOWN_CLIENT_TYPE: &str = "ERR Unknown client type '{0}'";
pub const GENERIC_ERR_DUPLICATE_FILTER: &str = "ERR Filter '{0}' defined multiple times";
pub const GENERIC_ERR_SHOULD_BE_GREATER_THAN_ZERO: &str = "ERR {0} should be greater than 0";
pub const RESP_ERR_GENERIC_INSTANTIATING_CLASS: &str =
"ERR unable to instantiate one or more classes from given assemblies.";
pub const RESP_ERR_GENERIC_REGISTERCS_UNSUPPORTED_CLASS: &str =
"ERR unable to register one or more unsupported classes.";
pub const GENERIC_ERR_UNKNOWN_OPTION_CONFIG_SET: &str =
"ERR Unknown option or number of arguments for CONFIG SET - '{0}'";
#[inline]
pub fn write_error_raw(output: &mut Vec<u8>, msg: &str) {
output.push(b'-');
output.extend_from_slice(msg.as_bytes());
output.extend_from_slice(b"\r\n");
}
pub fn abort_with_wrong_number_of_arguments(output: &mut Vec<u8>, cmd_name: &str) {
write_error_raw(output, &GENERIC_ERR_WRONG_NUM_ARGS.replace("{0}", cmd_name));
}
pub fn abort_with_error_message(output: &mut Vec<u8>, error_message: &str) {
write_error_raw(output, error_message);
}
#[inline]
pub fn write_raw(output: &mut Vec<u8>, frame: &[u8]) {
output.extend_from_slice(frame);
}
#[inline]
pub fn write_map_len_resp2(output: &mut Vec<u8>, len: usize) {
output.write_resp_array_len(len * 2);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn write_error_raw_frames_message() {
let mut out = Vec::new();
write_error_raw(&mut out, RESP_ERR_GENERIC_NOSUCHKEY);
assert_eq!(out, b"-ERR no such key\r\n");
}
#[test]
fn wrong_num_args_formats_command_name() {
let mut out = Vec::new();
abort_with_wrong_number_of_arguments(&mut out, "GETEX");
assert_eq!(
out,
b"-ERR wrong number of arguments for 'GETEX' command\r\n"
);
}
#[test]
fn unknown_subcommand_formats() {
assert_eq!(
GENERIC_ERR_UNKNOWN_SUB_COMMAND_NO_HELP.replace("{0}", "NOPE"),
"ERR unknown subcommand 'NOPE'."
);
assert_eq!(
GENERIC_ERR_UNKNOWN_SUB_COMMAND
.replace("{0}", "NOPE")
.replace("{1}", "CLUSTER"),
"ERR unknown subcommand 'NOPE'. Try CLUSTER HELP"
);
}
#[test]
fn map_len_resp2_doubles_array_length() {
let mut out = Vec::new();
write_map_len_resp2(&mut out, 2);
assert_eq!(out, b"*4\r\n");
}
#[test]
fn raw_frames_passthrough() {
let mut out = Vec::new();
write_raw(&mut out, RESP_OK);
write_raw(&mut out, RESP_RETURN_VAL_N2);
assert_eq!(out, b"+OK\r\n:-2\r\n");
}
}