,
{
prepare_command(self, cmd("COMMAND").arg("LIST").arg(options))
}
/// Used to read the configuration parameters of a running Redis server.
///
/// For every key that does not hold a string value or does not exist,
/// the special value nil is returned. Because of this, the operation never fails.
///
/// # Return
/// Array reply: collection of the requested params with their matching values.
///
/// # See Also
/// [](https://redis.io/commands/config-get/)
#[must_use]
fn config_get(self, params: PP) -> PreparedCommand<'a, Self, VV>
where
Self: Sized,
P: SingleArg,
PP: SingleArgCollection
,
V: PrimitiveResponse,
VV: KeyValueCollectionResponse,
{
prepare_command(self, cmd("CONFIG").arg("GET").arg(params))
}
/// The command returns a helpful text describing the different CONFIG subcommands.
///
/// # Return
/// An array of strings.
///
/// # Example
/// ```
/// # use rustis::{
/// # client::Client,
/// # commands::ServerCommands,
/// # Result,
/// # };
/// #
/// # #[cfg_attr(feature = "tokio-runtime", tokio::main)]
/// # #[cfg_attr(feature = "async-std-runtime", async_std::main)]
/// # async fn main() -> Result<()> {
/// # let client = Client::connect("127.0.0.1:6379").await?;
/// let result: Vec = client.config_help().await?;
/// assert!(result.iter().any(|e| e == "HELP"));
/// # Ok(())
/// # }
/// ```
/// # See Also
/// [](https://redis.io/commands/config-help/)
#[must_use]
fn config_help(self) -> PreparedCommand<'a, Self, Vec>
where
Self: Sized,
{
prepare_command(self, cmd("CONFIG").arg("HELP"))
}
/// Resets the statistics reported by Redis using the [`info`](ServerCommands::info) command.
///
/// # See Also
/// [](https://redis.io/commands/config-resetstat/)
#[must_use]
fn config_resetstat(self) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("CONFIG").arg("RESETSTAT"))
}
/// Rewrites the redis.conf file the server was started with,
/// applying the minimal changes needed to make it reflect the configuration currently used by the server,
/// which may be different compared to the original one because of the use of the
/// [`config_set`](ServerCommands::config_set) command.
///
/// # See Also
/// [](https://redis.io/commands/config-rewrite/)
#[must_use]
fn config_rewrite(self) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("CONFIG").arg("REWRITE"))
}
/// Used in order to reconfigure the server at run time without the need to restart Redis.
///
/// # See Also
/// [](https://redis.io/commands/config-set/)
#[must_use]
fn config_set(self, configs: C) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
P: SingleArg,
V: SingleArg,
C: KeyValueArgsCollection
,
{
prepare_command(self, cmd("CONFIG").arg("SET").arg(configs))
}
/// Return the number of keys in the currently-selected database.
///
/// # See Also
/// [](https://redis.io/commands/dbsize/)
#[must_use]
fn dbsize(self) -> PreparedCommand<'a, Self, usize>
where
Self: Sized,
{
prepare_command(self, cmd("DBSIZE"))
}
/// This command will start a coordinated failover between
/// the currently-connected-to master and one of its replicas.
///
/// # See Also
/// [](https://redis.io/commands/failover/)
#[must_use]
fn failover(self, options: FailOverOptions) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("FAILOVER").arg(options))
}
/// Delete all the keys of the currently selected DB.
///
/// # See Also
/// [](https://redis.io/commands/flushdb/)
#[must_use]
fn flushdb(self, flushing_mode: FlushingMode) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("FLUSHDB").arg(flushing_mode))
}
/// Delete all the keys of all the existing databases, not just the currently selected one.
///
/// # See Also
/// [](https://redis.io/commands/flushall/)
#[must_use]
fn flushall(self, flushing_mode: FlushingMode) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("FLUSHALL").arg(flushing_mode))
}
/// This command returns information and statistics about the server
/// in a format that is simple to parse by computers and easy to read by humans.
///
/// # See Also
/// [](https://redis.io/commands/info/)
#[must_use]
fn info(self, sections: SS) -> PreparedCommand<'a, Self, String>
where
Self: Sized,
SS: SingleArgCollection,
{
prepare_command(self, cmd("INFO").arg(sections))
}
/// Return the UNIX TIME of the last DB save executed with success.
///
/// # See Also
/// [](https://redis.io/commands/lastsave/)
#[must_use]
fn lastsave(self) -> PreparedCommand<'a, Self, u64>
where
Self: Sized,
{
prepare_command(self, cmd("LASTSAVE"))
}
/// This command reports about different latency-related issues and advises about possible remedies.
///
/// # Return
/// String report
///
/// # See Also
/// [](https://redis.io/commands/latency-doctor/)
#[must_use]
fn latency_doctor(self) -> PreparedCommand<'a, Self, String>
where
Self: Sized,
{
prepare_command(self, cmd("LATENCY").arg("DOCTOR"))
}
/// Produces an ASCII-art style graph for the specified event.
///
/// # Return
/// String graph
///
/// # See Also
/// [](https://redis.io/commands/latency-graph/)
#[must_use]
fn latency_graph(self, event: LatencyHistoryEvent) -> PreparedCommand<'a, Self, String>
where
Self: Sized,
{
prepare_command(self, cmd("LATENCY").arg("GRAPH").arg(event))
}
/// The command returns a helpful text describing the different LATENCY subcommands.
///
/// # Return
/// An array of strings.
///
/// # Example
/// ```
/// # use rustis::{
/// # client::Client,
/// # commands::ServerCommands,
/// # Result,
/// # };
/// #
/// # #[cfg_attr(feature = "tokio-runtime", tokio::main)]
/// # #[cfg_attr(feature = "async-std-runtime", async_std::main)]
/// # async fn main() -> Result<()> {
/// # let client = Client::connect("127.0.0.1:6379").await?;
/// let result: Vec = client.latency_help().await?;
/// assert!(result.iter().any(|e| e == "HELP"));
/// # Ok(())
/// # }
/// ```
/// # See Also
/// [](https://redis.io/commands/latency-help/)
fn latency_help(self) -> PreparedCommand<'a, Self, Vec>
where
Self: Sized,
{
prepare_command(self, cmd("LATENCY").arg("HELP"))
}
/// This command reports a cumulative distribution of latencies
/// in the format of a histogram for each of the specified command names.
///
/// # Return
/// The command returns a map where each key is a command name, and each value is a CommandHistogram instance.
///
/// # See Also
/// [](https://redis.io/commands/latency-histogram/)
#[must_use]
fn latency_histogram(self, commands: CC) -> PreparedCommand<'a, Self, RR>
where
Self: Sized,
C: SingleArg,
CC: SingleArgCollection,
RR: KeyValueCollectionResponse,
{
prepare_command(self, cmd("LATENCY").arg("HISTOGRAM").arg(commands))
}
/// This command returns the raw data of the event's latency spikes time series.
///
/// # Return
/// The command returns a collection where each element is a two elements tuple representing
/// - the unix timestamp in seconds
/// - the latency of the event in milliseconds
///
/// # See Also
/// [](https://redis.io/commands/latency-history/)
#[must_use]
fn latency_history(self, event: LatencyHistoryEvent) -> PreparedCommand<'a, Self, RR>
where
Self: Sized,
RR: CollectionResponse<(u32, u32)>,
{
prepare_command(self, cmd("LATENCY").arg("HISTORY").arg(event))
}
/// This command reports the latest latency events logged.
///
/// # Return
/// A collection of the latest latency events logged.
/// Each reported event has the following fields:
/// - Event name.
/// - Unix timestamp of the latest latency spike for the event.
/// - Latest event latency in millisecond.
/// - All-time maximum latency for this event.
///
/// "All-time" means the maximum latency since the Redis instance was started,
/// or the time that events were [`reset`](crate::commands::ConnectionCommands::reset).
///
/// # See Also
/// [](https://redis.io/commands/latency-latest/)
#[must_use]
fn latency_latest(self) -> PreparedCommand<'a, Self, RR>
where
Self: Sized,
RR: CollectionResponse<(String, u32, u32, u32)>,
{
prepare_command(self, cmd("LATENCY").arg("LATEST"))
}
/// This command resets the latency spikes time series of all, or only some, events.
///
/// # Return
/// the number of event time series that were reset.
///
/// # See Also
/// [](https://redis.io/commands/latency-latest/)
#[must_use]
fn latency_reset(self, events: EE) -> PreparedCommand<'a, Self, usize>
where
Self: Sized,
EE: SingleArgCollection,
{
prepare_command(self, cmd("LATENCY").arg("RESET").arg(events))
}
/// The LOLWUT command displays the Redis version: however as a side effect of doing so,
/// it also creates a piece of generative computer art that is different with each version of Redis.
///
/// # Return
/// the string containing the generative computer art, and a text with the Redis version.
///
/// # See Also
/// [](https://redis.io/commands/lolwut/)
#[must_use]
fn lolwut(self, options: LolWutOptions) -> PreparedCommand<'a, Self, String>
where
Self: Sized,
{
prepare_command(self, cmd("LOLWUT").arg(options))
}
/// This command reports about different memory-related issues that
/// the Redis server experiences, and advises about possible remedies.
///
/// # Return
/// the string report.
///
/// # See Also
/// [](https://redis.io/commands/memory-doctor/)
#[must_use]
fn memory_doctor(self) -> PreparedCommand<'a, Self, String>
where
Self: Sized,
{
prepare_command(self, cmd("MEMORY").arg("DOCTOR"))
}
/// The command returns a helpful text describing the different MEMORY subcommands.
///
/// # Return
/// An array of strings.
///
/// # Example
/// ```
/// # use rustis::{
/// # client::Client,
/// # commands::ServerCommands,
/// # Result,
/// # };
/// #
/// # #[cfg_attr(feature = "tokio-runtime", tokio::main)]
/// # #[cfg_attr(feature = "async-std-runtime", async_std::main)]
/// # async fn main() -> Result<()> {
/// # let client = Client::connect("127.0.0.1:6379").await?;
/// let result: Vec = client.memory_help().await?;
/// assert!(result.iter().any(|e| e == "HELP"));
/// # Ok(())
/// # }
/// ```
///
/// # See Also
/// [](https://redis.io/commands/memory-help/)
#[must_use]
fn memory_help(self) -> PreparedCommand<'a, Self, Vec>
where
Self: Sized,
{
prepare_command(self, cmd("MEMORY").arg("HELP"))
}
/// This command provides an internal statistics report from the memory allocator.
///
/// # Return
/// the memory allocator's internal statistics report.
///
/// # See Also
/// [](https://redis.io/commands/memory-malloc-stats/)
#[must_use]
fn memory_malloc_stats(self) -> PreparedCommand<'a, Self, String>
where
Self: Sized,
{
prepare_command(self, cmd("MEMORY").arg("MALLOC-STATS"))
}
/// This command attempts to purge dirty pages so these can be reclaimed by the allocator.
///
/// # See Also
/// [](https://redis.io/commands/memory-purge/)
#[must_use]
fn memory_purge(self) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("MEMORY").arg("PURGE"))
}
/// This command returns information about the memory usage of the server.
///
/// # Return
/// the memory allocator's internal statistics report.
///
/// # See Also
/// [](https://redis.io/commands/memory-stats/)
#[must_use]
fn memory_stats(self) -> PreparedCommand<'a, Self, MemoryStats>
where
Self: Sized,
{
prepare_command(self, cmd("MEMORY").arg("STATS"))
}
/// This command reports the number of bytes that a key and its value require to be stored in RAM.
///
/// # Return
/// the memory usage in bytes, or None when the key does not exist.
///
/// # See Also
/// [](https://redis.io/commands/memory-usage/)
#[must_use]
fn memory_usage(
self,
key: K,
options: MemoryUsageOptions,
) -> PreparedCommand<'a, Self, Option>
where
Self: Sized,
K: SingleArg,
{
prepare_command(self, cmd("MEMORY").arg("USAGE").arg(key).arg(options))
}
/// Returns information about the modules loaded to the server.
///
/// # Return
/// list of loaded modules.
/// Each element in the list represents a module as an instance of [`ModuleInfo`](ModuleInfo)
///
/// # See Also
/// [](https://redis.io/commands/module-list/)
#[must_use]
fn module_list(self) -> PreparedCommand<'a, Self, MM>
where
Self: Sized,
MM: CollectionResponse,
{
prepare_command(self, cmd("MODULE").arg("LIST"))
}
/// The command returns a helpful text describing the different MODULE subcommands.
///
/// # Return
/// An array of strings.
///
/// # Example
/// ```
/// # use rustis::{
/// # client::Client,
/// # commands::ServerCommands,
/// # Result,
/// # };
/// #
/// # #[cfg_attr(feature = "tokio-runtime", tokio::main)]
/// # #[cfg_attr(feature = "async-std-runtime", async_std::main)]
/// # async fn main() -> Result<()> {
/// # let client = Client::connect("127.0.0.1:6379").await?;
/// let result: Vec = client.module_help().await?;
/// assert!(result.iter().any(|e| e == "HELP"));
/// # Ok(())
/// # }
/// ```
///
/// # See Also
/// [](https://redis.io/commands/module-help/)
#[must_use]
fn module_help(self) -> PreparedCommand<'a, Self, Vec>
where
Self: Sized,
{
prepare_command(self, cmd("MODULE").arg("HELP"))
}
/// Loads a module from a dynamic library at runtime.
///
/// # See Also
/// [](https://redis.io/commands/module-load/)
#[must_use]
fn module_load(self, path: P, options: ModuleLoadOptions) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
P: SingleArg,
{
prepare_command(self, cmd("MODULE").arg("LOADEX").arg(path).arg(options))
}
/// Unloads a module.
///
/// # See Also
/// [](https://redis.io/commands/module-unload/)
#[must_use]
fn module_unload(self, name: N) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
N: SingleArg,
{
prepare_command(self, cmd("MODULE").arg("UNLOAD").arg(name))
}
/// This command can change the replication settings of a replica on the fly.
///
/// # See Also
/// [](https://redis.io/commands/replicaof/)
#[must_use]
fn replicaof(self, options: ReplicaOfOptions) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("REPLICAOF").arg(options))
}
/// Provide information on the role of a Redis instance in the context of replication,
/// by returning if the instance is currently a `master`, `slave`, or `sentinel`.
///
/// # See Also
/// [](https://redis.io/commands/role/)
#[must_use]
fn role(self) -> PreparedCommand<'a, Self, RoleResult>
where
Self: Sized,
{
prepare_command(self, cmd("ROLE"))
}
/// This command performs a synchronous save of the dataset producing a point in time snapshot
/// of all the data inside the Redis instance, in the form of an RDB file.
///
/// # See Also
/// [](https://redis.io/commands/save/)
#[must_use]
fn save(self) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("SAVE"))
}
/// Shutdown the server
///
/// # See Also
/// [](https://redis.io/commands/shutdown/)
#[must_use]
fn shutdown(self, options: ShutdownOptions) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("SHUTDOWN").arg(options))
}
/// This command returns entries from the slow log in chronological order.
///
/// # See Also
/// [](https://redis.io/commands/slowlog-get/)
#[must_use]
fn slowlog_get(self, options: SlowLogOptions) -> PreparedCommand<'a, Self, Vec>
where
Self: Sized,
{
prepare_command(self, cmd("SLOWLOG").arg("GET").arg(options))
}
/// The command returns a helpful text describing the different SLOWLOG subcommands.
///
/// # Return
/// An array of strings.
///
/// # Example
/// ```
/// # use rustis::{
/// # client::Client,
/// # commands::ServerCommands,
/// # Result,
/// # };
/// #
/// # #[cfg_attr(feature = "tokio-runtime", tokio::main)]
/// # #[cfg_attr(feature = "async-std-runtime", async_std::main)]
/// # async fn main() -> Result<()> {
/// # let client = Client::connect("127.0.0.1:6379").await?;
/// let result: Vec = client.slowlog_help().await?;
/// assert!(result.iter().any(|e| e == "HELP"));
/// # Ok(())
/// # }
/// ```
///
/// # See Also
/// [](https://redis.io/commands/slowlog-help/)
#[must_use]
fn slowlog_help(self) -> PreparedCommand<'a, Self, Vec>
where
Self: Sized,
{
prepare_command(self, cmd("SLOWLOG").arg("HELP"))
}
/// This command returns the current number of entries in the slow log.
///
/// # See Also
/// [](https://redis.io/commands/slowlog-len/)
#[must_use]
fn slowlog_len(self) -> PreparedCommand<'a, Self, usize>
where
Self: Sized,
{
prepare_command(self, cmd("SLOWLOG").arg("LEN"))
}
/// This command resets the slow log, clearing all entries in it.
///
/// # See Also
/// [](https://redis.io/commands/slowlog-reset/)
#[must_use]
fn slowlog_reset(self) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("SLOWLOG").arg("RESET"))
}
/// This command swaps two Redis databases,
/// so that immediately all the clients connected to a given database
/// will see the data of the other database, and the other way around.
///
/// # See Also
/// [](https://redis.io/commands/swapdb/)
#[must_use]
fn swapdb(self, index1: usize, index2: usize) -> PreparedCommand<'a, Self, ()>
where
Self: Sized,
{
prepare_command(self, cmd("SWAPDB").arg(index1).arg(index2))
}
/// The TIME command returns the current server time as a two items lists:
/// a Unix timestamp and the amount of microseconds already elapsed in the current second.
///
/// # See Also
/// [](https://redis.io/commands/time/)
#[must_use]
fn time(self) -> PreparedCommand<'a, Self, (u32, u32)>
where
Self: Sized,
{
prepare_command(self, cmd("TIME"))
}
}
/// Database flushing mode
#[derive(Default)]
pub enum FlushingMode {
#[default]
Default,
/// Flushes the database(s) asynchronously
Async,
/// Flushed the database(s) synchronously
Sync,
}
impl ToArgs for FlushingMode {
fn write_args(&self, args: &mut CommandArgs) {
match self {
FlushingMode::Default => {}
FlushingMode::Async => {
args.arg("ASYNC");
}
FlushingMode::Sync => {
args.arg("SYNC");
}
}
}
}
/// Options for the [`acl_cat`](ServerCommands::acl_cat) command
#[derive(Default)]
pub struct AclCatOptions {
command_args: CommandArgs,
}
impl AclCatOptions {
#[must_use]
pub fn category_name(mut self, category_name: C) -> Self {
Self {
command_args: self.command_args.arg(category_name).build(),
}
}
}
impl ToArgs for AclCatOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Options for the [`acl_dryrun`](ServerCommands::acl_dryrun) command
#[derive(Default)]
pub struct AclDryRunOptions {
command_args: CommandArgs,
}
impl AclDryRunOptions {
#[must_use]
pub fn arg(mut self, args: AA) -> Self
where
A: SingleArg,
AA: SingleArgCollection,
{
Self {
command_args: self.command_args.arg(args).build(),
}
}
}
impl ToArgs for AclDryRunOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Options for the [`acl_genpass`](ServerCommands::acl_genpass) command
#[derive(Default)]
pub struct AclGenPassOptions {
command_args: CommandArgs,
}
impl AclGenPassOptions {
/// The command output is a hexadecimal representation of a binary string.
/// By default it emits 256 bits (so 64 hex characters).
/// The user can provide an argument in form of number of bits to emit from 1 to 1024 to change the output length.
/// Note that the number of bits provided is always rounded to the next multiple of 4.
/// So for instance asking for just 1 bit password will result in 4 bits to be emitted, in the form of a single hex character.
#[must_use]
pub fn bits(mut self, bits: usize) -> Self {
Self {
command_args: self.command_args.arg(bits).build(),
}
}
}
impl ToArgs for AclGenPassOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Options for the [`acl_log`](ServerCommands::acl_log) command
#[derive(Default)]
pub struct AclLogOptions {
command_args: CommandArgs,
}
impl AclLogOptions {
/// This optional argument specifies how many entries to show.
/// By default up to ten failures are returned.
#[must_use]
pub fn count(mut self, count: usize) -> Self {
Self {
command_args: self.command_args.arg(count).build(),
}
}
/// The special RESET argument clears the log.
#[must_use]
pub fn reset(mut self) -> Self {
Self {
command_args: self.command_args.arg("RESET").build(),
}
}
}
impl ToArgs for AclLogOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Options for the [`bgsave`](ServerCommands::bgsave) command
#[derive(Default)]
pub struct BgsaveOptions {
command_args: CommandArgs,
}
impl BgsaveOptions {
/// This argument will immediately return OK
/// when an AOF rewrite is in progress and schedule the background save
/// to run at the next opportunity.
#[must_use]
pub fn schedule(mut self) -> Self {
Self {
command_args: self.command_args.arg("SCHEDULE").build(),
}
}
}
impl ToArgs for BgsaveOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Command info result for the [`command`](ServerCommands::command) command.
#[derive(Debug, Clone, Deserialize)]
pub struct CommandInfo {
/// This is the command's name in lowercase.
pub name: String,
/// Arity is the number of arguments a command expects. It follows a simple pattern:
/// - A positive integer means a fixed number of arguments.
/// - A negative integer means a minimal number of arguments.
pub arity: isize,
/// Command flags are an array.
/// See [COMMAND documentation](https://redis.io/commands/command/) for the list of flags
pub flags: Vec,
/// The position of the command's first key name argument.
/// For most commands, the first key's position is 1. Position 0 is always the command name itself.
pub first_key: usize,
/// The position of the command's last key name argument.
pub last_key: isize,
/// The step, or increment, between the first key and the position of the next key.
pub step: usize,
/// [From Redis 6.0] This is an array of simple strings that are the ACL categories to which the command belongs.
pub acl_categories: Vec,
/// [From Redis 7.0] Helpful information about the command. To be used by clients/proxies.
/// See [](https://redis.io/docs/reference/command-tips/)
#[serde(default)]
pub command_tips: Vec,
/// [From Redis 7.0] This is an array consisting of the command's key specifications.
/// See [](https://redis.io/docs/reference/key-specs/)
#[serde(default)]
pub key_specifications: Vec,
#[serde(default)]
pub sub_commands: Vec,
}
/// Get additional information about a command
///
/// See
#[derive(Debug, Clone)]
pub enum CommandTip {
NonDeterministricOutput,
NonDeterministricOutputOrder,
RequestPolicy(RequestPolicy),
ResponsePolicy(ResponsePolicy),
}
impl<'de> Deserialize<'de> for CommandTip {
fn deserialize(deserializer: D) -> std::result::Result
where
D: Deserializer<'de>,
{
let tip = <&str>::deserialize(deserializer)?;
match tip {
"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) => {
match RequestPolicy::from_str(policy) {
Ok(request_policy) => Ok(CommandTip::RequestPolicy(request_policy)),
Err(_) => Err(de::Error::invalid_value(
de::Unexpected::Str(policy),
&"a valid RequestPolicy value",
)),
}
}
(Some("response_policy"), Some(policy), None) => {
match ResponsePolicy::from_str(policy) {
Ok(response_policy) => Ok(CommandTip::ResponsePolicy(response_policy)),
Err(_) => Err(de::Error::invalid_value(
de::Unexpected::Str(policy),
&"a valid ResponsePolicy value",
)),
}
}
_ => Err(de::Error::invalid_value(
de::Unexpected::Str(tip),
&"a valid CommandTip value",
)),
}
}
}
}
}
/// This tip can help clients determine the shards to send the command in clustering mode.
///
/// The default behavior a client should implement for commands without the request_policy tip is as follows:
/// 1. The command doesn't accept key name arguments: the client can execute the command on an arbitrary shard.
/// 2. For commands that accept one or more key name arguments: the client should route the command to a single shard,
/// as determined by the hash slot of the input keys.
#[derive(Debug, Clone, Deserialize)]
pub enum RequestPolicy {
/// the client should execute the command on all nodes - masters and replicas alike.
///
/// An example is the [`config_set`](ServerCommands::config_set) command.
/// This tip is in-use by commands that don't accept key name arguments. The command operates atomically per shard.
AllNodes,
/// the client should execute the command on all master shards (e.g., the [`dbsize`](ServerCommands::dbsize) command).
///
/// This tip is in-use by commands that don't accept key name arguments. The command operates atomically per shard.
AllShards,
/// the client should execute the command on several shards.
///
/// The shards that execute the command are determined by the hash slots of its input key name arguments.
/// Examples for such commands include [`mset`](crate::commands::StringCommands::mset), [`mget`](crate::commands::StringCommands::mget)
/// and [`del`](crate::commands::GenericCommands::del).
/// However, note that [`sunionstore`](crate::commands::SetCommands::sunionstore) isn't considered
/// as multi_shard because all of its keys must belong to the same hash slot.
MultiShard,
/// indicates a non-trivial form of the client's request policy, such as the [`scan`](crate::commands::GenericCommands::scan) command.
Special,
}
impl FromStr for RequestPolicy {
type Err = Error;
fn from_str(str: &str) -> Result {
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(),
)),
}
}
}
/// This tip can help clients determine the aggregate they need to compute from the replies of multiple shards in a cluster.
///
/// The default behavior for commands without a request_policy tip only applies to replies with of nested types
/// (i.e., an array, a set, or a map).
/// The client's implementation for the default behavior should be as follows:
/// 1. The command doesn't accept key name arguments: the client can aggregate all replies within a single nested data structure.
/// For example, the array replies we get from calling [`keys`](crate::commands::GenericCommands::keys) against all shards.
/// These should be packed in a single in no particular order.
/// 2. For commands that accept one or more key name arguments: the client needs to retain the same order of replies as the input key names.
/// For example, [`mget`](crate::commands::StringCommands::mget)'s aggregated reply.
#[derive(Debug, Clone, Deserialize)]
pub enum ResponsePolicy {
/// the clients should return success if at least one shard didn't reply with an error.
///
/// The client should reply with the first non-error reply it obtains.
/// If all shards return an error, the client can reply with any one of these.
/// For example, consider a [`script_kill`](crate::commands::ScriptingCommands::script_kill) command that's sent to all shards.
/// Although the script should be loaded in all of the cluster's shards,
/// the [`script_kill`](crate::commands::ScriptingCommands::script_kill) will typically run only on one at a given time.
OneSucceeded,
/// the client should return successfully only if there are no error replies.
///
/// Even a single error reply should disqualify the aggregate and be returned.
/// Otherwise, the client should return one of the non-error replies.
/// As an example, consider the [`config_set`](ServerCommands::config_set),
/// [`script_flush`](crate::commands::ScriptingCommands::script_flush) and
/// [`script_load`](crate::commands::ScriptingCommands::script_load) commands.
AllSucceeded,
/// the client should return the result of a logical `AND` operation on all replies
/// (only applies to integer replies, usually from commands that return either 0 or 1).
///
/// Consider the [`script_exists`](crate::commands::ScriptingCommands::script_exists) command as an example.
/// It returns an array of 0's and 1's that denote the existence of its given SHA1 sums in the script cache.
/// The aggregated response should be 1 only when all shards had reported that a given script SHA1 sum is in their respective cache.
AggLogicalAnd,
/// the client should return the result of a logical `OR` operation on all replies
/// (only applies to integer replies, usually from commands that return either 0 or 1).
AggLogicalOr,
/// the client should return the minimal value from the replies (only applies to numerical replies).
///
/// The aggregate reply from a cluster-wide [`wait`](crate::commands::GenericCommands::wait) command, for example,
/// should be the minimal value (number of synchronized replicas) from all shards
AggMin,
/// the client should return the maximal value from the replies (only applies to numerical replies).
AggMax,
/// the client should return the sum of replies (only applies to numerical replies).
///
/// Example: [`dbsize`](ServerCommands::dbsize).
AggSum,
/// this type of tip indicates a non-trivial form of reply policy.
///
/// [`info`](ServerCommands::info) is an excellent example of that.
Special,
}
impl FromStr for ResponsePolicy {
type Err = Error;
fn from_str(str: &str) -> Result {
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(),
)),
}
}
}
/// Key specifications of a command for the [`command`](ServerCommands::command) command.
#[derive(Debug, Clone, Deserialize)]
pub struct KeySpecification {
pub begin_search: BeginSearch,
pub find_keys: FindKeys,
pub flags: Vec,
#[serde(default)]
pub notes: String,
}
/// The BeginSearch value of a specification informs
/// the client of the extraction's beginning
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type", content = "spec")]
#[serde(rename_all = "lowercase")]
pub enum BeginSearch {
#[serde(deserialize_with = "deserialize_begin_search_idx")]
Index(usize),
Keyword {
keyword: String,
#[serde(rename = "startfrom")]
start_from: isize,
},
#[serde(deserialize_with = "deserialize_begin_search_unknown")]
Unknown,
}
fn deserialize_begin_search_idx<'de, D>(deserializer: D) -> std::result::Result
where
D: Deserializer<'de>,
{
let map = HashMap::::deserialize(deserializer)?;
let index = map
.get("index")
.ok_or_else(|| de::Error::custom("Cannot parse BeginSearch index"))?;
Ok(*index)
}
fn deserialize_begin_search_unknown<'de, D>(deserializer: D) -> std::result::Result<(), D::Error>
where
D: Deserializer<'de>,
{
let map = HashMap::::deserialize(deserializer)?;
assert!(map.is_empty());
Ok(())
}
/// The FindKeys value of a key specification tells the client
/// how to continue the search for key names.
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type", content = "spec")]
#[serde(rename_all = "lowercase")]
pub enum FindKeys {
Range {
#[serde(rename = "lastkey")]
last_key: isize,
#[serde(rename = "keystep")]
key_step: usize,
limit: usize,
},
KeyNum {
#[serde(rename = "keynumidx")]
key_num_idx: usize,
#[serde(rename = "firstkey")]
first_key: usize,
#[serde(rename = "keystep")]
key_step: usize,
},
Unknown {},
}
/// Command doc result for the [`command_docs`](ServerCommands::command_docs) command
#[derive(Debug, Default, Deserialize)]
pub struct CommandDoc {
/// short command description.
pub summary: String,
/// the Redis version that added the command (or for module commands, the module version).
pub since: String,
/// he functional group to which the command belongs.
pub group: String,
/// a short explanation about the command's time complexity.
pub complexity: String,
/// an array of documentation flags. Possible values are:
/// - `deprecated`: the command is deprecated.
/// - `syscmd`: a system command that isn't meant to be called by users.
#[serde(default)]
pub doc_flags: Vec,
/// the Redis version that deprecated the command (or for module commands, the module version).
#[serde(default)]
pub deprecated_since: String,
/// the alternative for a deprecated command.
#[serde(default)]
pub replaced_by: String,
/// an array of historical notes describing changes to the command's behavior or arguments.
#[serde(default)]
pub history: Vec,
/// an array of [`command arguments`](https://redis.io/docs/reference/command-arguments/)
pub arguments: Vec,
}
/// Command documenation flag
#[derive(Debug, Deserialize)]
pub enum CommandDocFlag {
/// the command is deprecated.
Deprecated,
/// a system command that isn't meant to be called by users.
SystemCommand,
}
/// Sub-result for the [`command_docs`](ServerCommands::command_docs) command
#[derive(Debug, Deserialize)]
pub struct HistoricalNote {
pub version: String,
pub description: String,
}
/// [`command argument`](https://redis.io/docs/reference/command-arguments/)
#[derive(Debug, Deserialize)]
pub struct CommandArgument {
/// the argument's name, always present.
pub name: String,
/// the argument's display string, present in arguments that have a displayable representation
#[serde(default)]
pub display_text: String,
/// the argument's type, always present.
#[serde(rename = "type")]
pub type_: CommandArgumentType,
/// this value is available for every argument of the `key` type.
/// t is a 0-based index of the specification in the command's [`key specifications`](https://redis.io/topics/key-specs)
/// that corresponds to the argument.
#[serde(default)]
pub key_spec_index: usize,
/// a constant literal that precedes the argument (user input) itself.
#[serde(default)]
pub token: String,
/// a short description of the argument.
#[serde(default)]
pub summary: String,
/// the debut Redis version of the argument (or for module commands, the module version).
#[serde(default)]
pub since: String,
/// the Redis version that deprecated the command (or for module commands, the module version).
#[serde(default)]
pub deprecated_since: String,
/// an array of argument flags.
#[serde(default)]
pub flags: Vec,
/// the argument's value.
#[serde(default)]
pub value: Vec,
}
/// An argument must have one of the following types:
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum CommandArgumentType {
/// a string argument.
String,
/// an integer argument.
Integer,
/// a double-precision argument.
Double,
/// a string that represents the name of a key.
Key,
/// a string that represents a glob-like pattern.
Pattern,
/// an integer that represents a Unix timestamp.
UnixTime,
/// a token, i.e. a reserved keyword, which may or may not be provided.
/// Not to be confused with free-text user input.
PureToken,
/// the argument is a container for nested arguments.
/// This type enables choice among several nested arguments
Oneof,
/// the argument is a container for nested arguments.
/// This type enables grouping arguments and applying a property (such as optional) to all
Block,
}
/// Flag for a command argument
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum ArgumentFlag {
/// denotes that the argument is optional (for example, the GET clause of the SET command).
Optional,
/// denotes that the argument may be repeated (such as the key argument of DEL).
Multiple,
/// denotes the possible repetition of the argument with its preceding token (see SORT's GET pattern clause).
MultipleToken,
}
/// Options for the [`command_list`](ServerCommands::command_list) command.
#[derive(Default)]
pub struct CommandListOptions {
command_args: CommandArgs,
}
impl CommandListOptions {
/// get the commands that belong to the module specified by `module-name`.
#[must_use]
pub fn filter_by_module_name(mut self, module_name: M) -> Self {
Self {
command_args: self
.command_args
.arg("FILTERBY")
.arg("MODULE")
.arg(module_name)
.build(),
}
}
/// get the commands in the [`ACL category`](https://redis.io/docs/manual/security/acl/#command-categories) specified by `category`.
#[must_use]
pub fn filter_by_acl_category(mut self, category: C) -> Self {
Self {
command_args: self
.command_args
.arg("FILTERBY")
.arg("ACLCAT")
.arg(category)
.build(),
}
}
/// get the commands that match the given glob-like `pattern`.
#[must_use]
pub fn filter_by_pattern(mut self, pattern: P) -> Self {
Self {
command_args: self
.command_args
.arg("FILTERBY")
.arg("PATTERN")
.arg(pattern)
.build(),
}
}
}
impl ToArgs for CommandListOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Options for the [`failover`](ServerCommands::failover) command.
#[derive(Default)]
pub struct FailOverOptions {
command_args: CommandArgs,
}
impl FailOverOptions {
/// This option allows designating a specific replica, by its host and port, to failover to.
#[must_use]
pub fn to(mut self, host: H, port: u16) -> Self {
Self {
command_args: self.command_args.arg("TO").arg(host).arg(port).build(),
}
}
/// This option allows specifying a maximum time a master will wait in the waiting-for-sync state
/// before aborting the failover attempt and rolling back.
#[must_use]
pub fn timeout(mut self, milliseconds: u64) -> Self {
Self {
command_args: self.command_args.arg("TIMEOUT").arg(milliseconds).build(),
}
}
/// If both the [`timeout`](FailOverOptions::timeout) and [`to`](FailOverOptions::to) options are set,
/// the force flag can also be used to designate that that once the timeout has elapsed,
/// the master should failover to the target replica instead of rolling back.
#[must_use]
pub fn force(mut self) -> Self {
Self {
command_args: self.command_args.arg("FORCE").build(),
}
}
/// This command will abort an ongoing failover and return the master to its normal state.
#[must_use]
pub fn abort(mut self) -> Self {
Self {
command_args: self.command_args.arg("ABORT").build(),
}
}
}
impl ToArgs for FailOverOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Section for the [`info`](ServerCommands::info) command.
pub enum InfoSection {
Server,
Clients,
Memory,
Persistence,
Stats,
Replication,
Cpu,
Commandstats,
Latencystats,
Cluster,
Keyspace,
Modules,
Errorstats,
All,
Default,
Everything,
}
impl SingleArg for InfoSection {}
impl ToArgs for InfoSection {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(match self {
InfoSection::Server => "server",
InfoSection::Clients => "clients",
InfoSection::Memory => "memory",
InfoSection::Persistence => "persistence",
InfoSection::Stats => "stats",
InfoSection::Replication => "replication",
InfoSection::Cpu => "cpu",
InfoSection::Commandstats => "commandstats",
InfoSection::Latencystats => "latencystats",
InfoSection::Cluster => "cluster",
InfoSection::Keyspace => "keyspace",
InfoSection::Modules => "modules",
InfoSection::Errorstats => "errorstats",
InfoSection::All => "all",
InfoSection::Default => "default",
InfoSection::Everything => "everything",
});
}
}
/// Latency history event for the [`latency_graph`](ServerCommands::latency_graph)
/// & [`latency_history`](ServerCommands::latency_history) commands.
pub enum LatencyHistoryEvent {
ActiveDefragCycle,
AofFsyncAlways,
AofStat,
AofRewriteDiffWrite,
AofRename,
AofWrite,
AofWriteActiveChild,
AofWriteAlone,
AofWritePendingFsync,
Command,
ExpireCycle,
EvictionCycle,
EvictionDel,
FastCommand,
Fork,
RdbUnlinkTempFile,
}
impl SingleArg for LatencyHistoryEvent {}
impl ToArgs for LatencyHistoryEvent {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(match self {
LatencyHistoryEvent::ActiveDefragCycle => "active-defrag-cycle",
LatencyHistoryEvent::AofFsyncAlways => "aof-fsync-always",
LatencyHistoryEvent::AofStat => "aof-stat",
LatencyHistoryEvent::AofRewriteDiffWrite => "aof-rewrite-diff-write",
LatencyHistoryEvent::AofRename => "aof-rename",
LatencyHistoryEvent::AofWrite => "aof-write",
LatencyHistoryEvent::AofWriteActiveChild => "aof-write-active-child",
LatencyHistoryEvent::AofWriteAlone => "aof-write-alone",
LatencyHistoryEvent::AofWritePendingFsync => "aof-write-pending-fsync",
LatencyHistoryEvent::Command => "command",
LatencyHistoryEvent::ExpireCycle => "expire-cycle",
LatencyHistoryEvent::EvictionCycle => "eviction-cycle",
LatencyHistoryEvent::EvictionDel => "eviction-del",
LatencyHistoryEvent::FastCommand => "fast-command",
LatencyHistoryEvent::Fork => "fork",
LatencyHistoryEvent::RdbUnlinkTempFile => "rdb-unlink-temp-file",
});
}
}
/// Command Histogram for the [`latency_histogram`](ServerCommands::latency_histogram) commands.
#[derive(Default, Deserialize)]
pub struct CommandHistogram {
/// The total calls for that command.
pub calls: usize,
/// A map of time buckets:
/// - Each bucket represents a latency range.
/// - Each bucket covers twice the previous bucket's range.
/// - Empty buckets are not printed.
/// - The tracked latencies are between 1 microsecond and roughly 1 second.
/// - Everything above 1 sec is considered +Inf.
/// - At max there will be log2(1000000000)=30 buckets.
pub histogram_usec: HashMap,
}
/// Options for the [`lolwut`](ServerCommands::lolwut) command
#[derive(Default)]
pub struct LolWutOptions {
command_args: CommandArgs,
}
impl LolWutOptions {
#[must_use]
pub fn version(mut self, version: usize) -> Self {
Self {
command_args: self.command_args.arg("VERSION").arg(version).build(),
}
}
#[must_use]
pub fn optional_arg(mut self, arg: A) -> Self {
Self {
command_args: self.command_args.arg(arg).build(),
}
}
}
impl ToArgs for LolWutOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Result for the [`memory_stats`](ServerCommands::memory_stats) command.
#[derive(Debug, Deserialize)]
pub struct MemoryStats {
/// Peak memory consumed by Redis in bytes
/// (see [`INFO`](https://redis.io/commands/info)'s used_memory_peak)
#[serde(rename = "peak.allocated")]
pub peak_allocated: usize,
/// Total number of bytes allocated by Redis using its allocator
/// (see [`INFO`](https://redis.io/commands/info)'s used_memory)
#[serde(rename = "total.allocated")]
pub total_allocated: usize,
/// Initial amount of memory consumed by Redis at startup in bytes
/// (see [`INFO`](https://redis.io/commands/info)'s used_memory_startup)
#[serde(rename = "startup.allocated")]
#[serde(default)]
pub startup_allocated: usize,
/// Size in bytes of the replication backlog
/// (see [`INFO`](https://redis.io/commands/info)'s repl_backlog_active)
#[serde(rename = "replication.backlog")]
#[serde(default)]
pub replication_backlog: usize,
/// The total size in bytes of all replicas overheads
/// (output and query buffers, connection contexts)
#[serde(rename = "clients.slaves")]
#[serde(default)]
pub clients_slaves: usize,
/// The total size in bytes of all clients overheads
/// (output and query buffers, connection contexts)
#[serde(rename = "clients.normal")]
#[serde(default)]
pub clients_normal: usize,
/// Memory usage by cluster links
/// (Added in Redis 7.0, see [`INFO`](https://redis.io/commands/info)'s mem_cluster_links).
#[serde(rename = "cluster.links")]
#[serde(default)]
pub cluster_links: usize,
/// The summed size in bytes of AOF related buffers.
#[serde(rename = "aof.buffer")]
#[serde(default)]
pub aof_buffer: usize,
/// the summed size in bytes of the overheads of the Lua scripts' caches
#[serde(rename = "lua.caches")]
#[serde(default)]
pub lua_caches: usize,
/// the summed size in bytes of the overheads of the functions' caches
#[serde(rename = "functions.caches")]
#[serde(default)]
pub functions_caches: usize,
/// The sum of all overheads, i.e. `startup.allocated`, `replication.backlog`,
/// `clients.slaves`, `clients.normal`, `aof.buffer` and those of the internal data structures
/// that are used in managing the Redis keyspace (see [`INFO`](https://redis.io/commands/info)'s used_memory_overhead)
#[serde(rename = "overhead.total")]
#[serde(default)]
pub overhead_total: usize,
/// The total number of keys stored across all databases in the server
#[serde(rename = "keys.count")]
#[serde(default)]
pub keys_count: usize,
/// The ratio between net memory usage (`total.allocated` minus `startup.allocated`) and `keys.count`
#[serde(rename = "keys.bytes-per-key")]
#[serde(default)]
pub keys_bytes_per_key: usize,
/// The size in bytes of the dataset, i.e. `overhead.total` subtracted from `total.allocated`
/// (see [`INFO`](https://redis.io/commands/info)'s used_memory_dataset)
#[serde(rename = "dataset.bytes")]
#[serde(default)]
pub dataset_bytes: usize,
/// The percentage of `dataset.bytes` out of the net memory usage
#[serde(rename = "dataset.percentage")]
#[serde(default)]
pub dataset_percentage: f64,
/// The percentage of `peak.allocated` out of `total.allocated`
#[serde(rename = "peak.percentage")]
#[serde(default)]
pub peak_percentage: f64,
#[serde(rename = "allocator.allocated")]
#[serde(default)]
pub allocator_allocated: usize,
#[serde(rename = "allocator.active")]
#[serde(default)]
pub allocator_active: usize,
#[serde(rename = "allocator.resident")]
#[serde(default)]
pub allocator_resident: usize,
#[serde(rename = "allocator-fragmentation.ratio")]
#[serde(default)]
pub allocator_fragmentation_ratio: f64,
#[serde(rename = "allocator-fragmentation.bytes")]
#[serde(default)]
pub allocator_fragmentation_bytes: isize,
#[serde(rename = "allocator-rss.ratio")]
#[serde(default)]
pub allocator_rss_ratio: f64,
#[serde(rename = "allocator-rss.bytes")]
#[serde(default)]
pub allocator_rss_bytes: isize,
#[serde(rename = "rss-overhead.ratio")]
#[serde(default)]
pub rss_overhead_ratio: f64,
#[serde(rename = "rss-overhead.bytes")]
#[serde(default)]
pub rss_overhead_bytes: isize,
/// See [`INFO`](https://redis.io/commands/info)'s mem_fragmentation_ratio
#[serde(rename = "fragmentation")]
#[serde(default)]
pub fragmentation: f64,
#[serde(rename = "fragmentation.bytes")]
#[serde(default)]
pub fragmentation_bytes: isize,
}
/// Sub-result for the [`memory_stats`](ServerCommands::memory_stats) command.
#[derive(Debug, Deserialize)]
pub struct DatabaseOverhead {
pub overhead_hashtable_main: usize,
pub overhead_hashtable_expires: usize,
pub overhead_hashtable_slot_to_keys: usize,
}
/// Options for the [`memory_usage`](ServerCommands::memory_usage) command
#[derive(Default)]
pub struct MemoryUsageOptions {
command_args: CommandArgs,
}
impl MemoryUsageOptions {
/// For nested data types, the optional `samples` option can be provided,
/// where count is the number of sampled nested values.
/// By default, this option is set to 5.
/// To sample the all of the nested values, use samples(0).
#[must_use]
pub fn samples(mut self, count: usize) -> Self {
Self {
command_args: self.command_args.arg("SAMPLES").arg(count).build(),
}
}
}
impl ToArgs for MemoryUsageOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Module information result for the [`module_list`](ServerCommands::module_list) command.
#[derive(Deserialize)]
pub struct ModuleInfo {
/// Name of the module
pub name: String,
/// Version of the module
#[serde(default)]
pub version: u64,
}
/// Options for the [`module_load`](ServerCommands::module_load) command
#[derive(Default)]
pub struct ModuleLoadOptions {
command_args: CommandArgs,
args_added: bool,
}
impl ModuleLoadOptions {
/// You can use this optional associated function to provide the module with configuration directives.
/// This associated function can be called multiple times
#[must_use]
pub fn config(mut self, name: N, value: V) -> Self
where
N: SingleArg,
V: SingleArg,
{
if self.args_added {
panic!(
"associated function `config` should be called before associated function `arg`"
);
}
Self {
command_args: self.command_args.arg("CONFIG").arg(name).arg(value).build(),
args_added: false,
}
}
/// Any additional arguments are passed unmodified to the module.
/// This associated function can be called multiple times
#[must_use]
pub fn arg(mut self, arg: A) -> Self {
if !self.args_added {
Self {
command_args: self.command_args.arg("ARGS").arg(arg).build(),
args_added: true,
}
} else {
Self {
command_args: self.command_args.arg(arg).build(),
args_added: false,
}
}
}
}
impl ToArgs for ModuleLoadOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// options for the [`replicaof`](ServerCommands::replicaof) command.
pub struct ReplicaOfOptions {
command_args: CommandArgs,
}
impl ReplicaOfOptions {
/// If a Redis server is already acting as replica,
/// the command REPLICAOF NO ONE will turn off the replication,
/// turning the Redis server into a MASTER.
#[must_use]
pub fn no_one() -> Self {
Self {
command_args: CommandArgs::default().arg("NO").arg("ONE").build(),
}
}
/// In the proper form REPLICAOF hostname port will make the server
/// a replica of another server listening at the specified hostname and port.
#[must_use]
pub fn master(host: H, port: u16) -> Self {
Self {
command_args: CommandArgs::default().arg(host).arg(port).build(),
}
}
}
impl ToArgs for ReplicaOfOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Result for the [`role`](ServerCommands::role) command.
#[derive(Debug)]
pub enum RoleResult {
Master {
/// The current master replication offset,
/// which is an offset that masters and replicas share to understand,
/// in partial resynchronizations,
/// the part of the replication stream the replicas needs to fetch to continue.
master_replication_offset: usize,
/// information av=bout the connected replicas
replica_infos: Vec,
},
Replica {
/// The IP of the master.
master_ip: String,
/// The port number of the master.
master_port: u16,
/// The state of the replication from the point of view of the master
state: ReplicationState,
/// The amount of data received from the replica
/// so far in terms of master replication offset.
amount_data_received: isize,
},
Sentinel {
/// An array of master names monitored by this Sentinel instance.
master_names: Vec,
},
}
impl<'de> Deserialize<'de> for RoleResult {
fn deserialize(deserializer: D) -> std::result::Result
where
D: Deserializer<'de>,
{
struct RoleResultVisitor;
impl<'de> Visitor<'de> for RoleResultVisitor {
type Value = RoleResult;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("RoleResult")
}
fn visit_seq(self, mut seq: A) -> std::result::Result
where
A: SeqAccess<'de>,
{
let Some(role): Option<&str> = seq.next_element()? else {
return Err(de::Error::invalid_length(0, &"more elements in sequence"));
};
match role {
"master" => {
let Some(master_replication_offset): Option = seq.next_element()?
else {
return Err(de::Error::invalid_length(1, &"more elements in sequence"));
};
let Some(replica_infos): Option> = seq.next_element()?
else {
return Err(de::Error::invalid_length(2, &"more elements in sequence"));
};
Ok(RoleResult::Master {
master_replication_offset,
replica_infos,
})
}
"slave" => {
let Some(master_ip): Option = seq.next_element()? else {
return Err(de::Error::invalid_length(1, &"more elements in sequence"));
};
let Some(master_port): Option = seq.next_element()? else {
return Err(de::Error::invalid_length(2, &"more elements in sequence"));
};
let Some(state): Option = seq.next_element()? else {
return Err(de::Error::invalid_length(3, &"more elements in sequence"));
};
let Some(amount_data_received): Option = seq.next_element()? else {
return Err(de::Error::invalid_length(4, &"more elements in sequence"));
};
Ok(RoleResult::Replica {
master_ip,
master_port,
state,
amount_data_received,
})
}
"sentinel" => {
let Some(master_names): Option> = seq.next_element()? else {
return Err(de::Error::invalid_length(1, &"more elements in sequence"));
};
Ok(RoleResult::Sentinel { master_names })
}
_ => Err(de::Error::invalid_value(
de::Unexpected::Str(role),
&"expected `master`, `slave` or `sentinel`",
)),
}
}
}
deserializer.deserialize_seq(RoleResultVisitor)
}
}
/// Represents a connected replicas to a master
///
/// returned by the [`role`](ServerCommands::role) command.
#[derive(Debug, Deserialize)]
pub struct ReplicaInfo {
/// the replica IP
pub ip: String,
/// the replica port
pub port: u16,
/// the last acknowledged replication offset.
pub last_ack_offset: usize,
}
/// The state of the replication from the point of view of the master,
///
/// returned by the [`role`](ServerCommands::role) command.
#[derive(Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum ReplicationState {
/// the instance is in handshake with its master
Handshake,
/// the instance needs to connect to its master
None,
/// the instance in not active
Connect,
/// the master-replica connection is in progress
Connecting,
/// the master and replica are trying to perform the synchronization
Sync,
/// the replica is online
Connected,
/// instance state is unknown
Unknown,
}
/// options for the [`shutdown`](ServerCommands::shutdown) command.
#[derive(Default)]
pub struct ShutdownOptions {
command_args: CommandArgs,
}
impl ShutdownOptions {
/// - if save is true, will force a DB saving operation even if no save points are configured
/// - if save is false, will prevent a DB saving operation even if one or more save points are configured.
#[must_use]
pub fn save(mut self, save: bool) -> Self {
Self {
command_args: self
.command_args
.arg(if save { "SAVE" } else { "NOSAVE" })
.build(),
}
}
/// skips waiting for lagging replicas, i.e. it bypasses the first step in the shutdown sequence.
#[must_use]
pub fn now(mut self) -> Self {
Self {
command_args: self.command_args.arg("NOW").build(),
}
}
/// ignores any errors that would normally prevent the server from exiting.
#[must_use]
pub fn force(mut self) -> Self {
Self {
command_args: self.command_args.arg("FORCE").build(),
}
}
/// cancels an ongoing shutdown and cannot be combined with other flags.
#[must_use]
pub fn abort(mut self) -> Self {
Self {
command_args: self.command_args.arg("ABORT").build(),
}
}
}
impl ToArgs for ShutdownOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// options for the [`slowlog_get`](ServerCommands::slowlog_get) command.
#[derive(Default)]
pub struct SlowLogOptions {
command_args: CommandArgs,
}
impl SlowLogOptions {
/// limits the number of returned entries, so the command returns at most up to `count` entries.
#[must_use]
pub fn count(mut self, count: usize) -> Self {
Self {
command_args: self.command_args.arg(count).build(),
}
}
}
impl ToArgs for SlowLogOptions {
fn write_args(&self, args: &mut CommandArgs) {
args.arg(&self.command_args);
}
}
/// Result [`slowlog_get`](ServerCommands::slowlog_get) for the command.
#[derive(Deserialize)]
pub struct SlowLogEntry {
/// A unique progressive identifier for every slow log entry.
pub id: i64,
/// A unique progressive identifier for every slow log entry.
pub unix_timestamp: u32,
/// The amount of time needed for its execution, in microseconds.
pub execution_time_micros: u64,
/// The array composing the arguments of the command.
pub command: Vec,
/// Client IP address and port.
pub client_address: String,
/// Client name if set via the CLIENT SETNAME command.
pub client_name: String,
}