Skip to main content

glide/commands/
core.rs

1// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0
2//! GLIDE's command API: [`AsyncCommands`] and [`Commands`], and their typed
3//! counterparts [`AsyncTypedCommands`] and [`TypedCommands`].
4//!
5//! One command table (below) defines all four traits via the
6//! `implement_commands!` macro. Entries are source-compatible with
7//! redis-rs's command table: same method names, generic parameter
8//! order, bounds, and argument lists, so migrated call sites (including
9//! turbofish annotations) compile unchanged.
10//!
11//! Each entry carries the command body (mirroring redis-rs's
12//! `implement_commands!`), so the wire encoding matches redis-rs's
13//! (except for the deliberate differences pinned in `differences.json` (e.g.
14//! `hset_multiple` sends `HSET`, not `HMSET`); signature parity is enforced by
15//! the `parity_tests` module (`src/parity_tests/`).
16//!
17//! The built command is handed to glide-core **by value** — the same
18//! zero-extra-copy path as the rest of the client. Methods take `&self` (the clients are cheaply
19//! cloneable handles); migrated `&mut` call sites still compile via
20//! auto-borrow.
21//!
22//! Two deliberate deviations trade redis-rs parity for performance and
23//! clarity (parity here is a command-surface contract, not a
24//! connection-plumbing one):
25//! * the traits are **not** coupled to the `redis` crate's connection-object
26//!   (`ConnectionLike`) machinery — dispatch is GLIDE's owned-send path only;
27//! * the `scan*` methods take `&self` and return GLIDE's own iterators
28//!   ([`crate::commands::scan`]) with the familiar `next_item()` /
29//!   `Iterator` shape, each page dispatched by value (no per-page copies).
30//!
31//! Commands beyond this table (the remaining stream commands, geo search,
32//! `JSON.*`, …) live in the per-family extension traits in [`crate::commands`].
33//!
34//! Maintenance: add or adjust entries in the `implement_commands!`
35//! invocation at the bottom of this file; the parity test will flag any
36//! divergence from redis-rs's command table (see DEVELOPER.md).
37
38use crate::ValkeyFuture;
39use crate::cmd::Cmd;
40use crate::commands::geo::GeoCoord;
41use crate::commands::geo::GeoUnit;
42use crate::commands::options::CopyOptions;
43use crate::commands::options::Direction;
44use crate::commands::options::ExpireOption;
45use crate::commands::options::Expiry;
46use crate::commands::options::FlushAllOptions;
47use crate::commands::options::FlushDbOptions;
48use crate::commands::options::HashFieldExpirationOptions;
49use crate::commands::options::LposOptions;
50use crate::commands::options::SetOptions;
51use crate::commands::stream::StreamAddOptions;
52use crate::commands::stream::StreamAutoClaimOptions;
53use crate::commands::stream::StreamAutoClaimReply;
54use crate::commands::stream::StreamClaimOptions;
55use crate::commands::stream::StreamClaimReply;
56use crate::commands::stream::StreamInfoConsumersReply;
57use crate::commands::stream::StreamInfoGroupsReply;
58use crate::commands::stream::StreamInfoStreamReply;
59use crate::commands::stream::StreamMaxlen;
60use crate::commands::stream::StreamPendingCountReply;
61use crate::commands::stream::StreamPendingReply;
62use crate::commands::stream::StreamRangeReply;
63use crate::commands::stream::StreamReadOptions;
64use crate::commands::stream::StreamReadReply;
65use crate::commands::stream::StreamTrimOptions;
66use crate::pipeline::Pipeline;
67use crate::types::IntegerReplyOrNoOp;
68use crate::value::FromValkeyValue;
69use crate::value::ValkeyValue;
70use crate::write::ToSingleValkeyArg;
71use crate::write::ToValkeyArgs;
72use crate::write::ValkeyNumericBehavior;
73use std::collections::HashSet;
74
75// Only exposed by sync commands.
76#[cfg(feature = "sync")]
77use crate::ValkeyResult;
78
79/// Expands to the scan iterators, shared by every command trait:
80/// - `$iter` builds the iterator from the client and the command's prefix and suffix
81///   arguments (e.g. `SyncScanIter::new`).
82/// - `$ret` is the iterator's return type (e.g. `ValkeyResult<SyncScanIter<'s, Self, RV>>`),
83///   which may name each method's `'s` lifetime and `RV` result type.
84///
85/// These scan iterators mirror redis-rs's (same names and arguments, and the same generics
86/// apart from the `'s` lifetime) but deliberately deviate: `&self` receivers returning
87/// GLIDE's own iterator types (same `next_item()` call shape), with every page dispatched
88/// by value on the owned-send path.
89///
90/// Follows the structure of redis-rs's `implement_iterators!`.
91macro_rules! implement_iterators {
92    ($iter:expr, $ret:ty) => {
93        /// Cursor-driven `SCAN` over the whole keyspace.
94        // TODO #6872: Use `GlideClusterClient::cluster_scan` for cluster iteration.
95        // TODO #7082: Add `scan_options` (redis-rs's `ScanOptions`) for `SCAN` `COUNT` / `TYPE`.
96        #[inline]
97        fn scan<'s, RV: FromValkeyValue + 's>(&'s self) -> $ret {
98            ($iter)(self, vec![b"SCAN".to_vec()], Vec::new())
99        }
100
101        /// Cursor-driven `SCAN` over the keyspace, filtered by a `MATCH` pattern.
102        // TODO #6872: Use `GlideClusterClient::cluster_scan` for cluster iteration.
103        #[inline]
104        fn scan_match<'s, P: ToSingleValkeyArg, RV: FromValkeyValue + 's>(
105            &'s self,
106            pattern: P,
107        ) -> $ret {
108            let mut suffix = vec![b"MATCH".to_vec()];
109            pattern.write_valkey_args(&mut suffix);
110            ($iter)(self, vec![b"SCAN".to_vec()], suffix)
111        }
112
113        /// Cursor-driven `HSCAN` over a hash's fields and values.
114        #[inline]
115        fn hscan<'s, K: ToSingleValkeyArg, RV: FromValkeyValue + 's>(&'s self, key: K) -> $ret {
116            let mut prefix = vec![b"HSCAN".to_vec()];
117            key.write_valkey_args(&mut prefix);
118            ($iter)(self, prefix, Vec::new())
119        }
120
121        /// Cursor-driven `HSCAN`, filtered by a field-name `MATCH` pattern.
122        #[inline]
123        fn hscan_match<'s, K: ToSingleValkeyArg, P: ToSingleValkeyArg, RV: FromValkeyValue + 's>(
124            &'s self,
125            key: K,
126            pattern: P,
127        ) -> $ret {
128            let mut prefix = vec![b"HSCAN".to_vec()];
129            key.write_valkey_args(&mut prefix);
130            let mut suffix = vec![b"MATCH".to_vec()];
131            pattern.write_valkey_args(&mut suffix);
132            ($iter)(self, prefix, suffix)
133        }
134
135        /// Cursor-driven `SSCAN` over a set's members.
136        #[inline]
137        fn sscan<'s, K: ToSingleValkeyArg, RV: FromValkeyValue + 's>(&'s self, key: K) -> $ret {
138            let mut prefix = vec![b"SSCAN".to_vec()];
139            key.write_valkey_args(&mut prefix);
140            ($iter)(self, prefix, Vec::new())
141        }
142
143        /// Cursor-driven `SSCAN`, filtered by a `MATCH` pattern.
144        #[inline]
145        fn sscan_match<'s, K: ToSingleValkeyArg, P: ToSingleValkeyArg, RV: FromValkeyValue + 's>(
146            &'s self,
147            key: K,
148            pattern: P,
149        ) -> $ret {
150            let mut prefix = vec![b"SSCAN".to_vec()];
151            key.write_valkey_args(&mut prefix);
152            let mut suffix = vec![b"MATCH".to_vec()];
153            pattern.write_valkey_args(&mut suffix);
154            ($iter)(self, prefix, suffix)
155        }
156
157        /// Cursor-driven `ZSCAN` over a sorted set's members and scores.
158        #[inline]
159        fn zscan<'s, K: ToSingleValkeyArg, RV: FromValkeyValue + 's>(&'s self, key: K) -> $ret {
160            let mut prefix = vec![b"ZSCAN".to_vec()];
161            key.write_valkey_args(&mut prefix);
162            ($iter)(self, prefix, Vec::new())
163        }
164
165        /// Cursor-driven `ZSCAN`, filtered by a `MATCH` pattern.
166        #[inline]
167        fn zscan_match<'s, K: ToSingleValkeyArg, P: ToSingleValkeyArg, RV: FromValkeyValue + 's>(
168            &'s self,
169            key: K,
170            pattern: P,
171        ) -> $ret {
172            let mut prefix = vec![b"ZSCAN".to_vec()];
173            key.write_valkey_args(&mut prefix);
174            let mut suffix = vec![b"MATCH".to_vec()];
175            pattern.write_valkey_args(&mut suffix);
176            ($iter)(self, prefix, suffix)
177        }
178    };
179}
180
181/// Expands one command table entry into an [`AsyncTypedCommands`] method.
182///
183/// Follows the structure of redis-rs's `implement_command_async!`.
184macro_rules! implement_typed_command_async {
185    (
186        $lifetime:lifetime
187        $(#[$attr:meta])*
188        fn $name:ident <$($g:ident: $b:ident),*> ($($arg:ident: $ty:ty),*) Generic
189    ) => {
190        $(#[$attr])*
191        #[inline]
192        #[allow(deprecated)]
193        #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
194        fn $name<$lifetime, RV: FromValkeyValue, $($g: $b + Send + Sync + $lifetime,)*>(
195            &$lifetime self $(, $arg: $ty)*
196        ) -> ValkeyFuture<$lifetime, RV> {
197            let cmd = Cmd::$name($($arg),*);
198            Box::pin(async move { RV::from_owned_valkey_value(self.glide_dispatch_command(cmd).await?) })
199        }
200    };
201    (
202        $lifetime:lifetime
203        $(#[$attr:meta])*
204        fn $name:ident <$($g:ident: $b:ident),*> ($($arg:ident: $ty:ty),*) $ret:ty
205    ) => {
206        $(#[$attr])*
207        #[inline]
208        #[allow(deprecated)]
209        // Table return types are parenthesized, as in redis-rs (e.g. `-> (usize)`).
210        #[allow(unused_parens)]
211        #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
212        fn $name<$lifetime, $($g: $b + Send + Sync + $lifetime,)*>(
213            &$lifetime self $(, $arg: $ty)*
214        ) -> ValkeyFuture<$lifetime, $ret> {
215            let cmd = Cmd::$name($($arg),*);
216            Box::pin(async move {
217                <$ret as FromValkeyValue>::from_owned_valkey_value(self.glide_dispatch_command(cmd).await?)
218            })
219        }
220    };
221}
222
223/// Expands one command table entry into a [`TypedCommands`] method.
224///
225/// Blocking counterpart of `implement_typed_command_async!`.
226#[cfg(feature = "sync")]
227macro_rules! implement_typed_command_sync {
228    (
229        $lifetime:lifetime
230        $(#[$attr:meta])*
231        fn $name:ident <$($g:ident: $b:ident),*> ($($arg:ident: $ty:ty),*) Generic
232    ) => {
233        $(#[$attr])*
234        #[inline]
235        #[allow(deprecated)]
236        #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
237        fn $name<$lifetime, RV: FromValkeyValue, $($g: $b,)*>(
238            &self $(, $arg: $ty)*
239        ) -> ValkeyResult<RV> {
240            RV::from_owned_valkey_value(self.glide_dispatch_command(Cmd::$name($($arg),*))?)
241        }
242    };
243    (
244        $lifetime:lifetime
245        $(#[$attr:meta])*
246        fn $name:ident <$($g:ident: $b:ident),*> ($($arg:ident: $ty:ty),*) $ret:ty
247    ) => {
248        $(#[$attr])*
249        #[inline]
250        #[allow(deprecated)]
251        // Table return types are parenthesized, as in redis-rs (e.g. `-> (usize)`).
252        #[allow(unused_parens)]
253        #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
254        fn $name<$lifetime, $($g: $b,)*>(
255            &self $(, $arg: $ty)*
256        ) -> ValkeyResult<$ret> {
257            <$ret as FromValkeyValue>::from_owned_valkey_value(
258                self.glide_dispatch_command(Cmd::$name($($arg),*))?,
259            )
260        }
261    };
262}
263
264/// Async command dispatch shared by [`AsyncCommands`] and [`AsyncTypedCommands`].
265///
266/// Both command traits extend this trait so a generic bound on one of them brings
267/// only that trait's methods into scope. Implemented by GLIDE's async clients; not
268/// implementable outside the crate.
269#[doc(hidden)]
270#[sealed::sealed(pub(crate))]
271pub trait CommandDispatch: Send + Sync + Sized {
272    /// Send an already-built command by value.
273    fn glide_dispatch_command<'a>(&'a self, cmd: Cmd) -> ValkeyFuture<'a, ValkeyValue>;
274}
275
276/// Blocking command dispatch shared by [`Commands`] and [`TypedCommands`].
277///
278/// Blocking counterpart of [`CommandDispatch`].
279#[cfg(feature = "sync")]
280#[doc(hidden)]
281#[sealed::sealed(pub(crate))]
282pub trait SyncCommandDispatch: Sized {
283    /// Send an already-built command by value.
284    fn glide_dispatch_command(&self, cmd: Cmd) -> ValkeyResult<ValkeyValue>;
285}
286
287/// Defines the [`AsyncCommands`], [`Commands`], [`AsyncTypedCommands`], and
288/// [`TypedCommands`] traits from one command table.
289///
290/// Each `fn name<G: Bound>(args) -> (T) { body }` entry expands to an async
291/// method, its blocking counterpart, and the typed async and blocking
292/// methods returning `T`.
293macro_rules! implement_commands {
294    (
295        $lifetime:lifetime;
296        $(
297            $(#[$attr:meta])*
298            fn $name:ident <$($g:ident: $b:ident),*> ($($arg:ident: $ty:ty),*) -> $ret:tt $body:block
299        )*
300    ) => {
301        /// Command methods, one per table entry.
302        ///
303        /// For example, the `pttl` table entry expands to:
304        ///
305        /// ```ignore
306        /// impl Cmd {
307        ///     pub(crate) fn pttl<K: ToSingleValkeyArg>(key: K) -> Cmd {
308        ///         build_cmd!("PTTL", key)
309        ///     }
310        /// }
311        /// ```
312        impl Cmd {
313            $(
314                $(#[$attr])*
315                #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
316                pub(crate) fn $name<$lifetime, $($g: $b),*>($($arg: $ty),*) -> Self $body
317            )*
318        }
319
320        /// Pipeline builder methods, one per table entry.
321        ///
322        /// For example, the `pttl` table entry expands to:
323        ///
324        /// ```ignore
325        /// impl Pipeline {
326        ///     pub fn pttl<K: ToSingleValkeyArg>(&mut self, key: K) -> &mut Self {
327        ///         self.add_command(Cmd::pttl(key));
328        ///         self
329        ///     }
330        /// }
331        /// ```
332        impl Pipeline {
333            $(
334                $(#[$attr])*
335                #[inline]
336                #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
337                pub fn $name<$lifetime, $($g: $b),*>(&mut self $(, $arg: $ty)*) -> &mut Self {
338                    self.add_command(Cmd::$name($($arg),*));
339                    self
340                }
341            )*
342        }
343
344        /// **GLIDE's async command API.**
345        ///
346        /// Implemented by [`crate::GlideClient`] and
347        /// [`crate::GlideClusterClient`] — see the [module docs](self).
348        ///
349        /// Deliberately **not** tied to the `redis` crate's connection-object
350        /// traits: every method hands its command to the client by value,
351        /// GLIDE's zero-extra-copy path.
352        pub trait AsyncCommands: CommandDispatch {
353            /// Send an already-built command **by value** (no clone). A
354            /// zero-extra-copy escape hatch for custom commands with large
355            /// payloads.
356            ///
357            /// Prefer the typed commands (e.g. [`get`](Self::get)).
358            /// Use this method only for commands GLIDE does not implement.
359            #[inline]
360            fn glide_send_command<'a>(&'a self, cmd: Cmd) -> ValkeyFuture<'a, ValkeyValue> {
361                self.glide_dispatch_command(cmd)
362            }
363
364            /// Typed escape hatch: send an already-built [`Cmd`] by value and
365            /// decode the reply into `RV`. An alternative to
366            /// `cmd(...).query_async(con)` ([`Cmd::query_async`] delegates here)
367            /// — same decode, no connection-object machinery, no payload copy.
368            ///
369            /// Prefer the typed commands (e.g. [`get`](Self::get)).
370            /// Use this method only for commands GLIDE does not implement.
371            #[inline]
372            fn glide_send_command_as<'a, RV: FromValkeyValue>(&'a self, cmd: Cmd) -> ValkeyFuture<'a, RV> {
373                Box::pin(async move { RV::from_owned_valkey_value(self.glide_send_command(cmd).await?) })
374            }
375
376            $(
377                $(#[$attr])*
378                #[inline]
379                #[allow(deprecated)]
380                #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
381                fn $name<$lifetime, $($g: $b + Send + Sync + $lifetime,)* RV>(
382                    &$lifetime self $(, $arg: $ty)*
383                ) -> ValkeyFuture<$lifetime, RV>
384                where
385                    RV: FromValkeyValue,
386                {
387                    let cmd = Cmd::$name($($arg),*);
388                    Box::pin(async move { RV::from_owned_valkey_value(self.glide_dispatch_command(cmd).await?) })
389                }
390            )*
391
392            implement_iterators!(
393                |con, prefix, suffix| Box::pin(crate::commands::scan::ScanIter::new(con, prefix, suffix)),
394                ValkeyFuture<'s, crate::commands::scan::ScanIter<'s, Self, RV>>
395            );
396        }
397
398        impl<T: CommandDispatch> AsyncCommands for T {}
399
400        /// **GLIDE's blocking command API.**
401        ///
402        /// Blocking counterpart of [`AsyncCommands`] — see there and the
403        /// [module docs](self). Implemented by
404        /// [`crate::sync::SyncGlideClient`] and
405        /// [`crate::sync::SyncGlideClusterClient`].
406        ///
407        /// These methods block on the internal runtime and **must not be
408        /// called from within an async context** (doing so panics with
409        /// tokio's "cannot block the current thread from within a runtime");
410        /// use [`AsyncCommands`] on the async clients there instead.
411        #[cfg(feature = "sync")]
412        pub trait Commands: SyncCommandDispatch {
413            /// Send an already-built command **by value** (no clone).
414            ///
415            /// Prefer the typed commands (e.g. [`get`](Self::get)).
416            /// Use this method only for commands GLIDE does not implement.
417            #[inline]
418            fn glide_send_command(&self, cmd: Cmd) -> ValkeyResult<ValkeyValue> {
419                self.glide_dispatch_command(cmd)
420            }
421
422            /// Typed escape hatch (blocking counterpart of the async
423            /// `glide_send_command_as`): send an already-built [`Cmd`] by value and
424            /// decode the reply into `RV`.
425            ///
426            /// Prefer the typed commands (e.g. [`get`](Self::get)).
427            /// Use this method only for commands GLIDE does not implement.
428            #[inline]
429            fn glide_send_command_as<RV: FromValkeyValue>(&self, cmd: Cmd) -> ValkeyResult<RV> {
430                RV::from_owned_valkey_value(self.glide_send_command(cmd)?)
431            }
432
433            $(
434                $(#[$attr])*
435                #[inline]
436                #[allow(deprecated)]
437                #[allow(clippy::extra_unused_lifetimes, clippy::needless_lifetimes)]
438                fn $name<$lifetime, $($g: $b,)* RV: FromValkeyValue>(
439                    &self $(, $arg: $ty)*
440                ) -> ValkeyResult<RV> {
441                    RV::from_owned_valkey_value(self.glide_dispatch_command(Cmd::$name($($arg),*))?)
442                }
443            )*
444
445            implement_iterators!(
446                crate::commands::scan::SyncScanIter::new,
447                ValkeyResult<crate::commands::scan::SyncScanIter<'s, Self, RV>>
448            );
449        }
450
451        #[cfg(feature = "sync")]
452        impl<T: SyncCommandDispatch> Commands for T {}
453
454        /// **GLIDE's typed async command API.**
455        ///
456        /// Like [`AsyncCommands`], but each method returns a concrete type
457        /// (e.g. [`get`](Self::get) returns `Option<String>`), so no type
458        /// annotation is needed. To choose the return type, use
459        /// [`AsyncCommands`] instead.
460        ///
461        /// Implemented for every [`AsyncCommands`] type. Neither trait extends
462        /// the other, so a generic bound on one brings only its methods into
463        /// scope. Import only one of the two traits: their methods share
464        /// names, so calls are ambiguous when both are in scope (as with
465        /// redis-rs's `AsyncCommands` and `AsyncTypedCommands`).
466        pub trait AsyncTypedCommands: CommandDispatch {
467            $(
468                implement_typed_command_async! {
469                    $lifetime
470                    $(#[$attr])*
471                    fn $name<$($g: $b),*>($($arg: $ty),*) $ret
472                }
473            )*
474
475            implement_iterators!(
476                |con, prefix, suffix| Box::pin(crate::commands::scan::ScanIter::new(con, prefix, suffix)),
477                ValkeyFuture<'s, crate::commands::scan::ScanIter<'s, Self, RV>>
478            );
479        }
480
481        impl<T: CommandDispatch> AsyncTypedCommands for T {}
482
483        /// **GLIDE's typed blocking command API.**
484        ///
485        /// Blocking counterpart of [`AsyncTypedCommands`]; implemented for
486        /// every [`Commands`] type. Import only one of [`Commands`] and
487        /// [`TypedCommands`] (see [`AsyncTypedCommands`]).
488        #[cfg(feature = "sync")]
489        pub trait TypedCommands: SyncCommandDispatch {
490            $(
491                implement_typed_command_sync! {
492                    $lifetime
493                    $(#[$attr])*
494                    fn $name<$($g: $b),*>($($arg: $ty),*) $ret
495                }
496            )*
497
498            implement_iterators!(
499                crate::commands::scan::SyncScanIter::new,
500                ValkeyResult<crate::commands::scan::SyncScanIter<'s, Self, RV>>
501            );
502        }
503
504        #[cfg(feature = "sync")]
505        impl<T: SyncCommandDispatch> TypedCommands for T {}
506    };
507}
508
509/// Split `&[(key, weight)]` into separate key and weight slices.
510fn unzip_weights<K, W>(items: &[(K, W)]) -> (Vec<&K>, Vec<&W>) {
511    items.iter().map(|(key, weight)| (key, weight)).unzip()
512}
513
514/// Build an owned [`Cmd`]: `build_cmd!(NAME, arg1, arg2, …)`.
515macro_rules! build_cmd {
516    ($name:expr $(,)?) => {
517        $crate::cmd::cmd($name)
518    };
519    ($name:expr $(, $arg:expr)* $(,)?) => {{
520        let mut command = $crate::cmd::cmd($name);
521        $( command.arg($arg); )*
522        command
523    }};
524}
525
526implement_commands! {
527    'a;
528
529    // ==== Strings =======================================================
530
531    /// `GET`.
532    fn get<K: ToSingleValkeyArg>(key: K) -> (Option<String>) {
533        build_cmd!("GET", key)
534    }
535
536    /// `MGET`.
537    fn mget<K: ToValkeyArgs>(key: K) -> (Vec<Option<String>>) {
538        build_cmd!("MGET", key)
539    }
540
541    /// `SET`.
542    fn set<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, value: V) -> (()) {
543        build_cmd!("SET", key, value)
544    }
545
546    /// `SET`.
547    fn set_options<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, value: V, options: SetOptions) -> (Option<String>) {
548        build_cmd!("SET", key, value, options)
549    }
550
551    /// `MSET`.
552    fn mset<K: ToValkeyArgs, V: ToValkeyArgs>(items: &'a [(K, V)]) -> (()) {
553        build_cmd!("MSET", items)
554    }
555
556    /// `SETEX`.
557    fn set_ex<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, value: V, seconds: u64) -> (()) {
558        build_cmd!("SETEX", key, seconds, value)
559    }
560
561    /// `PSETEX`.
562    fn pset_ex<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, value: V, milliseconds: u64) -> (()) {
563        build_cmd!("PSETEX", key, milliseconds, value)
564    }
565
566    /// `SETNX`.
567    fn set_nx<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, value: V) -> (bool) {
568        build_cmd!("SETNX", key, value)
569    }
570
571    /// `MSETNX`.
572    fn mset_nx<K: ToValkeyArgs, V: ToValkeyArgs>(items: &'a [(K, V)]) -> (bool) {
573        build_cmd!("MSETNX", items)
574    }
575
576    /// `GETSET`.
577    fn getset<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, value: V) -> (Option<String>) {
578        build_cmd!("GETSET", key, value)
579    }
580
581    /// `GETRANGE`.
582    fn getrange<K: ToSingleValkeyArg>(key: K, from: isize, to: isize) -> (String) {
583        build_cmd!("GETRANGE", key, from, to)
584    }
585
586    /// `SETRANGE`.
587    fn setrange<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, offset: isize, value: V) -> (usize) {
588        build_cmd!("SETRANGE", key, offset, value)
589    }
590
591    /// `GETEX`.
592    fn get_ex<K: ToSingleValkeyArg>(key: K, expire_at: Expiry) -> (Option<String>) {
593        build_cmd!("GETEX", key, expire_at)
594    }
595
596    /// `GETDEL`.
597    fn get_del<K: ToSingleValkeyArg>(key: K) -> (Option<String>) {
598        build_cmd!("GETDEL", key)
599    }
600
601    /// `APPEND`.
602    fn append<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, value: V) -> (usize) {
603        build_cmd!("APPEND", key, value)
604    }
605
606    /// `INCRBY`/`INCRBYFLOAT`
607    // TODO #7262: redis-rs's typed `isize` return cannot represent the
608    // `INCRBYFLOAT` (float `delta`) reply.
609    fn incr<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, delta: V) -> (isize) {
610        build_cmd!(if delta.describe_numeric_behavior() == ValkeyNumericBehavior::NumberIsFloat {
611            "INCRBYFLOAT"
612        } else {
613            "INCRBY"
614        }, key, delta)
615    }
616
617    /// `DECRBY`.
618    fn decr<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, delta: V) -> (isize) {
619        build_cmd!("DECRBY", key, delta)
620    }
621
622    /// `STRLEN`.
623    fn strlen<K: ToSingleValkeyArg>(key: K) -> (usize) {
624        build_cmd!("STRLEN", key)
625    }
626
627    // ==== Keys & expiry =================================================
628
629    /// `KEYS`.
630    fn keys<K: ToSingleValkeyArg>(key: K) -> (Vec<String>) {
631        build_cmd!("KEYS", key)
632    }
633
634    /// `DEL`.
635    fn del<K: ToValkeyArgs>(key: K) -> (usize) {
636        build_cmd!("DEL", key)
637    }
638
639    /// `EXISTS`.
640    fn exists<K: ToValkeyArgs>(key: K) -> (bool) {
641        build_cmd!("EXISTS", key)
642    }
643
644    /// `TYPE`.
645    fn key_type<K: ToSingleValkeyArg>(key: K) -> (crate::types::ValueType) {
646        build_cmd!("TYPE", key)
647    }
648
649    /// `EXPIRE`.
650    fn expire<K: ToSingleValkeyArg>(key: K, seconds: i64) -> (bool) {
651        build_cmd!("EXPIRE", key, seconds)
652    }
653
654    /// `EXPIREAT`.
655    fn expire_at<K: ToSingleValkeyArg>(key: K, ts: i64) -> (bool) {
656        build_cmd!("EXPIREAT", key, ts)
657    }
658
659    /// `EXPIRETIME`.
660    fn expire_time<K: ToSingleValkeyArg>(key: K) -> (IntegerReplyOrNoOp) {
661        build_cmd!("EXPIRETIME", key)
662    }
663
664    /// `PEXPIRE`.
665    fn pexpire<K: ToSingleValkeyArg>(key: K, ms: i64) -> (bool) {
666        build_cmd!("PEXPIRE", key, ms)
667    }
668
669    /// `PEXPIREAT`.
670    fn pexpire_at<K: ToSingleValkeyArg>(key: K, ts: i64) -> (bool) {
671        build_cmd!("PEXPIREAT", key, ts)
672    }
673
674    /// `PEXPIRETIME`.
675    fn pexpire_time<K: ToSingleValkeyArg>(key: K) -> (IntegerReplyOrNoOp) {
676        build_cmd!("PEXPIRETIME", key)
677    }
678
679    /// `PERSIST`.
680    fn persist<K: ToSingleValkeyArg>(key: K) -> (bool) {
681        build_cmd!("PERSIST", key)
682    }
683
684    /// `TTL`.
685    fn ttl<K: ToSingleValkeyArg>(key: K) -> (IntegerReplyOrNoOp) {
686        build_cmd!("TTL", key)
687    }
688
689    /// `PTTL`.
690    fn pttl<K: ToSingleValkeyArg>(key: K) -> (IntegerReplyOrNoOp) {
691        build_cmd!("PTTL", key)
692    }
693
694    /// `RENAME`.
695    fn rename<K: ToSingleValkeyArg, N: ToSingleValkeyArg>(key: K, new_key: N) -> (()) {
696        build_cmd!("RENAME", key, new_key)
697    }
698
699    /// `RENAMENX`.
700    fn rename_nx<K: ToSingleValkeyArg, N: ToSingleValkeyArg>(key: K, new_key: N) -> (bool) {
701        build_cmd!("RENAMENX", key, new_key)
702    }
703
704    /// `UNLINK`.
705    fn unlink<K: ToValkeyArgs>(key: K) -> (usize) {
706        build_cmd!("UNLINK", key)
707    }
708
709    /// `OBJECT ENCODING`.
710    fn object_encoding<K: ToSingleValkeyArg>(key: K) -> (Option<String>) {
711        build_cmd!("OBJECT", "ENCODING", key)
712    }
713
714    /// `OBJECT IDLETIME`.
715    fn object_idletime<K: ToSingleValkeyArg>(key: K) -> (Option<usize>) {
716        build_cmd!("OBJECT", "IDLETIME", key)
717    }
718
719    /// `OBJECT FREQ`.
720    fn object_freq<K: ToSingleValkeyArg>(key: K) -> (Option<usize>) {
721        build_cmd!("OBJECT", "FREQ", key)
722    }
723
724    /// `OBJECT REFCOUNT`.
725    fn object_refcount<K: ToSingleValkeyArg>(key: K) -> (Option<usize>) {
726        build_cmd!("OBJECT", "REFCOUNT", key)
727    }
728
729    /// `COPY`.
730    fn copy<KSrc: ToSingleValkeyArg, KDst: ToSingleValkeyArg, Db: ToString>(source: KSrc, destination: KDst, options: CopyOptions<Db>) -> (bool) {
731        build_cmd!("COPY", source, destination, options)
732    }
733
734    // ==== Lists =========================================================
735
736    /// `BLMOVE`.
737    fn blmove<S: ToSingleValkeyArg, D: ToSingleValkeyArg>(srckey: S, dstkey: D, src_dir: Direction, dst_dir: Direction, timeout: f64) -> (Option<String>) {
738        build_cmd!("BLMOVE", srckey, dstkey, src_dir, dst_dir, timeout)
739    }
740
741    /// `BLMPOP`.
742    fn blmpop<K: ToValkeyArgs>(timeout: f64, numkeys: usize, key: K, dir: Direction, count: usize) -> (Option<(String, Vec<String>)>) {
743        build_cmd!("BLMPOP", timeout, numkeys, key, dir, "COUNT", count)
744    }
745
746    /// `BLPOP`.
747    fn blpop<K: ToValkeyArgs>(key: K, timeout: f64) -> (Option<[String; 2]>) {
748        build_cmd!("BLPOP", key, timeout)
749    }
750
751    /// `BRPOP`.
752    fn brpop<K: ToValkeyArgs>(key: K, timeout: f64) -> (Option<[String; 2]>) {
753        build_cmd!("BRPOP", key, timeout)
754    }
755
756    /// `BRPOPLPUSH`.
757    fn brpoplpush<S: ToSingleValkeyArg, D: ToSingleValkeyArg>(srckey: S, dstkey: D, timeout: f64) -> (Option<String>) {
758        build_cmd!("BRPOPLPUSH", srckey, dstkey, timeout)
759    }
760
761    /// `LINDEX`.
762    fn lindex<K: ToSingleValkeyArg>(key: K, index: isize) -> (Option<String>) {
763        build_cmd!("LINDEX", key, index)
764    }
765
766    /// `LINSERT`.
767    fn linsert_before<K: ToSingleValkeyArg, P: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, pivot: P, value: V) -> (isize) {
768        build_cmd!("LINSERT", key, "BEFORE", pivot, value)
769    }
770
771    /// `LINSERT`.
772    fn linsert_after<K: ToSingleValkeyArg, P: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, pivot: P, value: V) -> (isize) {
773        build_cmd!("LINSERT", key, "AFTER", pivot, value)
774    }
775
776    /// `LLEN`.
777    fn llen<K: ToSingleValkeyArg>(key: K) -> (usize) {
778        build_cmd!("LLEN", key)
779    }
780
781    /// `LMOVE`.
782    fn lmove<S: ToSingleValkeyArg, D: ToSingleValkeyArg>(srckey: S, dstkey: D, src_dir: Direction, dst_dir: Direction) -> (String) {
783        build_cmd!("LMOVE", srckey, dstkey, src_dir, dst_dir)
784    }
785
786    /// `LMPOP`.
787    fn lmpop<K: ToValkeyArgs>(numkeys: usize, key: K, dir: Direction, count: usize) -> (Option<(String, Vec<String>)>) {
788        build_cmd!("LMPOP", numkeys, key, dir, "COUNT", count)
789    }
790
791    /// `LPOP`.
792    fn lpop<K: ToSingleValkeyArg>(key: K, count: Option<core::num::NonZeroUsize>) -> Generic {
793        build_cmd!("LPOP", key, count)
794    }
795
796    /// `LPOS`.
797    fn lpos<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, value: V, options: LposOptions) -> Generic {
798        build_cmd!("LPOS", key, value, options)
799    }
800
801    /// `LPUSH`.
802    fn lpush<K: ToSingleValkeyArg, V: ToValkeyArgs>(key: K, value: V) -> (usize) {
803        build_cmd!("LPUSH", key, value)
804    }
805
806    /// `LPUSHX`.
807    fn lpush_exists<K: ToSingleValkeyArg, V: ToValkeyArgs>(key: K, value: V) -> (usize) {
808        build_cmd!("LPUSHX", key, value)
809    }
810
811    /// `LRANGE`.
812    fn lrange<K: ToSingleValkeyArg>(key: K, start: isize, stop: isize) -> (Vec<String>) {
813        build_cmd!("LRANGE", key, start, stop)
814    }
815
816    /// `LREM`.
817    fn lrem<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, count: isize, value: V) -> (usize) {
818        build_cmd!("LREM", key, count, value)
819    }
820
821    /// `LTRIM`.
822    fn ltrim<K: ToSingleValkeyArg>(key: K, start: isize, stop: isize) -> (()) {
823        build_cmd!("LTRIM", key, start, stop)
824    }
825
826    /// `LSET`.
827    fn lset<K: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, index: isize, value: V) -> (()) {
828        build_cmd!("LSET", key, index, value)
829    }
830
831    /// `RPOP`.
832    fn rpop<K: ToSingleValkeyArg>(key: K, count: Option<core::num::NonZeroUsize>) -> Generic {
833        build_cmd!("RPOP", key, count)
834    }
835
836    /// `RPOPLPUSH`.
837    fn rpoplpush<K: ToSingleValkeyArg, D: ToSingleValkeyArg>(key: K, dstkey: D) -> (Option<String>) {
838        build_cmd!("RPOPLPUSH", key, dstkey)
839    }
840
841    /// `RPUSH`.
842    fn rpush<K: ToSingleValkeyArg, V: ToValkeyArgs>(key: K, value: V) -> (usize) {
843        build_cmd!("RPUSH", key, value)
844    }
845
846    /// `RPUSHX`.
847    fn rpush_exists<K: ToSingleValkeyArg, V: ToValkeyArgs>(key: K, value: V) -> (usize) {
848        build_cmd!("RPUSHX", key, value)
849    }
850
851    // ==== Hashes ========================================================
852
853    /// `HGET`.
854    fn hget<K: ToSingleValkeyArg, F: ToSingleValkeyArg>(key: K, field: F) -> (Option<String>) {
855        build_cmd!("HGET", key, field)
856    }
857
858    /// `HMGET`.
859    fn hmget<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, fields: F) -> (Vec<String>) {
860        build_cmd!("HMGET", key, fields)
861    }
862
863    /// `HDEL`.
864    fn hdel<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, field: F) -> (usize) {
865        build_cmd!("HDEL", key, field)
866    }
867
868    /// `HSET`.
869    fn hset<K: ToSingleValkeyArg, F: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, field: F, value: V) -> (usize) {
870        build_cmd!("HSET", key, field, value)
871    }
872
873    /// `HSET`.
874    fn hset_multiple<K: ToSingleValkeyArg, F: ToValkeyArgs, V: ToValkeyArgs>(key: K, items: &'a [(F, V)]) -> (usize) {
875        build_cmd!("HSET", key, items)
876    }
877
878    /// `HSETNX`.
879    fn hset_nx<K: ToSingleValkeyArg, F: ToSingleValkeyArg, V: ToSingleValkeyArg>(key: K, field: F, value: V) -> (bool) {
880        build_cmd!("HSETNX", key, field, value)
881    }
882
883    /// `HINCRBY`/`HINCRBYFLOAT`.
884    fn hincr<K: ToSingleValkeyArg, F: ToSingleValkeyArg, D: ToSingleValkeyArg>(key: K, field: F, delta: D) -> (f64) {
885        build_cmd!(if delta.describe_numeric_behavior() == ValkeyNumericBehavior::NumberIsFloat {
886            "HINCRBYFLOAT"
887        } else {
888            "HINCRBY"
889        }, key, field, delta)
890    }
891
892    /// `HEXISTS`.
893    fn hexists<K: ToSingleValkeyArg, F: ToSingleValkeyArg>(key: K, field: F) -> (bool) {
894        build_cmd!("HEXISTS", key, field)
895    }
896
897    /// `HKEYS`.
898    fn hkeys<K: ToSingleValkeyArg>(key: K) -> (Vec<String>) {
899        build_cmd!("HKEYS", key)
900    }
901
902    /// `HVALS`.
903    fn hvals<K: ToSingleValkeyArg>(key: K) -> (Vec<String>) {
904        build_cmd!("HVALS", key)
905    }
906
907    /// `HGETALL`.
908    fn hgetall<K: ToSingleValkeyArg>(key: K) -> (std::collections::HashMap<String, String>) {
909        build_cmd!("HGETALL", key)
910    }
911
912    /// `HLEN`.
913    fn hlen<K: ToSingleValkeyArg>(key: K) -> (usize) {
914        build_cmd!("HLEN", key)
915    }
916
917    /// `HGETEX`.
918    fn hget_ex<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, fields: F, expire_at: Expiry) -> (Vec<String>) {
919        build_cmd!("HGETEX", key, expire_at, "FIELDS", fields.num_of_args(), fields)
920    }
921
922    /// `HSETEX`.
923    fn hset_ex<K: ToSingleValkeyArg, F: ToValkeyArgs, V: ToValkeyArgs>(key: K, hash_field_expiration_options: &'a HashFieldExpirationOptions, fields_values: &'a [(F, V)]) -> (bool) {
924        build_cmd!("HSETEX", key, hash_field_expiration_options, "FIELDS", fields_values.len(), fields_values)
925    }
926
927    /// `HTTL`.
928    fn httl<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
929        build_cmd!("HTTL", key, "FIELDS", fields.num_of_args(), fields)
930    }
931
932    /// `HPTTL`.
933    fn hpttl<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
934        build_cmd!("HPTTL", key, "FIELDS", fields.num_of_args(), fields)
935    }
936
937    /// `HEXPIRE`.
938    fn hexpire<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, seconds: i64, opt: ExpireOption, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
939        build_cmd!("HEXPIRE", key, seconds, opt, "FIELDS", fields.num_of_args(), fields)
940    }
941
942    /// `HEXPIREAT`.
943    fn hexpire_at<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, ts: i64, opt: ExpireOption, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
944        build_cmd!("HEXPIREAT", key, ts, opt, "FIELDS", fields.num_of_args(), fields)
945    }
946
947    /// `HEXPIRETIME`.
948    fn hexpire_time<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
949        build_cmd!("HEXPIRETIME", key, "FIELDS", fields.num_of_args(), fields)
950    }
951
952    /// `HPERSIST`.
953    fn hpersist<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
954        build_cmd!("HPERSIST", key, "FIELDS", fields.num_of_args(), fields)
955    }
956
957    /// `HPEXPIRE`.
958    fn hpexpire<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, milliseconds: i64, opt: ExpireOption, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
959        build_cmd!("HPEXPIRE", key, milliseconds, opt, "FIELDS", fields.num_of_args(), fields)
960    }
961
962    /// `HPEXPIREAT`.
963    fn hpexpire_at<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, ts: i64, opt: ExpireOption, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
964        build_cmd!("HPEXPIREAT", key, ts, opt, "FIELDS", fields.num_of_args(), fields)
965    }
966
967    /// `HPEXPIRETIME`.
968    fn hpexpire_time<K: ToSingleValkeyArg, F: ToValkeyArgs>(key: K, fields: F) -> (Vec<IntegerReplyOrNoOp>) {
969        build_cmd!("HPEXPIRETIME", key, "FIELDS", fields.num_of_args(), fields)
970    }
971
972    // ==== Sets ==========================================================
973
974    /// `SADD`.
975    fn sadd<K: ToSingleValkeyArg, M: ToValkeyArgs>(key: K, member: M) -> (usize) {
976        build_cmd!("SADD", key, member)
977    }
978
979    /// `SCARD`.
980    fn scard<K: ToSingleValkeyArg>(key: K) -> (usize) {
981        build_cmd!("SCARD", key)
982    }
983
984    /// `SDIFF`.
985    fn sdiff<K: ToValkeyArgs>(keys: K) -> (HashSet<String>) {
986        build_cmd!("SDIFF", keys)
987    }
988
989    /// `SDIFFSTORE`.
990    fn sdiffstore<D: ToSingleValkeyArg, K: ToValkeyArgs>(dstkey: D, keys: K) -> (usize) {
991        build_cmd!("SDIFFSTORE", dstkey, keys)
992    }
993
994    /// `SINTER`.
995    fn sinter<K: ToValkeyArgs>(keys: K) -> (HashSet<String>) {
996        build_cmd!("SINTER", keys)
997    }
998
999    /// `SINTERSTORE`.
1000    fn sinterstore<D: ToSingleValkeyArg, K: ToValkeyArgs>(dstkey: D, keys: K) -> (usize) {
1001        build_cmd!("SINTERSTORE", dstkey, keys)
1002    }
1003
1004    /// `SISMEMBER`.
1005    fn sismember<K: ToSingleValkeyArg, M: ToSingleValkeyArg>(key: K, member: M) -> (bool) {
1006        build_cmd!("SISMEMBER", key, member)
1007    }
1008
1009    /// `SMISMEMBER`.
1010    fn smismember<K: ToSingleValkeyArg, M: ToValkeyArgs>(key: K, members: M) -> (Vec<bool>) {
1011        build_cmd!("SMISMEMBER", key, members)
1012    }
1013
1014    /// `SMEMBERS`.
1015    fn smembers<K: ToSingleValkeyArg>(key: K) -> (HashSet<String>) {
1016        build_cmd!("SMEMBERS", key)
1017    }
1018
1019    /// `SMOVE`.
1020    fn smove<S: ToSingleValkeyArg, D: ToSingleValkeyArg, M: ToSingleValkeyArg>(srckey: S, dstkey: D, member: M) -> (bool) {
1021        build_cmd!("SMOVE", srckey, dstkey, member)
1022    }
1023
1024    /// `SPOP`.
1025    fn spop<K: ToSingleValkeyArg>(key: K) -> Generic {
1026        build_cmd!("SPOP", key)
1027    }
1028
1029    /// `SRANDMEMBER`.
1030    fn srandmember<K: ToSingleValkeyArg>(key: K) -> (Option<String>) {
1031        build_cmd!("SRANDMEMBER", key)
1032    }
1033
1034    /// `SRANDMEMBER`.
1035    fn srandmember_multiple<K: ToSingleValkeyArg>(key: K, count: isize) -> (Vec<String>) {
1036        build_cmd!("SRANDMEMBER", key, count)
1037    }
1038
1039    /// `SREM`.
1040    fn srem<K: ToSingleValkeyArg, M: ToValkeyArgs>(key: K, member: M) -> (usize) {
1041        build_cmd!("SREM", key, member)
1042    }
1043
1044    /// `SUNION`.
1045    fn sunion<K: ToValkeyArgs>(keys: K) -> (HashSet<String>) {
1046        build_cmd!("SUNION", keys)
1047    }
1048
1049    /// `SUNIONSTORE`.
1050    fn sunionstore<D: ToSingleValkeyArg, K: ToValkeyArgs>(dstkey: D, keys: K) -> (usize) {
1051        build_cmd!("SUNIONSTORE", dstkey, keys)
1052    }
1053
1054    // ==== Sorted sets ===================================================
1055
1056    /// `ZADD`.
1057    fn zadd<K: ToSingleValkeyArg, S: ToSingleValkeyArg, M: ToSingleValkeyArg>(key: K, member: M, score: S) -> usize {
1058        build_cmd!("ZADD", key, score, member)
1059    }
1060
1061    /// `ZADD`.
1062    fn zadd_multiple<K: ToSingleValkeyArg, S: ToValkeyArgs, M: ToValkeyArgs>(key: K, items: &'a [(S, M)]) -> (usize) {
1063        build_cmd!("ZADD", key, items)
1064    }
1065
1066    /// `ZCARD`.
1067    fn zcard<K: ToSingleValkeyArg>(key: K) -> (usize) {
1068        build_cmd!("ZCARD", key)
1069    }
1070
1071    /// `ZCOUNT`.
1072    fn zcount<K: ToSingleValkeyArg, M: ToSingleValkeyArg, MM: ToSingleValkeyArg>(key: K, min: M, max: MM) -> (usize) {
1073        build_cmd!("ZCOUNT", key, min, max)
1074    }
1075
1076    /// `ZINCRBY`.
1077    fn zincr<K: ToSingleValkeyArg, M: ToSingleValkeyArg, D: ToSingleValkeyArg>(key: K, member: M, delta: D) -> (f64) {
1078        build_cmd!("ZINCRBY", key, delta, member)
1079    }
1080
1081    /// `ZINTERSTORE`.
1082    fn zinterstore<D: ToSingleValkeyArg, K: ToValkeyArgs>(dstkey: D, keys: K) -> (usize) {
1083        build_cmd!("ZINTERSTORE", dstkey, keys.num_of_args(), keys)
1084    }
1085
1086    /// `ZINTERSTORE`.
1087    fn zinterstore_min<D: ToSingleValkeyArg, K: ToValkeyArgs>(dstkey: D, keys: K) -> (usize) {
1088        build_cmd!("ZINTERSTORE", dstkey, keys.num_of_args(), keys, "AGGREGATE", "MIN")
1089    }
1090
1091    /// `ZINTERSTORE`.
1092    fn zinterstore_max<D: ToSingleValkeyArg, K: ToValkeyArgs>(dstkey: D, keys: K) -> (usize) {
1093        build_cmd!("ZINTERSTORE", dstkey, keys.num_of_args(), keys, "AGGREGATE", "MAX")
1094    }
1095
1096    /// `ZINTERSTORE`.
1097    fn zinterstore_weights<D: ToSingleValkeyArg, K: ToValkeyArgs, W: ToValkeyArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1098        let (keys, weights) = unzip_weights(keys);
1099        build_cmd!("ZINTERSTORE", dstkey, keys.len(), keys, "WEIGHTS", weights)
1100    }
1101
1102    /// `ZINTERSTORE`.
1103    fn zinterstore_min_weights<D: ToSingleValkeyArg, K: ToValkeyArgs, W: ToValkeyArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1104        let (keys, weights) = unzip_weights(keys);
1105        build_cmd!("ZINTERSTORE", dstkey, keys.len(), keys, "AGGREGATE", "MIN", "WEIGHTS", weights)
1106    }
1107
1108    /// `ZINTERSTORE`.
1109    fn zinterstore_max_weights<D: ToSingleValkeyArg, K: ToValkeyArgs, W: ToValkeyArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1110        let (keys, weights) = unzip_weights(keys);
1111        build_cmd!("ZINTERSTORE", dstkey, keys.len(), keys, "AGGREGATE", "MAX", "WEIGHTS", weights)
1112    }
1113
1114    /// `ZLEXCOUNT`.
1115    fn zlexcount<K: ToSingleValkeyArg, M: ToSingleValkeyArg, MM: ToSingleValkeyArg>(key: K, min: M, max: MM) -> (usize) {
1116        build_cmd!("ZLEXCOUNT", key, min, max)
1117    }
1118
1119    /// `BZPOPMAX`.
1120    fn bzpopmax<K: ToValkeyArgs>(key: K, timeout: f64) -> (Option<(String, String, f64)>) {
1121        build_cmd!("BZPOPMAX", key, timeout)
1122    }
1123
1124    /// `ZPOPMAX`.
1125    fn zpopmax<K: ToSingleValkeyArg>(key: K, count: isize) -> (Vec<(String, f64)>) {
1126        build_cmd!("ZPOPMAX", key, count)
1127    }
1128
1129    /// `BZPOPMIN`.
1130    fn bzpopmin<K: ToValkeyArgs>(key: K, timeout: f64) -> (Option<(String, String, f64)>) {
1131        build_cmd!("BZPOPMIN", key, timeout)
1132    }
1133
1134    /// `ZPOPMIN`.
1135    fn zpopmin<K: ToSingleValkeyArg>(key: K, count: isize) -> (Vec<(String, f64)>) {
1136        build_cmd!("ZPOPMIN", key, count)
1137    }
1138
1139    /// `BZMPOP`.
1140    fn bzmpop_max<K: ToValkeyArgs>(timeout: f64, keys: K, count: isize) -> (Option<(String, Vec<(String, f64)>)>) {
1141        build_cmd!("BZMPOP", timeout, keys.num_of_args(), keys, "MAX", "COUNT", count)
1142    }
1143
1144    /// `ZMPOP`.
1145    fn zmpop_max<K: ToValkeyArgs>(keys: K, count: isize) -> (Option<(String, Vec<(String, f64)>)>) {
1146        build_cmd!("ZMPOP", keys.num_of_args(), keys, "MAX", "COUNT", count)
1147    }
1148
1149    /// `BZMPOP`.
1150    fn bzmpop_min<K: ToValkeyArgs>(timeout: f64, keys: K, count: isize) -> (Option<(String, Vec<(String, f64)>)>) {
1151        build_cmd!("BZMPOP", timeout, keys.num_of_args(), keys, "MIN", "COUNT", count)
1152    }
1153
1154    /// `ZMPOP`.
1155    fn zmpop_min<K: ToValkeyArgs>(keys: K, count: isize) -> (Option<(String, Vec<(String, f64)>)>) {
1156        build_cmd!("ZMPOP", keys.num_of_args(), keys, "MIN", "COUNT", count)
1157    }
1158
1159    /// `ZRANDMEMBER`.
1160    fn zrandmember<K: ToSingleValkeyArg>(key: K, count: Option<isize>) -> Generic {
1161        build_cmd!("ZRANDMEMBER", key, count)
1162    }
1163
1164    /// `ZRANDMEMBER WITHSCORES`.
1165    fn zrandmember_withscores<K: ToSingleValkeyArg>(key: K, count: isize) -> Generic {
1166        build_cmd!("ZRANDMEMBER", key, count, "WITHSCORES")
1167    }
1168
1169    /// `ZRANGE`.
1170    fn zrange<K: ToSingleValkeyArg>(key: K, start: isize, stop: isize) -> (Vec<String>) {
1171        build_cmd!("ZRANGE", key, start, stop)
1172    }
1173
1174    /// `ZRANGE WITHSCORES`.
1175    fn zrange_withscores<K: ToSingleValkeyArg>(key: K, start: isize, stop: isize) -> (Vec<(String, f64)>) {
1176        build_cmd!("ZRANGE", key, start, stop, "WITHSCORES")
1177    }
1178
1179    /// `ZRANGEBYLEX`.
1180    fn zrangebylex<K: ToSingleValkeyArg, M: ToSingleValkeyArg, MM: ToSingleValkeyArg>(key: K, min: M, max: MM) -> (Vec<String>) {
1181        build_cmd!("ZRANGEBYLEX", key, min, max)
1182    }
1183
1184    /// `ZRANGEBYLEX LIMIT`.
1185    fn zrangebylex_limit<K: ToSingleValkeyArg, M: ToSingleValkeyArg, MM: ToSingleValkeyArg>(key: K, min: M, max: MM, offset: isize, count: isize) -> (Vec<String>) {
1186        build_cmd!("ZRANGEBYLEX", key, min, max, "LIMIT", offset, count)
1187    }
1188
1189    /// `ZREVRANGEBYLEX`.
1190    fn zrevrangebylex<K: ToSingleValkeyArg, MM: ToSingleValkeyArg, M: ToSingleValkeyArg>(key: K, max: MM, min: M) -> (Vec<String>) {
1191        build_cmd!("ZREVRANGEBYLEX", key, max, min)
1192    }
1193
1194    /// `ZREVRANGEBYLEX LIMIT`.
1195    fn zrevrangebylex_limit<K: ToSingleValkeyArg, MM: ToSingleValkeyArg, M: ToSingleValkeyArg>(key: K, max: MM, min: M, offset: isize, count: isize) -> (Vec<String>) {
1196        build_cmd!("ZREVRANGEBYLEX", key, max, min, "LIMIT", offset, count)
1197    }
1198
1199    /// `ZRANGEBYSCORE`.
1200    fn zrangebyscore<K: ToSingleValkeyArg, M: ToSingleValkeyArg, MM: ToSingleValkeyArg>(key: K, min: M, max: MM) -> (Vec<String>) {
1201        build_cmd!("ZRANGEBYSCORE", key, min, max)
1202    }
1203
1204    /// `ZRANGEBYSCORE WITHSCORES`.
1205    fn zrangebyscore_withscores<K: ToSingleValkeyArg, M: ToSingleValkeyArg, MM: ToSingleValkeyArg>(key: K, min: M, max: MM) -> (Vec<(String, usize)>) {
1206        build_cmd!("ZRANGEBYSCORE", key, min, max, "WITHSCORES")
1207    }
1208
1209    /// `ZRANGEBYSCORE LIMIT`.
1210    fn zrangebyscore_limit<K: ToSingleValkeyArg, M: ToSingleValkeyArg, MM: ToSingleValkeyArg>(key: K, min: M, max: MM, offset: isize, count: isize) -> (Vec<String>) {
1211        build_cmd!("ZRANGEBYSCORE", key, min, max, "LIMIT", offset, count)
1212    }
1213
1214    /// `ZRANGEBYSCORE WITHSCORES LIMIT`.
1215    fn zrangebyscore_limit_withscores<K: ToSingleValkeyArg, M: ToSingleValkeyArg, MM: ToSingleValkeyArg>(key: K, min: M, max: MM, offset: isize, count: isize) -> (Vec<(String, usize)>) {
1216        build_cmd!("ZRANGEBYSCORE", key, min, max, "WITHSCORES", "LIMIT", offset, count)
1217    }
1218
1219    /// `ZRANK`.
1220    fn zrank<K: ToSingleValkeyArg, M: ToSingleValkeyArg>(key: K, member: M) -> (Option<usize>) {
1221        build_cmd!("ZRANK", key, member)
1222    }
1223
1224    /// `ZREM`.
1225    fn zrem<K: ToSingleValkeyArg, M: ToValkeyArgs>(key: K, members: M) -> (usize) {
1226        build_cmd!("ZREM", key, members)
1227    }
1228
1229    /// `ZREMRANGEBYLEX`.
1230    fn zrembylex<K: ToSingleValkeyArg, M: ToSingleValkeyArg, MM: ToSingleValkeyArg>(key: K, min: M, max: MM) -> (usize) {
1231        build_cmd!("ZREMRANGEBYLEX", key, min, max)
1232    }
1233
1234    /// `ZREMRANGEBYRANK`.
1235    fn zremrangebyrank<K: ToSingleValkeyArg>(key: K, start: isize, stop: isize) -> (usize) {
1236        build_cmd!("ZREMRANGEBYRANK", key, start, stop)
1237    }
1238
1239    /// `ZREMRANGEBYSCORE`.
1240    fn zrembyscore<K: ToSingleValkeyArg, M: ToSingleValkeyArg, MM: ToSingleValkeyArg>(key: K, min: M, max: MM) -> (usize) {
1241        build_cmd!("ZREMRANGEBYSCORE", key, min, max)
1242    }
1243
1244    /// `ZREVRANGE`.
1245    fn zrevrange<K: ToSingleValkeyArg>(key: K, start: isize, stop: isize) -> (Vec<String>) {
1246        build_cmd!("ZREVRANGE", key, start, stop)
1247    }
1248
1249    /// `ZREVRANGE WITHSCORES`.
1250    fn zrevrange_withscores<K: ToSingleValkeyArg>(key: K, start: isize, stop: isize) -> (Vec<(String, f64)>) {
1251        build_cmd!("ZREVRANGE", key, start, stop, "WITHSCORES")
1252    }
1253
1254    /// `ZREVRANGEBYSCORE`.
1255    fn zrevrangebyscore<K: ToSingleValkeyArg, MM: ToSingleValkeyArg, M: ToSingleValkeyArg>(key: K, max: MM, min: M) -> (Vec<String>) {
1256        build_cmd!("ZREVRANGEBYSCORE", key, max, min)
1257    }
1258
1259    /// `ZREVRANGEBYSCORE WITHSCORES`.
1260    fn zrevrangebyscore_withscores<K: ToSingleValkeyArg, MM: ToSingleValkeyArg, M: ToSingleValkeyArg>(key: K, max: MM, min: M) -> (Vec<(String, f64)>) {
1261        build_cmd!("ZREVRANGEBYSCORE", key, max, min, "WITHSCORES")
1262    }
1263
1264    /// `ZREVRANGEBYSCORE LIMIT`.
1265    fn zrevrangebyscore_limit<K: ToSingleValkeyArg, MM: ToSingleValkeyArg, M: ToSingleValkeyArg>(key: K, max: MM, min: M, offset: isize, count: isize) -> (Vec<String>) {
1266        build_cmd!("ZREVRANGEBYSCORE", key, max, min, "LIMIT", offset, count)
1267    }
1268
1269    /// `ZREVRANGEBYSCORE WITHSCORES LIMIT`.
1270    fn zrevrangebyscore_limit_withscores<K: ToSingleValkeyArg, MM: ToSingleValkeyArg, M: ToSingleValkeyArg>(key: K, max: MM, min: M, offset: isize, count: isize) -> (Vec<(String, f64)>) {
1271        build_cmd!("ZREVRANGEBYSCORE", key, max, min, "WITHSCORES", "LIMIT", offset, count)
1272    }
1273
1274    /// `ZREVRANK`.
1275    fn zrevrank<K: ToSingleValkeyArg, M: ToSingleValkeyArg>(key: K, member: M) -> (Option<usize>) {
1276        build_cmd!("ZREVRANK", key, member)
1277    }
1278
1279    /// `ZSCORE`.
1280    fn zscore<K: ToSingleValkeyArg, M: ToSingleValkeyArg>(key: K, member: M) -> (Option<f64>) {
1281        build_cmd!("ZSCORE", key, member)
1282    }
1283
1284    /// `ZMSCORE`.
1285    fn zscore_multiple<K: ToSingleValkeyArg, M: ToValkeyArgs>(key: K, members: &'a [M]) -> (Option<Vec<f64>>) {
1286        build_cmd!("ZMSCORE", key, members)
1287    }
1288
1289    /// `ZUNIONSTORE`.
1290    fn zunionstore<D: ToSingleValkeyArg, K: ToValkeyArgs>(dstkey: D, keys: K) -> (usize) {
1291        build_cmd!("ZUNIONSTORE", dstkey, keys.num_of_args(), keys)
1292    }
1293
1294    /// `ZUNIONSTORE AGGREGATE MIN`.
1295    fn zunionstore_min<D: ToSingleValkeyArg, K: ToValkeyArgs>(dstkey: D, keys: K) -> (usize) {
1296        build_cmd!("ZUNIONSTORE", dstkey, keys.num_of_args(), keys, "AGGREGATE", "MIN")
1297    }
1298
1299    /// `ZUNIONSTORE AGGREGATE MAX`.
1300    fn zunionstore_max<D: ToSingleValkeyArg, K: ToValkeyArgs>(dstkey: D, keys: K) -> (usize) {
1301        build_cmd!("ZUNIONSTORE", dstkey, keys.num_of_args(), keys, "AGGREGATE", "MAX")
1302    }
1303
1304    /// `ZUNIONSTORE WEIGHTS`.
1305    fn zunionstore_weights<D: ToSingleValkeyArg, K: ToValkeyArgs, W: ToValkeyArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1306        let (keys, weights) = unzip_weights(keys);
1307        build_cmd!("ZUNIONSTORE", dstkey, keys.len(), keys, "WEIGHTS", weights)
1308    }
1309
1310    /// `ZUNIONSTORE AGGREGATE MIN WEIGHTS`.
1311    fn zunionstore_min_weights<D: ToSingleValkeyArg, K: ToValkeyArgs, W: ToValkeyArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1312        let (keys, weights) = unzip_weights(keys);
1313        build_cmd!("ZUNIONSTORE", dstkey, keys.len(), keys, "AGGREGATE", "MIN", "WEIGHTS", weights)
1314    }
1315
1316    /// `ZUNIONSTORE AGGREGATE MAX WEIGHTS`.
1317    fn zunionstore_max_weights<D: ToSingleValkeyArg, K: ToValkeyArgs, W: ToValkeyArgs>(dstkey: D, keys: &'a [(K, W)]) -> (usize) {
1318        let (keys, weights) = unzip_weights(keys);
1319        build_cmd!("ZUNIONSTORE", dstkey, keys.len(), keys, "AGGREGATE", "MAX", "WEIGHTS", weights)
1320    }
1321
1322    // ==== HyperLogLog ===================================================
1323
1324    /// `PFADD`.
1325    fn pfadd<K: ToSingleValkeyArg, E: ToValkeyArgs>(key: K, element: E) -> (bool) {
1326        build_cmd!("PFADD", key, element)
1327    }
1328
1329    /// `PFCOUNT`.
1330    fn pfcount<K: ToValkeyArgs>(key: K) -> (usize) {
1331        build_cmd!("PFCOUNT", key)
1332    }
1333
1334    /// `PFMERGE`.
1335    fn pfmerge<D: ToSingleValkeyArg, S: ToValkeyArgs>(dstkey: D, srckeys: S) -> (()) {
1336        build_cmd!("PFMERGE", dstkey, srckeys)
1337    }
1338
1339    // ==== Bitmaps =======================================================
1340
1341    /// `SETBIT`.
1342    fn setbit<K: ToSingleValkeyArg>(key: K, offset: usize, value: bool) -> (bool) {
1343        build_cmd!("SETBIT", key, offset, i32::from(value))
1344    }
1345
1346    /// `GETBIT`.
1347    fn getbit<K: ToSingleValkeyArg>(key: K, offset: usize) -> (bool) {
1348        build_cmd!("GETBIT", key, offset)
1349    }
1350
1351    /// `BITCOUNT`.
1352    fn bitcount<K: ToSingleValkeyArg>(key: K) -> (usize) {
1353        build_cmd!("BITCOUNT", key)
1354    }
1355
1356    /// `BITCOUNT`.
1357    fn bitcount_range<K: ToSingleValkeyArg>(key: K, start: usize, end: usize) -> (usize) {
1358        build_cmd!("BITCOUNT", key, start, end)
1359    }
1360
1361    /// `BITOP AND`.
1362    fn bit_and<D: ToSingleValkeyArg, S: ToValkeyArgs>(dstkey: D, srckeys: S) -> (usize) {
1363        build_cmd!("BITOP", "AND", dstkey, srckeys)
1364    }
1365
1366    /// `BITOP OR`.
1367    fn bit_or<D: ToSingleValkeyArg, S: ToValkeyArgs>(dstkey: D, srckeys: S) -> (usize) {
1368        build_cmd!("BITOP", "OR", dstkey, srckeys)
1369    }
1370
1371    /// `BITOP XOR`.
1372    fn bit_xor<D: ToSingleValkeyArg, S: ToValkeyArgs>(dstkey: D, srckeys: S) -> (usize) {
1373        build_cmd!("BITOP", "XOR", dstkey, srckeys)
1374    }
1375
1376    /// `BITOP NOT`.
1377    fn bit_not<D: ToSingleValkeyArg, S: ToSingleValkeyArg>(dstkey: D, srckey: S) -> (usize) {
1378        build_cmd!("BITOP", "NOT", dstkey, srckey)
1379    }
1380
1381    // ==== Geospatial ====================================================
1382
1383    /// `GEOADD`.
1384    fn geo_add<K: ToSingleValkeyArg, M: ToValkeyArgs>(key: K, members: M) -> (usize) {
1385        build_cmd!("GEOADD", key, members)
1386    }
1387
1388    /// `GEODIST`.
1389    fn geo_dist<K: ToSingleValkeyArg, M1: ToSingleValkeyArg, M2: ToSingleValkeyArg>(key: K, member1: M1, member2: M2, unit: GeoUnit) -> (Option<f64>) {
1390        build_cmd!("GEODIST", key, member1, member2, unit)
1391    }
1392
1393    /// `GEOHASH`.
1394    fn geo_hash<K: ToSingleValkeyArg, M: ToValkeyArgs>(key: K, members: M) -> (Vec<String>) {
1395        build_cmd!("GEOHASH", key, members)
1396    }
1397
1398    /// `GEOPOS`.
1399    fn geo_pos<K: ToSingleValkeyArg, M: ToValkeyArgs>(key: K, members: M) -> (Vec<Option<GeoCoord<f64>>>) {
1400        build_cmd!("GEOPOS", key, members)
1401    }
1402
1403    // ==== Streams =======================================================
1404
1405    /// `XACK`.
1406    fn xack<K: ToValkeyArgs, G: ToValkeyArgs, I: ToValkeyArgs>(key: K, group: G, ids: &'a [I]) -> (usize) {
1407        build_cmd!("XACK", key, group, ids)
1408    }
1409
1410    /// `XADD`.
1411    fn xadd<K: ToValkeyArgs, ID: ToValkeyArgs, F: ToValkeyArgs, V: ToValkeyArgs>(key: K, id: ID, items: &'a [(F, V)]) -> (Option<String>) {
1412        build_cmd!("XADD", key, id, items)
1413    }
1414
1415    /// `XADD` with options.
1416    fn xadd_options<K: ToValkeyArgs, ID: ToValkeyArgs, I: ToValkeyArgs>(key: K, id: ID, items: I, options: &'a StreamAddOptions) -> (Option<String>) {
1417        build_cmd!("XADD", key, options, id, items)
1418    }
1419
1420    /// `XAUTOCLAIM`.
1421    fn xautoclaim_options<K: ToSingleValkeyArg, G: ToValkeyArgs, C: ToValkeyArgs, MIT: ToValkeyArgs, S: ToValkeyArgs>(key: K, group: G, consumer: C, min_idle_time: MIT, start: S, options: StreamAutoClaimOptions) -> (StreamAutoClaimReply) {
1422        build_cmd!("XAUTOCLAIM", key, group, consumer, min_idle_time, start, options)
1423    }
1424
1425    /// `XCLAIM`.
1426    fn xclaim<K: ToSingleValkeyArg, G: ToValkeyArgs, C: ToValkeyArgs, MIT: ToValkeyArgs, ID: ToValkeyArgs>(key: K, group: G, consumer: C, min_idle_time: MIT, ids: &'a [ID]) -> (StreamClaimReply) {
1427        build_cmd!("XCLAIM", key, group, consumer, min_idle_time, ids)
1428    }
1429
1430    /// `XCLAIM`.
1431    fn xclaim_options<K: ToSingleValkeyArg, G: ToValkeyArgs, C: ToValkeyArgs, MIT: ToValkeyArgs, ID: ToValkeyArgs>(key: K, group: G, consumer: C, min_idle_time: MIT, ids: &'a [ID], options: StreamClaimOptions) -> Generic {
1432        build_cmd!("XCLAIM", key, group, consumer, min_idle_time, ids, options)
1433    }
1434
1435    /// `XDEL`.
1436    fn xdel<K: ToSingleValkeyArg, ID: ToValkeyArgs>(key: K, ids: &'a [ID]) -> (usize) {
1437        build_cmd!("XDEL", key, ids)
1438    }
1439
1440    /// `XGROUP CREATE`.
1441    fn xgroup_create<K: ToValkeyArgs, G: ToValkeyArgs, ID: ToValkeyArgs>(key: K, group: G, id: ID) -> () {
1442        build_cmd!("XGROUP", "CREATE", key, group, id)
1443    }
1444
1445    /// `XGROUP CREATE ... MKSTREAM`.
1446    fn xgroup_create_mkstream<K: ToValkeyArgs, G: ToValkeyArgs, ID: ToValkeyArgs>(key: K, group: G, id: ID) -> () {
1447        build_cmd!("XGROUP", "CREATE", key, group, id, "MKSTREAM")
1448    }
1449
1450    /// `XGROUP CREATECONSUMER`.
1451    fn xgroup_createconsumer<K: ToValkeyArgs, G: ToValkeyArgs, C: ToValkeyArgs>(key: K, group: G, consumer: C) -> bool {
1452        build_cmd!("XGROUP", "CREATECONSUMER", key, group, consumer)
1453    }
1454
1455    /// `XGROUP DELCONSUMER`.
1456    fn xgroup_delconsumer<K: ToValkeyArgs, G: ToValkeyArgs, C: ToValkeyArgs>(key: K, group: G, consumer: C) -> usize {
1457        build_cmd!("XGROUP", "DELCONSUMER", key, group, consumer)
1458    }
1459
1460    /// `XGROUP DESTROY`.
1461    fn xgroup_destroy<K: ToValkeyArgs, G: ToValkeyArgs>(key: K, group: G) -> bool {
1462        build_cmd!("XGROUP", "DESTROY", key, group)
1463    }
1464
1465    /// `XGROUP SETID`.
1466    fn xgroup_setid<K: ToValkeyArgs, G: ToValkeyArgs, ID: ToValkeyArgs>(key: K, group: G, id: ID) -> () {
1467        build_cmd!("XGROUP", "SETID", key, group, id)
1468    }
1469
1470    /// `XINFO CONSUMERS`.
1471    fn xinfo_consumers<K: ToValkeyArgs, G: ToValkeyArgs>(key: K, group: G) -> (StreamInfoConsumersReply) {
1472        build_cmd!("XINFO", "CONSUMERS", key, group)
1473    }
1474
1475    /// `XINFO GROUPS`.
1476    fn xinfo_groups<K: ToValkeyArgs>(key: K) -> (StreamInfoGroupsReply) {
1477        build_cmd!("XINFO", "GROUPS", key)
1478    }
1479
1480    /// `XINFO STREAM`.
1481    fn xinfo_stream<K: ToValkeyArgs>(key: K) -> (StreamInfoStreamReply) {
1482        build_cmd!("XINFO", "STREAM", key)
1483    }
1484
1485    /// `XLEN`.
1486    fn xlen<K: ToValkeyArgs>(key: K) -> usize {
1487        build_cmd!("XLEN", key)
1488    }
1489
1490    /// `XPENDING`.
1491    fn xpending<K: ToValkeyArgs, G: ToValkeyArgs>(key: K, group: G) -> (StreamPendingReply) {
1492        build_cmd!("XPENDING", key, group)
1493    }
1494
1495    /// `XPENDING` (extended form).
1496    fn xpending_count<K: ToValkeyArgs, G: ToValkeyArgs, S: ToValkeyArgs, E: ToValkeyArgs, C: ToValkeyArgs>(key: K, group: G, start: S, end: E, count: C) -> (StreamPendingCountReply) {
1497        build_cmd!("XPENDING", key, group, start, end, count)
1498    }
1499
1500    /// `XPENDING` (extended form, filtered by consumer).
1501    fn xpending_consumer_count<K: ToValkeyArgs, G: ToValkeyArgs, S: ToValkeyArgs, E: ToValkeyArgs, C: ToValkeyArgs, CN: ToValkeyArgs>(key: K, group: G, start: S, end: E, count: C, consumer: CN) -> (StreamPendingCountReply) {
1502        build_cmd!("XPENDING", key, group, start, end, count, consumer)
1503    }
1504
1505    /// `XRANGE`.
1506    fn xrange<K: ToValkeyArgs, S: ToValkeyArgs, E: ToValkeyArgs>(key: K, start: S, end: E) -> (StreamRangeReply) {
1507        build_cmd!("XRANGE", key, start, end)
1508    }
1509
1510    /// `XREAD`.
1511    fn xread<K: ToValkeyArgs, ID: ToValkeyArgs>(keys: &'a [K], ids: &'a [ID]) -> (Option<StreamReadReply>) {
1512        build_cmd!("XREAD", "STREAMS", keys, ids)
1513    }
1514
1515    /// `XREAD`.
1516    fn xread_options<K: ToValkeyArgs, ID: ToValkeyArgs>(keys: &'a [K], ids: &'a [ID], options: &'a StreamReadOptions) -> (Option<StreamReadReply>) {
1517        build_cmd!("XREAD", options, "STREAMS", keys, ids)
1518    }
1519
1520    /// `XREVRANGE`.
1521    fn xrevrange<K: ToValkeyArgs, E: ToValkeyArgs, S: ToValkeyArgs>(key: K, end: E, start: S) -> (StreamRangeReply) {
1522        build_cmd!("XREVRANGE", key, end, start)
1523    }
1524
1525    /// `XTRIM ... MAXLEN`.
1526    fn xtrim<K: ToValkeyArgs>(key: K, maxlen: StreamMaxlen) -> usize {
1527        build_cmd!("XTRIM", key, maxlen)
1528    }
1529
1530    /// `XTRIM`.
1531    fn xtrim_options<K: ToValkeyArgs>(key: K, options: &'a StreamTrimOptions) -> usize {
1532        build_cmd!("XTRIM", key, options)
1533    }
1534
1535    // ==== Connection ====================================================
1536
1537    /// `PING`.
1538    fn ping<>() -> (String) {
1539        build_cmd!("PING")
1540    }
1541
1542    /// `PING` with a message.
1543    fn ping_message<K: ToSingleValkeyArg>(message: K) -> (String) {
1544        build_cmd!("PING", message)
1545    }
1546
1547    /// `CLIENT GETNAME`.
1548    fn client_getname<>() -> (Option<String>) {
1549        build_cmd!("CLIENT", "GETNAME")
1550    }
1551
1552    /// `CLIENT ID`.
1553    fn client_id<>() -> (isize) {
1554        build_cmd!("CLIENT", "ID")
1555    }
1556
1557    /// `CLIENT SETNAME`.
1558    fn client_setname<K: ToSingleValkeyArg>(connection_name: K) -> (()) {
1559        build_cmd!("CLIENT", "SETNAME", connection_name)
1560    }
1561
1562    // ==== Server ========================================================
1563
1564    /// `FLUSHALL`.
1565    fn flushall<>() -> () {
1566        build_cmd!("FLUSHALL")
1567    }
1568
1569    /// `FLUSHALL`.
1570    fn flushall_options<>(options: &'a FlushAllOptions) -> () {
1571        build_cmd!("FLUSHALL", options)
1572    }
1573
1574    /// `FLUSHDB`.
1575    fn flushdb<>() -> () {
1576        build_cmd!("FLUSHDB")
1577    }
1578
1579    /// `FLUSHDB`.
1580    fn flushdb_options<>(options: &'a FlushDbOptions) -> () {
1581        build_cmd!("FLUSHDB", options)
1582    }
1583
1584    // ==== Pub/Sub =======================================================
1585
1586    /// `PUBLISH`.
1587    fn publish<K: ToSingleValkeyArg, E: ToSingleValkeyArg>(channel: K, message: E) -> (usize) {
1588        build_cmd!("PUBLISH", channel, message)
1589    }
1590
1591    /// `SPUBLISH`.
1592    fn spublish<K: ToSingleValkeyArg, E: ToSingleValkeyArg>(channel: K, message: E) -> (usize) {
1593        build_cmd!("SPUBLISH", channel, message)
1594    }
1595}