use std::{collections::HashMap, str::FromStr};
use crate::{
prepare_command,
resp::{
cmd, CommandArg, CommandArgs, FromKeyValueValueArray, FromSingleValueArray, FromValue,
HashMapExt, IntoArgs, KeyValueArgOrCollection, SingleArgOrCollection, Value,
},
Error, PreparedCommand, Result,
};
pub trait ServerCommands {
fn acl_cat<C, CC>(&mut self, options: AclCatOptions) -> PreparedCommand<Self, CC>
where
Self: Sized,
C: FromValue,
CC: FromSingleValueArray<C>,
{
prepare_command(self, cmd("ACL").arg("CAT").arg(options))
}
fn acl_deluser<U, UU>(&mut self, usernames: UU) -> PreparedCommand<Self, usize>
where
Self: Sized,
U: Into<CommandArg>,
UU: SingleArgOrCollection<U>,
{
prepare_command(self, cmd("ACL").arg("DELUSER").arg(usernames))
}
fn acl_dryrun<U, C, R>(
&mut self,
username: U,
command: C,
options: AclDryRunOptions,
) -> PreparedCommand<Self, R>
where
Self: Sized,
U: Into<CommandArg>,
C: Into<CommandArg>,
R: FromValue,
{
prepare_command(
self,
cmd("ACL")
.arg("DRYRUN")
.arg(username)
.arg(command)
.arg(options),
)
}
fn acl_genpass<R: FromValue>(&mut self, options: AclGenPassOptions) -> PreparedCommand<Self, R>
where
Self: Sized,
{
prepare_command(self, cmd("ACL").arg("GENPASS").arg(options))
}
fn acl_getuser<U, RR>(&mut self, username: U) -> PreparedCommand<Self, RR>
where
Self: Sized,
U: Into<CommandArg>,
RR: FromKeyValueValueArray<String, Value>,
{
prepare_command(self, cmd("ACL").arg("GETUSER").arg(username))
}
fn acl_list(&mut self) -> PreparedCommand<Self, Vec<String>>
where
Self: Sized,
{
prepare_command(self, cmd("ACL").arg("LIST"))
}
fn acl_load(&mut self) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("ACL").arg("LOAD"))
}
fn acl_log<EE>(&mut self, options: AclLogOptions) -> PreparedCommand<Self, Vec<EE>>
where
Self: Sized,
EE: FromKeyValueValueArray<String, Value>,
{
prepare_command(self, cmd("ACL").arg("LOG").arg(options))
}
fn acl_save(&mut self) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("ACL").arg("SAVE"))
}
fn acl_setuser<U, R, RR>(&mut self, username: U, rules: RR) -> PreparedCommand<Self, ()>
where
Self: Sized,
U: Into<CommandArg>,
R: Into<CommandArg>,
RR: SingleArgOrCollection<R>,
{
prepare_command(self, cmd("ACL").arg("SETUSER").arg(username).arg(rules))
}
fn acl_users<U, UU>(&mut self) -> PreparedCommand<Self, UU>
where
Self: Sized,
U: FromValue,
UU: FromSingleValueArray<U>,
{
prepare_command(self, cmd("ACL").arg("USERS"))
}
fn acl_whoami<U: FromValue>(&mut self) -> PreparedCommand<Self, U>
where
Self: Sized,
{
prepare_command(self, cmd("ACL").arg("WHOAMI"))
}
fn command(&mut self) -> PreparedCommand<Self, Vec<CommandInfo>>
where
Self: Sized,
{
prepare_command(self, cmd("COMMAND"))
}
fn command_count(&mut self) -> PreparedCommand<Self, usize>
where
Self: Sized,
{
prepare_command(self, cmd("COMMAND").arg("COUNT"))
}
fn command_docs<N, NN, DD>(&mut self, command_names: NN) -> PreparedCommand<Self, DD>
where
Self: Sized,
N: Into<CommandArg>,
NN: SingleArgOrCollection<N>,
DD: FromKeyValueValueArray<String, CommandDoc>,
{
prepare_command(self, cmd("COMMAND").arg("DOCS").arg(command_names))
}
fn command_getkeys<A, AA, KK>(&mut self, args: AA) -> PreparedCommand<Self, KK>
where
Self: Sized,
A: Into<CommandArg>,
AA: SingleArgOrCollection<A>,
KK: FromSingleValueArray<String>,
{
prepare_command(self, cmd("COMMAND").arg("GETKEYS").arg(args))
}
fn command_getkeysandflags<A, AA, KK>(&mut self, args: AA) -> PreparedCommand<Self, KK>
where
Self: Sized,
A: Into<CommandArg>,
AA: SingleArgOrCollection<A>,
KK: FromKeyValueValueArray<String, Vec<String>>,
{
prepare_command(self, cmd("COMMAND").arg("GETKEYSANDFLAGS").arg(args))
}
fn command_info<N, NN>(&mut self, command_names: NN) -> PreparedCommand<Self, Vec<CommandInfo>>
where
Self: Sized,
N: Into<CommandArg>,
NN: SingleArgOrCollection<N>,
{
prepare_command(self, cmd("COMMAND").arg("INFO").arg(command_names))
}
fn command_list<CC>(&mut self, options: CommandListOptions) -> PreparedCommand<Self, CC>
where
Self: Sized,
CC: FromSingleValueArray<String>,
{
prepare_command(self, cmd("COMMAND").arg("LIST").arg(options))
}
#[must_use]
fn config_get<P, PP, V, VV>(&mut self, params: PP) -> PreparedCommand<Self, VV>
where
Self: Sized,
P: Into<CommandArg>,
PP: SingleArgOrCollection<P>,
V: FromValue,
VV: FromKeyValueValueArray<String, V>,
{
prepare_command(self, cmd("CONFIG").arg("GET").arg(params))
}
#[must_use]
fn config_resetstat(&mut self) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("CONFIG").arg("RESETSTAT"))
}
#[must_use]
fn config_rewrite(&mut self) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("CONFIG").arg("REWRITE"))
}
#[must_use]
fn config_set<P, V, C>(&mut self, configs: C) -> PreparedCommand<Self, ()>
where
Self: Sized,
P: Into<CommandArg>,
V: Into<CommandArg>,
C: KeyValueArgOrCollection<P, V>,
{
prepare_command(self, cmd("CONFIG").arg("SET").arg(configs))
}
#[must_use]
fn dbsize(&mut self) -> PreparedCommand<Self, usize>
where
Self: Sized,
{
prepare_command(self, cmd("DBSIZE"))
}
#[must_use]
fn failover(&mut self, options: FailOverOptions) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("FAILOVER").arg(options))
}
#[must_use]
fn flushdb(&mut self, flushing_mode: FlushingMode) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("FLUSHDB").arg(flushing_mode))
}
#[must_use]
fn flushall(&mut self, flushing_mode: FlushingMode) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("FLUSHALL").arg(flushing_mode))
}
#[must_use]
fn info<SS>(&mut self, sections: SS) -> PreparedCommand<Self, String>
where
Self: Sized,
SS: SingleArgOrCollection<InfoSection>,
{
prepare_command(self, cmd("INFO").arg(sections))
}
#[must_use]
fn lastsave(&mut self) -> PreparedCommand<Self, u64>
where
Self: Sized,
{
prepare_command(self, cmd("LASTSAVE"))
}
#[must_use]
fn latency_doctor(&mut self) -> PreparedCommand<Self, String>
where
Self: Sized,
{
prepare_command(self, cmd("LATENCY").arg("DOCTOR"))
}
#[must_use]
fn latency_graph(&mut self, event: LatencyHistoryEvent) -> PreparedCommand<Self, String>
where
Self: Sized,
{
prepare_command(self, cmd("LATENCY").arg("GRAPH").arg(event))
}
#[must_use]
fn latency_histogram<C, CC, RR>(&mut self, commands: CC) -> PreparedCommand<Self, RR>
where
Self: Sized,
C: Into<CommandArg>,
CC: SingleArgOrCollection<C>,
RR: FromKeyValueValueArray<String, CommandHistogram>,
{
prepare_command(self, cmd("LATENCY").arg("HISTOGRAM").arg(commands))
}
#[must_use]
fn latency_history<RR>(&mut self, event: LatencyHistoryEvent) -> PreparedCommand<Self, RR>
where
Self: Sized,
RR: FromSingleValueArray<(u32, u32)>,
{
prepare_command(self, cmd("LATENCY").arg("HISTORY").arg(event))
}
#[must_use]
fn latency_latest<RR>(&mut self) -> PreparedCommand<Self, RR>
where
Self: Sized,
RR: FromSingleValueArray<(String, u32, u32, u32)>,
{
prepare_command(self, cmd("LATENCY").arg("LATEST"))
}
#[must_use]
fn latency_reset<EE>(&mut self, events: EE) -> PreparedCommand<Self, usize>
where
Self: Sized,
EE: SingleArgOrCollection<LatencyHistoryEvent>,
{
prepare_command(self, cmd("LATENCY").arg("RESET").arg(events))
}
#[must_use]
fn lolwut(&mut self, options: LolWutOptions) -> PreparedCommand<Self, String>
where
Self: Sized,
{
prepare_command(self, cmd("LOLWUT").arg(options))
}
#[must_use]
fn memory_doctor(&mut self) -> PreparedCommand<Self, String>
where
Self: Sized,
{
prepare_command(self, cmd("MEMORY").arg("DOCTOR"))
}
#[must_use]
fn memory_malloc_stats(&mut self) -> PreparedCommand<Self, String>
where
Self: Sized,
{
prepare_command(self, cmd("MEMORY").arg("MALLOC-STATS"))
}
#[must_use]
fn memory_purge(&mut self) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("MEMORY").arg("PURGE"))
}
#[must_use]
fn memory_stats(&mut self) -> PreparedCommand<Self, MemoryStats>
where
Self: Sized,
{
prepare_command(self, cmd("MEMORY").arg("STATS"))
}
#[must_use]
fn memory_usage<K>(
&mut self,
key: K,
options: MemoryUsageOptions,
) -> PreparedCommand<Self, Option<usize>>
where
Self: Sized,
K: Into<CommandArg>,
{
prepare_command(self, cmd("MEMORY").arg("USAGE").arg(key).arg(options))
}
#[must_use]
fn module_list<MM>(&mut self) -> PreparedCommand<Self, MM>
where
Self: Sized,
MM: FromSingleValueArray<ModuleInfo>,
{
prepare_command(self, cmd("MODULE").arg("LIST"))
}
#[must_use]
fn module_load<P>(&mut self, path: P, options: ModuleLoadOptions) -> PreparedCommand<Self, ()>
where
Self: Sized,
P: Into<CommandArg>,
{
prepare_command(self, cmd("MODULE").arg("LOADEX").arg(path).arg(options))
}
#[must_use]
fn module_unload<N>(&mut self, name: N) -> PreparedCommand<Self, ()>
where
Self: Sized,
N: Into<CommandArg>,
{
prepare_command(self, cmd("MODULE").arg("UNLOAD").arg(name))
}
#[must_use]
fn replicaof(&mut self, options: ReplicaOfOptions) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("REPLICAOF").arg(options))
}
#[must_use]
fn role(&mut self) -> PreparedCommand<Self, RoleResult>
where
Self: Sized,
{
prepare_command(self, cmd("ROLE"))
}
#[must_use]
fn save(&mut self) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("SAVE"))
}
#[must_use]
fn shutdown(&mut self, options: ShutdownOptions) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("SHUTDOWN").arg(options))
}
#[must_use]
fn slowlog_get(&mut self, options: SlowLogOptions) -> PreparedCommand<Self, Vec<SlowLogEntry>>
where
Self: Sized,
{
prepare_command(self, cmd("SLOWLOG").arg("GET").arg(options))
}
#[must_use]
fn slowlog_len(&mut self) -> PreparedCommand<Self, usize>
where
Self: Sized,
{
prepare_command(self, cmd("SLOWLOG").arg("LEN"))
}
#[must_use]
fn slowlog_reset(&mut self) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("SLOWLOG").arg("RESET"))
}
#[must_use]
fn swapdb(&mut self, index1: usize, index2: usize) -> PreparedCommand<Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("SWAPDB").arg(index1).arg(index2))
}
#[must_use]
fn time(&mut self) -> PreparedCommand<Self, (u32, u32)>
where
Self: Sized,
{
prepare_command(self, cmd("TIME"))
}
}
pub enum FlushingMode {
Default,
Async,
Sync,
}
impl Default for FlushingMode {
fn default() -> Self {
FlushingMode::Default
}
}
impl IntoArgs for FlushingMode {
fn into_args(self, args: CommandArgs) -> CommandArgs {
match self {
FlushingMode::Default => args,
FlushingMode::Async => args.arg("ASYNC"),
FlushingMode::Sync => args.arg("SYNC"),
}
}
}
#[derive(Default)]
pub struct AclCatOptions {
command_args: CommandArgs,
}
impl AclCatOptions {
#[must_use]
pub fn category_name<C: Into<CommandArg>>(self, category_name: C) -> Self {
Self {
command_args: self.command_args.arg(category_name),
}
}
}
impl IntoArgs for AclCatOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
#[derive(Default)]
pub struct AclDryRunOptions {
command_args: CommandArgs,
}
impl AclDryRunOptions {
#[must_use]
pub fn arg<A, AA>(self, args: AA) -> Self
where
A: Into<CommandArg>,
AA: SingleArgOrCollection<A>,
{
Self {
command_args: self.command_args.arg(args),
}
}
}
impl IntoArgs for AclDryRunOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
#[derive(Default)]
pub struct AclGenPassOptions {
command_args: CommandArgs,
}
impl AclGenPassOptions {
#[must_use]
pub fn bits(self, bits: usize) -> Self {
Self {
command_args: self.command_args.arg(bits),
}
}
}
impl IntoArgs for AclGenPassOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
#[derive(Default)]
pub struct AclLogOptions {
command_args: CommandArgs,
}
impl AclLogOptions {
#[must_use]
pub fn count(self, count: usize) -> Self {
Self {
command_args: self.command_args.arg(count),
}
}
#[must_use]
pub fn reset(self) -> Self {
Self {
command_args: self.command_args.arg("RESET"),
}
}
}
impl IntoArgs for AclLogOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
#[derive(Debug, Clone)]
pub struct CommandInfo {
pub name: String,
pub arity: isize,
pub flags: Vec<String>,
pub first_key: usize,
pub last_key: isize,
pub step: usize,
pub acl_categories: Vec<String>,
pub command_tips: Vec<CommandTip>,
pub key_specifications: Vec<KeySpecification>,
pub sub_commands: Vec<CommandInfo>,
}
impl FromValue for CommandInfo {
fn from_value(value: Value) -> Result<Self> {
let values: Vec<Value> = value.into()?;
let mut iter = values.into_iter();
match (
iter.next(),
iter.next(),
iter.next(),
iter.next(),
iter.next(),
iter.next(),
iter.next(),
iter.next(),
iter.next(),
iter.next(),
) {
(
Some(name),
Some(arity),
Some(flags),
Some(first_key),
Some(last_key),
Some(step),
Some(acl_categories),
Some(command_tips),
Some(key_specifications),
Some(sub_commands),
) => Ok(Self {
name: name.into()?,
arity: arity.into()?,
flags: flags.into()?,
first_key: first_key.into()?,
last_key: last_key.into()?,
step: step.into()?,
acl_categories: acl_categories.into()?,
command_tips: command_tips.into()?,
key_specifications: key_specifications.into()?,
sub_commands: sub_commands.into()?,
}),
(
Some(name),
Some(arity),
Some(flags),
Some(first_key),
Some(last_key),
Some(step),
Some(acl_categories),
None,
None,
None,
) => Ok(Self {
name: name.into()?,
arity: arity.into()?,
flags: flags.into()?,
first_key: first_key.into()?,
last_key: last_key.into()?,
step: step.into()?,
acl_categories: acl_categories.into()?,
command_tips: Vec::new(),
key_specifications: Vec::new(),
sub_commands: Vec::new(),
}),
(
Some(name),
Some(arity),
Some(flags),
Some(first_key),
Some(last_key),
Some(step),
None,
None,
None,
None,
) => Ok(Self {
name: name.into()?,
arity: arity.into()?,
flags: flags.into()?,
first_key: first_key.into()?,
last_key: last_key.into()?,
step: step.into()?,
acl_categories: Vec::new(),
command_tips: Vec::new(),
key_specifications: Vec::new(),
sub_commands: Vec::new(),
}),
_ => Err(Error::Client(
"Cannot parse CommandInfo from result".to_owned(),
)),
}
}
}
#[derive(Debug, Clone)]
pub enum CommandTip {
NonDeterministricOutput,
NonDeterministricOutputOrder,
RequestPolicy(RequestPolicy),
ResponsePolicy(ResponsePolicy),
}
impl FromValue for CommandTip {
fn from_value(value: Value) -> Result<Self> {
let tip: String = value.into()?;
match tip.as_str() {
"nondeterministic_output" => Ok(CommandTip::NonDeterministricOutput),
"nondeterministic_output_order" => Ok(CommandTip::NonDeterministricOutputOrder),
_ => {
let mut parts = tip.split(':');
match (parts.next(), parts.next(), parts.next()) {
(Some("request_policy"), Some(policy), None) => {
Ok(CommandTip::RequestPolicy(RequestPolicy::from_str(policy)?))
}
(Some("response_policy"), Some(policy), None) => Ok(
CommandTip::ResponsePolicy(ResponsePolicy::from_str(policy)?),
),
_ => Err(Error::Client(
"Cannot parse CommandTip from result".to_owned(),
)),
}
}
}
}
}
#[derive(Debug, Clone)]
pub enum RequestPolicy {
AllNodes,
AllShards,
MultiShard,
Special,
}
impl FromStr for RequestPolicy {
type Err = Error;
fn from_str(str: &str) -> Result<Self> {
match str {
"all_nodes" => Ok(RequestPolicy::AllNodes),
"all_shards" => Ok(RequestPolicy::AllShards),
"multi_shard" => Ok(RequestPolicy::MultiShard),
"special" => Ok(RequestPolicy::Special),
_ => Err(Error::Client(
"Cannot parse RequestPolicy from result".to_owned(),
)),
}
}
}
#[derive(Debug, Clone)]
pub enum ResponsePolicy {
OneSucceeded,
AllSucceeded,
AggLogicalAnd,
AggLogicalOr,
AggMin,
AggMax,
AggSum,
Special,
}
impl FromStr for ResponsePolicy {
type Err = Error;
fn from_str(str: &str) -> Result<Self> {
match str {
"one_succeeded" => Ok(ResponsePolicy::OneSucceeded),
"all_succeeded" => Ok(ResponsePolicy::AllSucceeded),
"agg_logical_and" => Ok(ResponsePolicy::AggLogicalAnd),
"agg_logical_or" => Ok(ResponsePolicy::AggLogicalOr),
"agg_min" => Ok(ResponsePolicy::AggMin),
"agg_max" => Ok(ResponsePolicy::AggMax),
"agg_sum" => Ok(ResponsePolicy::AggSum),
"special" => Ok(ResponsePolicy::Special),
_ => Err(Error::Client(
"Cannot parse ResponsePolicy from result".to_owned(),
)),
}
}
}
#[derive(Debug, Clone)]
pub struct KeySpecification {
pub begin_search: BeginSearch,
pub find_keys: FindKeys,
pub flags: Vec<String>,
pub notes: String,
}
impl FromValue for KeySpecification {
fn from_value(value: Value) -> Result<Self> {
let mut values: HashMap<String, Value> = value.into()?;
let notes: String = match values.remove("notes") {
Some(notes) => notes.into()?,
None => "".to_owned(),
};
Ok(Self {
begin_search: values.remove_with_result("begin_search")?.into()?,
find_keys: values.remove_with_result("find_keys")?.into()?,
flags: values.remove_with_result("flags")?.into()?,
notes,
})
}
}
#[derive(Debug, Clone)]
pub enum BeginSearch {
Index(usize),
Keyword { keyword: String, start_from: isize },
Unknown,
}
impl FromValue for BeginSearch {
fn from_value(value: Value) -> Result<Self> {
let mut values: HashMap<String, Value> = value.into()?;
let type_: String = values.remove_with_result("type")?.into()?;
match type_.as_str() {
"index" => {
let mut spec: HashMap<String, Value> = values.remove_with_result("spec")?.into()?;
Ok(BeginSearch::Index(
spec.remove_with_result("index")?.into()?,
))
}
"keyword" => {
let mut spec: HashMap<String, Value> = values.remove_with_result("spec")?.into()?;
Ok(BeginSearch::Keyword {
keyword: spec.remove_with_result("keyword")?.into()?,
start_from: spec.remove_with_result("startfrom")?.into()?,
})
}
"unknown" => Ok(BeginSearch::Unknown),
_ => Err(Error::Client(
"Cannot parse BeginSearch from result".to_owned(),
)),
}
}
}
#[derive(Debug, Clone)]
pub enum FindKeys {
Range {
last_key: isize,
key_step: usize,
limit: usize,
},
KeyEnum {
key_num_idx: usize,
first_key: usize,
key_step: usize,
},
Unknown,
}
impl FromValue for FindKeys {
fn from_value(value: Value) -> Result<Self> {
let mut values: HashMap<String, Value> = value.into()?;
let type_: String = values.remove_with_result("type")?.into()?;
match type_.as_str() {
"range" => {
let mut spec: HashMap<String, Value> = values.remove_with_result("spec")?.into()?;
Ok(FindKeys::Range {
last_key: spec.remove_with_result("lastkey")?.into()?,
key_step: spec.remove_with_result("keystep")?.into()?,
limit: spec.remove_with_result("limit")?.into()?,
})
}
"keynum" => {
let mut spec: HashMap<String, Value> = values.remove_with_result("spec")?.into()?;
Ok(FindKeys::KeyEnum {
key_num_idx: spec.remove_with_result("keynumidx")?.into()?,
first_key: spec.remove_with_result("firstkey")?.into()?,
key_step: spec.remove_with_result("keystep")?.into()?,
})
}
"unknown" => Ok(FindKeys::Unknown),
_ => Err(Error::Client(
"Cannot parse BeginSearch from result".to_owned(),
)),
}
}
}
#[derive(Debug, Default)]
pub struct CommandDoc {
pub summary: String,
pub since: String,
pub group: String,
pub complexity: String,
pub doc_flags: Vec<CommandDocFlag>,
pub deprecated_since: String,
pub replaced_by: String,
pub history: Vec<HistoricalNote>,
pub arguments: Vec<CommandArgument>,
}
impl FromValue for CommandDoc {
fn from_value(value: Value) -> Result<Self> {
let mut values: HashMap<String, Value> = value.into()?;
Ok(Self {
summary: values.remove_with_result("summary")?.into()?,
since: values.remove_with_result("since")?.into()?,
group: values.remove_with_result("group")?.into()?,
complexity: values.remove_with_result("complexity")?.into()?,
doc_flags: values.remove_or_default("doc_flags").into()?,
deprecated_since: values.remove_or_default("deprecated_since").into()?,
replaced_by: values.remove_or_default("replaced_by").into()?,
history: values.remove_or_default("history").into()?,
arguments: values.remove_with_result("arguments")?.into()?,
})
}
}
#[derive(Debug)]
pub enum CommandDocFlag {
Deprecated,
SystemCommand,
}
impl FromValue for CommandDocFlag {
fn from_value(value: Value) -> Result<Self> {
let f: String = value.into()?;
match f.as_str() {
"deprecated" => Ok(CommandDocFlag::Deprecated),
"syscmd" => Ok(CommandDocFlag::SystemCommand),
_ => Err(Error::Client(
"Cannot parse CommandDocFlag from result".to_owned(),
)),
}
}
}
#[derive(Debug)]
pub struct HistoricalNote {
pub version: String,
pub description: String,
}
impl FromValue for HistoricalNote {
fn from_value(value: Value) -> Result<Self> {
let (version, description): (String, String) = value.into()?;
Ok(Self {
version,
description,
})
}
}
#[derive(Debug)]
pub struct CommandArgument {
pub name: String,
pub display_text: String,
pub type_: CommandArgumentType,
pub key_spec_index: usize,
pub token: String,
pub summary: String,
pub since: String,
pub deprecated_since: String,
pub flags: Vec<ArgumentFlag>,
pub value: Vec<String>,
}
impl FromValue for CommandArgument {
fn from_value(value: Value) -> Result<Self> {
let mut values: HashMap<String, Value> = value.into()?;
Ok(Self {
name: values.remove_with_result("name")?.into()?,
display_text: values.remove_or_default("display_text").into()?,
type_: values.remove_with_result("type")?.into()?,
key_spec_index: values.remove_or_default("key_spec_index").into()?,
token: values.remove_or_default("token").into()?,
summary: values.remove_or_default("summary").into()?,
since: values.remove_or_default("since").into()?,
deprecated_since: values.remove_or_default("deprecated_since").into()?,
flags: values.remove_or_default("flags").into()?,
value: match values.remove_or_default("value") {
value @ Value::BulkString(_) => vec![value.into()?],
value @ Value::Array(_) => value.into()?,
_ => {
return Err(Error::Client(
"Cannot parse CommandArgument from result".to_owned(),
))
}
},
})
}
}
#[derive(Debug)]
pub enum CommandArgumentType {
String,
Integer,
Double,
Key,
Pattern,
UnixTime,
PureToken,
OneOf,
Block,
}
impl FromValue for CommandArgumentType {
fn from_value(value: Value) -> Result<Self> {
let t: String = value.into()?;
match t.as_str() {
"string" => Ok(CommandArgumentType::String),
"integer" => Ok(CommandArgumentType::Integer),
"double" => Ok(CommandArgumentType::Double),
"key" => Ok(CommandArgumentType::Key),
"pattern" => Ok(CommandArgumentType::Pattern),
"unix-time" => Ok(CommandArgumentType::UnixTime),
"pure-token" => Ok(CommandArgumentType::PureToken),
"oneof" => Ok(CommandArgumentType::OneOf),
"block" => Ok(CommandArgumentType::Block),
_ => Err(Error::Client(
"Cannot parse CommandArgumentType from result".to_owned(),
)),
}
}
}
#[derive(Debug)]
pub enum ArgumentFlag {
Optional,
Multiple,
MultipleToken,
}
impl FromValue for ArgumentFlag {
fn from_value(value: Value) -> Result<Self> {
let f: String = value.into()?;
match f.as_str() {
"optional" => Ok(ArgumentFlag::Optional),
"multiple" => Ok(ArgumentFlag::Multiple),
"multiple-token" => Ok(ArgumentFlag::MultipleToken),
_ => Err(Error::Client(
"Cannot parse ArgumentFlag from result".to_owned(),
)),
}
}
}
#[derive(Default)]
pub struct CommandListOptions {
command_args: CommandArgs,
}
impl CommandListOptions {
#[must_use]
pub fn filter_by_module_name<M: Into<CommandArg>>(self, module_name: M) -> Self {
Self {
command_args: self
.command_args
.arg("FILTERBY")
.arg("MODULE")
.arg(module_name),
}
}
#[must_use]
pub fn filter_by_acl_category<C: Into<CommandArg>>(self, category: C) -> Self {
Self {
command_args: self
.command_args
.arg("FILTERBY")
.arg("ACLCAT")
.arg(category),
}
}
#[must_use]
pub fn filter_by_pattern<P: Into<CommandArg>>(self, pattern: P) -> Self {
Self {
command_args: self
.command_args
.arg("FILTERBY")
.arg("PATTERN")
.arg(pattern),
}
}
}
impl IntoArgs for CommandListOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
#[derive(Default)]
pub struct FailOverOptions {
command_args: CommandArgs,
}
impl FailOverOptions {
#[must_use]
pub fn to<H: Into<CommandArg>>(self, host: H, port: u16) -> Self {
Self {
command_args: self.command_args.arg("TO").arg(host).arg(port),
}
}
#[must_use]
pub fn timeout(self, milliseconds: u64) -> Self {
Self {
command_args: self.command_args.arg("TIMEOUT").arg(milliseconds),
}
}
#[must_use]
pub fn force(self) -> Self {
Self {
command_args: self.command_args.arg("FORCE"),
}
}
#[must_use]
pub fn abort(self) -> Self {
Self {
command_args: self.command_args.arg("ABORT"),
}
}
}
impl IntoArgs for FailOverOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
pub enum InfoSection {
Server,
Clients,
Memory,
Persistence,
Stats,
Replication,
Cpu,
Commandstats,
Latencystats,
Cluster,
Keyspace,
Modules,
Errorstats,
All,
Default,
Everything,
}
impl From<InfoSection> for CommandArg {
fn from(s: InfoSection) -> Self {
match s {
InfoSection::Server => CommandArg::Str("server"),
InfoSection::Clients => CommandArg::Str("clients"),
InfoSection::Memory => CommandArg::Str("memory"),
InfoSection::Persistence => CommandArg::Str("persistence"),
InfoSection::Stats => CommandArg::Str("stats"),
InfoSection::Replication => CommandArg::Str("replication"),
InfoSection::Cpu => CommandArg::Str("cpu"),
InfoSection::Commandstats => CommandArg::Str("commandstats"),
InfoSection::Latencystats => CommandArg::Str("latencystats"),
InfoSection::Cluster => CommandArg::Str("cluster"),
InfoSection::Keyspace => CommandArg::Str("keyspace"),
InfoSection::Modules => CommandArg::Str("modules"),
InfoSection::Errorstats => CommandArg::Str("errorstats"),
InfoSection::All => CommandArg::Str("all"),
InfoSection::Default => CommandArg::Str("default"),
InfoSection::Everything => CommandArg::Str("everything"),
}
}
}
pub enum LatencyHistoryEvent {
ActiveDefragCycle,
AofFsyncAlways,
AofStat,
AofRewriteDiffWrite,
AofRename,
AofWrite,
AofWriteActiveChild,
AofWriteAlone,
AofWritePendingFsync,
Command,
ExpireCycle,
EvictionCycle,
EvictionDel,
FastCommand,
Fork,
RdbUnlinkTempFile,
}
impl From<LatencyHistoryEvent> for CommandArg {
fn from(e: LatencyHistoryEvent) -> Self {
match e {
LatencyHistoryEvent::ActiveDefragCycle => "active-defrag-cycle".into(),
LatencyHistoryEvent::AofFsyncAlways => "aof-fsync-always".into(),
LatencyHistoryEvent::AofStat => "aof-stat".into(),
LatencyHistoryEvent::AofRewriteDiffWrite => "aof-rewrite-diff-write".into(),
LatencyHistoryEvent::AofRename => "aof-rename".into(),
LatencyHistoryEvent::AofWrite => "aof-write".into(),
LatencyHistoryEvent::AofWriteActiveChild => "aof-write-active-child".into(),
LatencyHistoryEvent::AofWriteAlone => "aof-write-alone".into(),
LatencyHistoryEvent::AofWritePendingFsync => "aof-write-pending-fsync".into(),
LatencyHistoryEvent::Command => "command".into(),
LatencyHistoryEvent::ExpireCycle => "expire-cycle".into(),
LatencyHistoryEvent::EvictionCycle => "eviction-cycle".into(),
LatencyHistoryEvent::EvictionDel => "eviction-del".into(),
LatencyHistoryEvent::FastCommand => "fast-command".into(),
LatencyHistoryEvent::Fork => "fork".into(),
LatencyHistoryEvent::RdbUnlinkTempFile => "rdb-unlink-temp-file".into(),
}
}
}
#[derive(Default)]
pub struct CommandHistogram {
pub calls: usize,
pub histogram_usec: HashMap<u32, u32>,
}
impl FromValue for CommandHistogram {
fn from_value(value: Value) -> Result<Self> {
let mut values: HashMap<String, Value> = value.into()?;
Ok(Self {
calls: values.remove_with_result("calls")?.into()?,
histogram_usec: values.remove_with_result("histogram_usec")?.into()?,
})
}
}
#[derive(Default)]
pub struct LolWutOptions {
command_args: CommandArgs,
}
impl LolWutOptions {
#[must_use]
pub fn version(self, version: usize) -> Self {
Self {
command_args: self.command_args.arg("VERSION").arg(version),
}
}
#[must_use]
pub fn optional_arg<A: Into<CommandArg>>(self, arg: A) -> Self {
Self {
command_args: self.command_args.arg(arg),
}
}
}
impl IntoArgs for LolWutOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
#[derive(Debug)]
pub struct MemoryStats {
pub peak_allocated: usize,
pub total_allocated: usize,
pub startup_allocated: usize,
pub replication_backlog: usize,
pub clients_slaves: usize,
pub clients_normal: usize,
pub cluster_links: usize,
pub aof_buffer: usize,
pub lua_caches: usize,
pub functions_caches: usize,
pub databases: HashMap<usize, DatabaseOverhead>,
pub overhead_total: usize,
pub keys_count: usize,
pub keys_bytes_per_key: usize,
pub dataset_bytes: usize,
pub dataset_percentage: f64,
pub peak_percentage: f64,
pub allocator_allocated: usize,
pub allocator_active: usize,
pub allocator_resident: usize,
pub allocator_fragmentation_ratio: f64,
pub allocator_fragmentation_bytes: usize,
pub allocator_rss_ratio: f64,
pub allocator_rss_bytes: usize,
pub rss_overhead_ratio: f64,
pub rss_overhead_bytes: usize,
pub fragmentation: f64,
pub fragmentation_bytes: usize,
pub additional_stats: HashMap<String, Value>,
}
impl FromValue for MemoryStats {
fn from_value(value: Value) -> Result<Self> {
let mut values: HashMap<String, Value> = value.into()?;
Ok(Self {
peak_allocated: values.remove_or_default("peak.allocated").into()?,
total_allocated: values.remove_or_default("total.allocated").into()?,
startup_allocated: values.remove_or_default("startup.allocated").into()?,
replication_backlog: values.remove_or_default("replication.backlog").into()?,
clients_slaves: values.remove_or_default("clients.slaves").into()?,
clients_normal: values.remove_or_default("clients.normal").into()?,
cluster_links: values.remove_or_default("cluster.links").into()?,
aof_buffer: values.remove_or_default("aof.buffer").into()?,
lua_caches: values.remove_or_default("lua.caches").into()?,
functions_caches: values.remove_or_default("functions.caches").into()?,
databases: (0..16)
.into_iter()
.filter_map(|i| {
values
.remove(&format!("db.{i}"))
.map(|v| DatabaseOverhead::from_value(v).map(|o| (i, o)))
})
.collect::<Result<HashMap<usize, DatabaseOverhead>>>()?,
overhead_total: values.remove_or_default("overhead.total").into()?,
keys_count: values.remove_or_default("keys.count").into()?,
keys_bytes_per_key: values.remove_or_default("keys.bytes-per-key").into()?,
dataset_bytes: values.remove_or_default("dataset.bytes").into()?,
dataset_percentage: values.remove_or_default("dataset.percentage").into()?,
peak_percentage: values.remove_or_default("peak.percentage").into()?,
allocator_allocated: values.remove_or_default("allocator.allocated").into()?,
allocator_active: values.remove_or_default("allocator.active").into()?,
allocator_resident: values.remove_or_default("allocator.resident").into()?,
allocator_fragmentation_ratio: values
.remove_or_default("allocator-fragmentation.ratio")
.into()?,
allocator_fragmentation_bytes: values
.remove_or_default("allocator-fragmentation.bytes")
.into()?,
allocator_rss_ratio: values.remove_or_default("allocator-rss.ratio").into()?,
allocator_rss_bytes: values.remove_or_default("allocator-rss.bytes").into()?,
rss_overhead_ratio: values.remove_or_default("rss-overhead.ratio").into()?,
rss_overhead_bytes: values.remove_or_default("rss-overhead.bytes").into()?,
fragmentation: values.remove_or_default("fragmentation").into()?,
fragmentation_bytes: values.remove_or_default("fragmentation.bytes").into()?,
additional_stats: values,
})
}
}
#[derive(Debug)]
pub struct DatabaseOverhead {
pub overhead_hashtable_main: usize,
pub overhead_hashtable_expires: usize,
pub overhead_hashtable_slot_to_keys: usize,
}
impl FromValue for DatabaseOverhead {
fn from_value(value: Value) -> Result<Self> {
let mut values: HashMap<String, Value> = value.into()?;
Ok(Self {
overhead_hashtable_main: values.remove_or_default("overhead.hashtable.main").into()?,
overhead_hashtable_expires: values
.remove_or_default("overhead.hashtable.expires")
.into()?,
overhead_hashtable_slot_to_keys: values
.remove_or_default("overhead.hashtable.slot-to-keys")
.into()?,
})
}
}
#[derive(Default)]
pub struct MemoryUsageOptions {
command_args: CommandArgs,
}
impl MemoryUsageOptions {
#[must_use]
pub fn samples(self, count: usize) -> Self {
Self {
command_args: self.command_args.arg("SAMPLES").arg(count),
}
}
}
impl IntoArgs for MemoryUsageOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
pub struct ModuleInfo {
pub name: String,
pub version: String,
}
impl FromValue for ModuleInfo {
fn from_value(value: Value) -> Result<Self> {
let mut values: HashMap<String, Value> = value.into()?;
Ok(Self {
name: values.remove_or_default("name").into()?,
version: values.remove_or_default("ver").into()?,
})
}
}
#[derive(Default)]
pub struct ModuleLoadOptions {
command_args: CommandArgs,
args_added: bool,
}
impl ModuleLoadOptions {
#[must_use]
pub fn config<N, V>(self, name: N, value: V) -> Self
where
N: Into<CommandArg>,
V: Into<CommandArg>,
{
if self.args_added {
panic!("method config should be called before method arg");
}
Self {
command_args: self.command_args.arg("CONFIG").arg(name).arg(value),
args_added: false,
}
}
#[must_use]
pub fn arg<A: Into<CommandArg>>(self, arg: A) -> Self {
if !self.args_added {
Self {
command_args: self.command_args.arg("ARGS").arg(arg),
args_added: true,
}
} else {
Self {
command_args: self.command_args.arg(arg),
args_added: false,
}
}
}
}
impl IntoArgs for ModuleLoadOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
pub struct ReplicaOfOptions {
command_args: CommandArgs,
}
impl ReplicaOfOptions {
#[must_use]
pub fn no_one() -> Self {
Self {
command_args: CommandArgs::Empty.arg("NO").arg("ONE"),
}
}
#[must_use]
pub fn master<H: Into<CommandArg>>(host: H, port: u16) -> Self {
Self {
command_args: CommandArgs::Empty.arg(host).arg(port),
}
}
}
impl IntoArgs for ReplicaOfOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
pub enum RoleResult {
Master {
master_replication_offset: usize,
replica_infos: Vec<ReplicaInfo>,
},
Replica {
master_ip: String,
master_port: u16,
state: ReplicationState,
amount_data_received: isize,
},
Sentinel {
master_names: Vec<String>,
},
}
impl FromValue for RoleResult {
fn from_value(value: Value) -> Result<Self> {
let values: Vec<Value> = value.into()?;
let mut iter = values.into_iter();
match (
iter.next(),
iter.next(),
iter.next(),
iter.next(),
iter.next(),
iter.next(),
) {
(
Some(Value::BulkString(Some(s))),
Some(master_replication_offset),
Some(replica_infos),
None,
None,
None,
) if s == b"master" => Ok(Self::Master {
master_replication_offset: master_replication_offset.into()?,
replica_infos: replica_infos.into()?,
}),
(
Some(Value::BulkString(Some(s))),
Some(master_ip),
Some(master_port),
Some(state),
Some(amount_data_received),
None,
) if s == b"slave" => Ok(Self::Replica {
master_ip: master_ip.into()?,
master_port: master_port.into()?,
state: state.into()?,
amount_data_received: amount_data_received.into()?,
}),
(
Some(Value::BulkString(Some(s))),
Some(master_names),
None,
None,
None,
None,
) if s == b"sentinel" => Ok(Self::Sentinel {
master_names: master_names.into()?,
}),
_ => Err(Error::Client(
"Cannot parse RoleResult from result".to_string(),
)),
}
}
}
pub struct ReplicaInfo {
pub ip: String,
pub port: u16,
pub last_ack_offset: usize,
}
impl FromValue for ReplicaInfo {
fn from_value(value: Value) -> Result<Self> {
let (ip, port, last_ack_offset) = value.into()?;
Ok(Self {
ip,
port,
last_ack_offset,
})
}
}
pub enum ReplicationState {
Connect,
Connecting,
Sync,
Connected,
}
impl FromValue for ReplicationState {
fn from_value(value: Value) -> Result<Self> {
let str: String = value.into()?;
match str.as_str() {
"connect" => Ok(Self::Connect),
"connecting" => Ok(Self::Connecting),
"sync" => Ok(Self::Sync),
"connected" => Ok(Self::Connected),
_ => Err(Error::Client(format!(
"Cannot parse {str} to ReplicationState"
))),
}
}
}
#[derive(Default)]
pub struct ShutdownOptions {
command_args: CommandArgs,
}
impl ShutdownOptions {
#[must_use]
pub fn save(self, save: bool) -> Self {
Self {
command_args: self.command_args.arg(if save { "SAVE" } else { "NOSAVE" }),
}
}
#[must_use]
pub fn now(self) -> Self {
Self {
command_args: self.command_args.arg("NOW"),
}
}
#[must_use]
pub fn force(self) -> Self {
Self {
command_args: self.command_args.arg("FORCE"),
}
}
#[must_use]
pub fn abort(self) -> Self {
Self {
command_args: self.command_args.arg("ABORT"),
}
}
}
impl IntoArgs for ShutdownOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
#[derive(Default)]
pub struct SlowLogOptions {
command_args: CommandArgs,
}
impl SlowLogOptions {
#[must_use]
pub fn count(self, count: usize) -> Self {
Self {
command_args: self.command_args.arg(count),
}
}
}
impl IntoArgs for SlowLogOptions {
fn into_args(self, args: CommandArgs) -> CommandArgs {
args.arg(self.command_args)
}
}
pub struct SlowLogEntry {
pub id: i64,
pub unix_timestamp: u32,
pub execution_time_micros: u64,
pub command: Vec<String>,
pub client_address: String,
pub client_name: String,
}
impl FromValue for SlowLogEntry {
fn from_value(value: Value) -> Result<Self> {
let (id, unix_timestamp, execution_time_micros, command, client_address, client_name) =
value.into()?;
Ok(Self {
id,
unix_timestamp,
execution_time_micros,
command,
client_address,
client_name,
})
}
}