Skip to main content

glide/
cmd.rs

1// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0
2//! GLIDE's owned command builder.
3//!
4//! [`Cmd`] represents a command keyword and arguments.
5
6use crate::ValkeyFuture;
7use crate::commands::core::AsyncCommands;
8use crate::value::FromValkeyValue;
9use crate::write::ToValkeyArgs;
10
11#[cfg(feature = "sync")]
12use crate::{ValkeyResult, commands::core::Commands};
13
14/// Create a new command with the given keyword.
15///
16/// This is the recommended way to start a command.
17/// Mirrors redis-rs's `cmd`.
18///
19/// ```
20/// glide::cmd("PING");
21/// ```
22pub fn cmd(name: &str) -> Cmd {
23    let mut command = Cmd::new();
24    command.arg(name);
25    command
26}
27
28/// A command to send to the server.
29///
30/// Build a command, then send it with one of:
31///
32/// - `crate::AsyncCommands::glide_send_command_as`
33/// - `crate::AsyncCommands::glide_send_command`
34/// - `crate::Commands::glide_send_command_as`
35/// - `crate::Commands::glide_send_command`
36///
37/// ```rust,no_run
38/// use glide::AsyncCommands;
39/// # async fn demo(client: glide::GlideClient) -> glide::ValkeyResult<()> {
40/// let set = glide::cmd("SET").arg("my_key").arg(42).clone();
41/// let _: () = client.glide_send_command_as(set).await?;
42///
43/// let get = glide::cmd("GET").arg("my_key").clone();
44/// let value: i64 = client.glide_send_command_as(get).await?;
45///
46/// # assert_eq!(value, 42);
47/// # Ok(()) }
48/// ```
49///
50/// Mirrors redis-rs's `Cmd`.
51#[derive(Clone, Default)]
52pub struct Cmd {
53    inner: redis::Cmd,
54}
55
56impl Cmd {
57    /// Create an empty command.
58    ///
59    /// It is recommended to use [`cmd`] instead
60    /// to explicitly specify the command keyword.
61    pub fn new() -> Self {
62        Cmd {
63            inner: redis::Cmd::new(),
64        }
65    }
66
67    /// Append an argument and return `&mut self` for chaining.
68    #[inline]
69    pub fn arg<A: ToValkeyArgs>(&mut self, arg: A) -> &mut Self {
70        arg.write_valkey_args(self);
71        self
72    }
73
74    /// Execute this command on an async GLIDE client.
75    ///
76    /// ```rust,no_run
77    /// use glide::cmd;
78    /// # async fn demo(client: &glide::GlideClient) -> glide::ValkeyResult<()> {
79    /// let v: i64 = cmd("GET").arg("k").query_async(client).await?;
80    /// # let _ = v; Ok(()) }
81    /// ```
82    ///
83    /// Mirrors `redis-rs`'s `query_async`.
84    #[inline]
85    pub fn query_async<'a, RV: FromValkeyValue>(
86        &self,
87        con: &'a impl AsyncCommands,
88    ) -> ValkeyFuture<'a, RV> {
89        con.glide_send_command_as(self.clone())
90    }
91
92    /// Execute this command on an async GLIDE client, discarding the reply.
93    ///
94    /// Mirrors `redis-rs`'s `exec_async`.
95    #[inline]
96    pub fn exec_async<'a>(&self, con: &'a impl AsyncCommands) -> ValkeyFuture<'a, ()> {
97        self.query_async(con)
98    }
99
100    /// Execute this command on a blocking GLIDE client.
101    ///
102    /// ```rust,no_run
103    /// use glide::cmd;
104    /// use glide::sync::SyncGlideClient;
105    /// # fn demo(client: &SyncGlideClient) -> glide::ValkeyResult<()> {
106    /// let v: i64 = cmd("GET").arg("k").query(client)?;
107    /// # let _ = v; Ok(()) }
108    /// ```
109    ///
110    /// Mirrors `redis-rs`'s `query`.
111    #[cfg(feature = "sync")]
112    #[inline]
113    pub fn query<RV: FromValkeyValue>(&self, con: &impl Commands) -> ValkeyResult<RV> {
114        con.glide_send_command_as(self.clone())
115    }
116
117    /// Execute this command on a blocking GLIDE client, discarding the reply.
118    ///
119    /// Mirrors `redis-rs`'s `exec`.
120    #[cfg(feature = "sync")]
121    #[inline]
122    pub fn exec(&self, con: &impl Commands) -> ValkeyResult<()> {
123        self.query(con)
124    }
125
126    /// Borrow the underlying `redis::Cmd`.
127    pub(crate) fn as_redis(&self) -> &redis::Cmd {
128        &self.inner
129    }
130
131    /// Mutably borrow the underlying `redis::Cmd`.
132    pub(crate) fn as_redis_mut(&mut self) -> &mut redis::Cmd {
133        &mut self.inner
134    }
135
136    /// Consume into the underlying `redis::Cmd`.
137    pub(crate) fn into_redis(self) -> redis::Cmd {
138        self.inner
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn matches_redis_cmd_single_arg() {
148        let mut v = Cmd::new();
149        v.arg("SET").arg("key").arg(42i64);
150
151        let mut r = redis::Cmd::new();
152        r.arg("SET").arg("key").arg(42i64);
153
154        assert_eq!(v.as_redis().get_packed_command(), r.get_packed_command());
155    }
156
157    #[test]
158    fn matches_redis_cmd_multi_arg() {
159        let mut v = Cmd::new();
160        v.arg("MGET").arg(&["a", "b", "c"][..]);
161
162        let mut r = redis::Cmd::new();
163        r.arg("MGET").arg(&["a", "b", "c"][..]);
164
165        assert_eq!(v.as_redis().get_packed_command(), r.get_packed_command());
166    }
167}