redis_driver/commands/
connection_commands.rs

1use crate::{
2    prepare_command,
3    resp::{
4        cmd, CommandArg, CommandArgs, FromValue, IntoArgs, SingleArgOrCollection, Value,
5    },
6    Error, PreparedCommand, Result,
7};
8use std::collections::HashMap;
9
10/// A group of Redis commands related to connection management
11///
12/// # See Also
13/// [Redis Connection Management Commands](https://redis.io/commands/?group=connection)
14pub trait ConnectionCommands {
15    /// Authenticates the current connection.
16    ///
17    /// # Errors
18    /// a Redis error if the password, or username/password pair, is invalid.
19    ///
20    /// # See Also
21    /// [<https://redis.io/commands/auth/>](https://redis.io/commands/auth/)
22    #[must_use]
23    fn auth<U, P>(&mut self, username: Option<U>, password: P) -> PreparedCommand<Self, ()>
24    where
25        Self: Sized,
26        U: Into<CommandArg>,
27        P: Into<CommandArg>,
28    {
29        prepare_command(self, cmd("AUTH").arg(username).arg(password))
30    }
31
32    /// This command controls the tracking of the keys in the next command executed by the connection,
33    /// when tracking is enabled in OPTIN or OPTOUT mode.
34    ///
35    /// # See Also
36    /// [<https://redis.io/commands/client-caching/>](https://redis.io/commands/client-caching/)
37    #[must_use]
38    fn client_caching(&mut self, mode: ClientCachingMode) -> PreparedCommand<Self, Option<()>>
39    where
40        Self: Sized,
41    {
42        prepare_command(self, cmd("CLIENT").arg("CACHING").arg(mode))
43    }
44
45    /// Returns the name of the current connection as set by [CLIENT SETNAME].
46    ///
47    /// # Return
48    /// The connection name, or a None if no name is set.
49    ///
50    /// # See Also
51    /// [<https://redis.io/commands/client-getname/>](https://redis.io/commands/client-getname/)
52    #[must_use]
53    fn client_getname<CN>(&mut self) -> PreparedCommand<Self, Option<CN>>
54    where
55        Self: Sized,
56        CN: FromValue,
57    {
58        prepare_command(self, cmd("CLIENT").arg("GETNAME"))
59    }
60
61    /// This command returns the client ID we are redirecting our tracking notifications to.
62    ///
63    /// # Return
64    /// the ID of the client we are redirecting the notifications to.
65    /// The command returns -1 if client tracking is not enabled,
66    /// or 0 if client tracking is enabled but we are not redirecting the notifications to any client.
67    ///
68    /// # See Also
69    /// [<https://redis.io/commands/client-getredir/>](https://redis.io/commands/client-getredir/)
70    #[must_use]
71    fn client_getredir(&mut self) -> PreparedCommand<Self, i64>
72    where
73        Self: Sized,
74    {
75        prepare_command(self, cmd("CLIENT").arg("GETREDIR"))
76    }
77
78    /// The command just returns the ID of the current connection.
79    ///
80    /// # Return
81    /// The id of the client.
82    ///
83    /// # See Also
84    /// [<https://redis.io/commands/client-id/>](https://redis.io/commands/client-id/)
85    #[must_use]
86    fn client_id(&mut self) -> PreparedCommand<Self, i64>
87    where
88        Self: Sized,
89    {
90        prepare_command(self, cmd("CLIENT").arg("ID"))
91    }
92
93    /// The command returns information and statistics about the current client connection
94    /// in a mostly human readable format.
95    ///
96    /// # Return
97    /// A ClientInfo struct with additional properties
98    ///
99    /// # See Also
100    /// [<https://redis.io/commands/client-info/>](https://redis.io/commands/client-info/)
101    #[must_use]
102    fn client_info(&mut self) -> PreparedCommand<Self, ClientInfo>
103    where
104        Self: Sized,
105    {
106        prepare_command(self, cmd("CLIENT").arg("INFO"))
107    }
108
109    /// Closes a given clients connection based on a filter list
110    ///
111    /// # Return
112    /// the number of clients killed.
113    ///
114    /// # See Also
115    /// [<https://redis.io/commands/client-kill/>](https://redis.io/commands/client-kill/)
116    #[must_use]
117    fn client_kill(&mut self, options: ClientKillOptions) -> PreparedCommand<Self, usize>
118    where
119        Self: Sized,
120    {
121        prepare_command(self, cmd("CLIENT").arg("KILL").arg(options))
122    }
123
124    /// Returns information and statistics about the client connections server in a mostly human readable format.
125    ///
126    /// # Return
127    /// A Vec of ClientInfo structs with additional properties
128    ///
129    /// # See Also
130    /// [<https://redis.io/commands/client-list/>](https://redis.io/commands/client-list/)
131    #[must_use]
132    fn client_list(&mut self, options: ClientListOptions) -> PreparedCommand<Self, ClientListResult>
133    where
134        Self: Sized,
135    {
136        prepare_command(self, cmd("CLIENT").arg("LIST").arg(options))
137    }
138
139    ///  sets the [`client eviction`](https://redis.io/docs/reference/clients/#client-eviction) mode for the current connection.
140    ///
141    /// # See Also
142    /// [<https://redis.io/commands/client-no-evict/>](https://redis.io/commands/client-no-evict/)
143    #[must_use]
144    fn client_no_evict(&mut self, no_evict: bool) -> PreparedCommand<Self, ()>
145    where
146        Self: Sized,
147    {
148        prepare_command(
149            self,
150            cmd("CLIENT")
151                .arg("NO-EVICT")
152                .arg(if no_evict { "ON" } else { "OFF" }),
153        )
154    }
155
156    /// Connections control command able to suspend all the Redis clients
157    /// for the specified amount of time (in milliseconds).
158    ///
159    /// # See Also
160    /// [<https://redis.io/commands/client-pause/>](https://redis.io/commands/client-pause/)
161    #[must_use]
162    fn client_pause(&mut self, timeout: u64, mode: ClientPauseMode) -> PreparedCommand<Self, ()>
163    where
164        Self: Sized,
165    {
166        prepare_command(self, cmd("CLIENT").arg("PAUSE").arg(timeout).arg(mode))
167    }
168
169    /// Sometimes it can be useful for clients to completely disable replies from the Redis server.
170    ///
171    /// # See Also
172    /// [<https://redis.io/commands/client-reply/>](https://redis.io/commands/client-reply/)
173    #[must_use]
174    fn client_reply(&mut self, mode: ClientReplyMode) -> PreparedCommand<Self, ()>
175    where
176        Self: Sized,
177    {
178        prepare_command(self, cmd("CLIENT").arg("REPLY").arg(mode))
179    }
180
181    /// Assigns a name to the current connection.
182    ///
183    /// # See Also
184    /// [<https://redis.io/commands/client-setname/>](https://redis.io/commands/client-setname/)
185    #[must_use]
186    fn client_setname<CN>(&mut self, connection_name: CN) -> PreparedCommand<Self, ()>
187    where
188        Self: Sized,
189        CN: Into<CommandArg>,
190    {
191        prepare_command(self, cmd("CLIENT").arg("SETNAME").arg(connection_name))
192    }
193
194    /// This command enables the tracking feature of the Redis server,
195    /// that is used for [`server assisted client side caching`](https://redis.io/topics/client-side-caching).
196    ///
197    /// # See Also
198    /// [<https://redis.io/commands/client-tracking/>](https://redis.io/commands/client-tracking/)
199    #[must_use]
200    fn client_tracking(
201        &mut self,
202        status: ClientTrackingStatus,
203        options: ClientTrackingOptions,
204    ) -> PreparedCommand<Self, ()>
205    where
206        Self: Sized,
207    {
208        prepare_command(self, cmd("CLIENT").arg("TRACKING").arg(status).arg(options))
209    }
210
211    /// This command enables the tracking feature of the Redis server,
212    /// that is used for [`server assisted client side caching`](https://redis.io/topics/client-side-caching).
213    ///
214    /// # See Also
215    /// [<https://redis.io/commands/client-tracking/>](https://redis.io/commands/client-tracking/)
216    #[must_use]
217    fn client_trackinginfo(&mut self) -> PreparedCommand<Self, ClientTrackingInfo>
218    where
219        Self: Sized,
220    {
221        prepare_command(self, cmd("CLIENT").arg("TRACKINGINFO"))
222    }
223
224    /// This command can unblock, from a different connection,
225    /// a client blocked in a blocking operation,
226    /// such as for instance `BRPOP` or `XREAD` or `WAIT`.
227    ///
228    /// # Return
229    /// * `true` - This command can unblock, from a different connection, a client blocked in a blocking operation, such as for instance BRPOP or XREAD or WAIT.
230    /// * `false` - if the client wasn't unblocked.
231    ///
232    /// # See Also
233    /// [<https://redis.io/commands/client-unblock/>](https://redis.io/commands/client-unblock/)
234    #[must_use]
235    fn client_unblock(
236        &mut self,
237        client_id: i64,
238        mode: ClientUnblockMode,
239    ) -> PreparedCommand<Self, bool>
240    where
241        Self: Sized,
242    {
243        prepare_command(self, cmd("CLIENT").arg("UNBLOCK").arg(client_id).arg(mode))
244    }
245
246    /// Used to resume command processing for all clients that were
247    /// paused by [`client_pause`](crate::ConnectionCommands::client_pause).
248    ///
249    /// # See Also
250    /// [<https://redis.io/commands/client-unpause/>](https://redis.io/commands/client-unpause/)
251    #[must_use]
252    fn client_unpause(&mut self) -> PreparedCommand<Self, bool>
253    where
254        Self: Sized,
255    {
256        prepare_command(self, cmd("CLIENT").arg("UNPAUSE"))
257    }
258
259    /// Returns `message`.
260    ///
261    /// # See Also
262    /// [<https://redis.io/commands/echo/>](https://redis.io/commands/echo/)
263    #[must_use]
264    fn echo<M, R>(&mut self, message: M) -> PreparedCommand<Self, R>
265    where
266        Self: Sized,
267        M: Into<CommandArg>,
268        R: FromValue,
269    {
270        prepare_command(self, cmd("ECHO").arg(message))
271    }
272
273    /// Switch to a different protocol,
274    /// optionally authenticating and setting the connection's name,
275    /// or provide a contextual client report.
276    ///
277    /// # See Also
278    /// [<https://redis.io/commands/hello/>](https://redis.io/commands/hello/)
279    #[must_use]
280    fn hello(&mut self, options: HelloOptions) -> PreparedCommand<Self, HelloResult>
281    where
282        Self: Sized,
283    {
284        prepare_command(self, cmd("HELLO").arg(options))
285    }
286
287    /// Returns PONG if no argument is provided, otherwise return a copy of the argument as a bulk.
288    ///
289    /// # See Also
290    /// [<https://redis.io/commands/ping/>](https://redis.io/commands/ping/)
291    #[must_use]
292    fn ping<R>(&mut self, options: PingOptions) -> PreparedCommand<Self, R>
293    where
294        Self: Sized,
295        R: FromValue,
296    {
297        prepare_command(self, cmd("PING").arg(options))
298    }
299
300    /// Ask the server to close the connection.
301    ///
302    /// # See Also
303    /// [<https://redis.io/commands/quit/>](https://redis.io/commands/quit/)
304    #[must_use]
305    fn quit(&mut self) -> PreparedCommand<Self, ()>
306    where
307        Self: Sized,
308    {
309        prepare_command(self, cmd("QUIT"))
310    }
311
312    /// This command performs a full reset of the connection's server-side context,
313    /// mimicking the effect of disconnecting and reconnecting again.
314    ///
315    /// # See Also
316    /// [<https://redis.io/commands/reset/>](https://redis.io/commands/reset/)
317    #[must_use]
318    fn reset(&mut self) -> PreparedCommand<Self, ()>
319    where
320        Self: Sized,
321    {
322        prepare_command(self, cmd("RESET"))
323    }
324
325    /// Select the Redis logical database having the specified zero-based numeric index.
326    ///
327    /// # See Also
328    /// [<https://redis.io/commands/reset/>](https://redis.io/commands/reset/)
329    #[must_use]
330    fn select(&mut self, index: usize) -> PreparedCommand<Self, ()>
331    where
332        Self: Sized,
333    {
334        prepare_command(self, cmd("SELECT").arg(index))
335    }
336}
337
338/// Client caching mode for the [`client_caching`](crate::ConnectionCommands::client_caching) command.
339pub enum ClientCachingMode {
340    Yes,
341    No,
342}
343
344impl IntoArgs for ClientCachingMode {
345    fn into_args(self, args: CommandArgs) -> CommandArgs {
346        args.arg(match self {
347            ClientCachingMode::Yes => CommandArg::Str("YES"),
348            ClientCachingMode::No => CommandArg::Str("NO"),
349        })
350    }
351}
352
353/// Client info results for the [`client_info`](crate::ConnectionCommands::client_info)
354/// & [`client_list`](crate::ConnectionCommands::client_list) commands.
355#[derive(Debug)]
356pub struct ClientInfo {
357    /// a unique 64-bit client ID
358    pub id: i64,
359
360    /// address/port of the client
361    pub addr: String,
362
363    /// address/port of local address client connected to (bind address)
364    pub laddr: String,
365
366    /// file descriptor corresponding to the socket
367    pub fd: u32,
368
369    /// the name set by the client with [`client_setname`](crate::ConnectionCommands::client_setname)
370    pub name: String,
371
372    /// total duration of the connection in seconds
373    pub age: u32,
374
375    /// idle time of the connection in seconds
376    pub idle: u32,
377
378    /// client flags (see [`client-list`](https://redis.io/commands/client-list/))
379    pub flags: String,
380
381    /// current database ID
382    pub db: usize,
383
384    /// number of channel subscriptions
385    pub sub: usize,
386
387    /// number of pattern matching subscriptions
388    pub psub: usize,
389
390    /// number of shard channel subscriptions. Added in Redis 7.0.3
391    pub ssub: usize,
392
393    /// number of commands in a MULTI/EXEC context
394    pub multi: usize,
395
396    /// query buffer length (0 means no query pending)
397    pub qbuf: usize,
398
399    /// free space of the query buffer (0 means the buffer is full)
400    pub qbuf_free: usize,
401
402    /// incomplete arguments for the next command (already extracted from query buffer)
403    pub argv_mem: usize,
404
405    /// memory is used up by buffered multi commands. Added in Redis 7.0
406    pub multi_mem: usize,
407
408    /// output buffer length
409    pub obl: usize,
410
411    /// output list length (replies are queued in this list when the buffer is full)
412    pub oll: usize,
413
414    /// output buffer memory usage
415    pub omem: usize,
416
417    ///  total memory consumed by this client in its various buffers
418    pub tot_mem: usize,
419
420    /// file descriptor events (r or w)
421    pub events: String,
422
423    /// last command played
424    pub cmd: String,
425
426    /// the authenticated username of the client
427    pub user: String,
428
429    /// client id of current client tracking redirection
430    pub redir: i64,
431
432    /// client RESP protocol version
433    pub resp: i32,
434
435    /// additional arguments that may be added in future versions of Redis
436    pub additional_arguments: HashMap<String, String>,
437}
438
439impl ClientInfo {
440    pub fn from_line(line: &str) -> Result<ClientInfo> {
441        // Each line is composed of a succession of property=value fields separated by a space character.
442        let mut values: HashMap<String, String> = line
443            .trim_end()
444            .split(' ')
445            .map(|kvp| {
446                let mut iter = kvp.split('=');
447                match (iter.next(), iter.next()) {
448                    (Some(key), None) => (key.to_owned(), "".to_owned()),
449                    (Some(key), Some(value)) => (key.to_owned(), value.to_owned()),
450                    _ => ("".to_owned(), "".to_owned()),
451                }
452            })
453            .collect();
454
455        Ok(ClientInfo {
456            id: values
457                .remove("id")
458                .map(|id| id.parse::<i64>().unwrap_or_default())
459                .unwrap_or_default(),
460            addr: values.remove("addr").unwrap_or_default(),
461            laddr: values.remove("laddr").unwrap_or_default(),
462            fd: values
463                .remove("fd")
464                .map(|id| id.parse::<u32>().unwrap_or_default())
465                .unwrap_or_default(),
466            name: values.remove("name").unwrap_or_default(),
467            age: values
468                .remove("age")
469                .map(|id| id.parse::<u32>().unwrap_or_default())
470                .unwrap_or_default(),
471            idle: values
472                .remove("idle")
473                .map(|id| id.parse::<u32>().unwrap_or_default())
474                .unwrap_or_default(),
475            flags: values.remove("flags").unwrap_or_default(),
476            db: values
477                .remove("db")
478                .map(|id| id.parse::<usize>().unwrap_or_default())
479                .unwrap_or_default(),
480            sub: values
481                .remove("sub")
482                .map(|id| id.parse::<usize>().unwrap_or_default())
483                .unwrap_or_default(),
484            psub: values
485                .remove("psub")
486                .map(|id| id.parse::<usize>().unwrap_or_default())
487                .unwrap_or_default(),
488            ssub: values
489                .remove("ssub")
490                .map(|id| id.parse::<usize>().unwrap_or_default())
491                .unwrap_or_default(),
492            multi: values
493                .remove("multi")
494                .map(|id| id.parse::<usize>().unwrap_or_default())
495                .unwrap_or_default(),
496            qbuf: values
497                .remove("qbuf")
498                .map(|id| id.parse::<usize>().unwrap_or_default())
499                .unwrap_or_default(),
500            qbuf_free: values
501                .remove("qbuf-free")
502                .map(|id| id.parse::<usize>().unwrap_or_default())
503                .unwrap_or_default(),
504            argv_mem: values
505                .remove("argv-mem")
506                .map(|id| id.parse::<usize>().unwrap_or_default())
507                .unwrap_or_default(),
508            multi_mem: values
509                .remove("multi-mem")
510                .map(|id| id.parse::<usize>().unwrap_or_default())
511                .unwrap_or_default(),
512            obl: values
513                .remove("obl")
514                .map(|id| id.parse::<usize>().unwrap_or_default())
515                .unwrap_or_default(),
516            oll: values
517                .remove("oll")
518                .map(|id| id.parse::<usize>().unwrap_or_default())
519                .unwrap_or_default(),
520            omem: values
521                .remove("omem")
522                .map(|id| id.parse::<usize>().unwrap_or_default())
523                .unwrap_or_default(),
524            tot_mem: values
525                .remove("tot-mem")
526                .map(|id| id.parse::<usize>().unwrap_or_default())
527                .unwrap_or_default(),
528            events: values.remove("events").unwrap_or_default(),
529            cmd: values.remove("cmd").unwrap_or_default(),
530            user: values.remove("user").unwrap_or_default(),
531            redir: values
532                .remove("redir")
533                .map(|id| id.parse::<i64>().unwrap_or_default())
534                .unwrap_or_default(),
535            resp: values
536                .remove("resp")
537                .map(|id| id.parse::<i32>().unwrap_or_default())
538                .unwrap_or_default(),
539            additional_arguments: values,
540        })
541    }
542}
543
544impl FromValue for ClientInfo {
545    fn from_value(value: Value) -> Result<Self> {
546        ClientInfo::from_line(&value.into::<String>()?)
547    }
548}
549
550/// Client type options for the [`client_list`](crate::ConnectionCommands::client_list) command.
551pub enum ClientType {
552    Normal,
553    Master,
554    Replica,
555    PubSub,
556}
557
558impl IntoArgs for ClientType {
559    fn into_args(self, args: CommandArgs) -> CommandArgs {
560        args.arg(match self {
561            ClientType::Normal => CommandArg::Str("NORMAL"),
562            ClientType::Master => CommandArg::Str("MASTER"),
563            ClientType::Replica => CommandArg::Str("REPLICA"),
564            ClientType::PubSub => CommandArg::Str("PUBSUB"),
565        })
566    }
567}
568
569/// Options for the [client_list](crate::ConnectionCommands::client_list) command.
570#[derive(Default)]
571pub struct ClientListOptions {
572    command_args: CommandArgs,
573}
574
575impl IntoArgs for ClientListOptions {
576    fn into_args(self, args: CommandArgs) -> CommandArgs {
577        args.arg(self.command_args)
578    }
579}
580
581impl ClientListOptions {
582    #[must_use]
583    pub fn client_type(self, client_type: ClientType) -> Self {
584        Self {
585            command_args: self.command_args.arg("TYPE").arg(client_type),
586        }
587    }
588
589    pub fn client_ids<II>(self, client_ids: II) -> Self
590    where
591        II: SingleArgOrCollection<i64>,
592    {
593        Self {
594            command_args: self.command_args.arg("ID").arg(client_ids),
595        }
596    }
597}
598
599/// Result for the [`client_list`](crate::ConnectionCommands::client_list) command.
600#[derive(Debug)]
601pub struct ClientListResult {
602    pub client_infos: Vec<ClientInfo>,
603}
604
605impl FromValue for ClientListResult {
606    fn from_value(value: Value) -> Result<Self> {
607        let lines: String = value.into()?;
608
609        let client_infos: Result<Vec<ClientInfo>> =
610            lines.split('\n').map(ClientInfo::from_line).collect();
611
612        Ok(Self {
613            client_infos: client_infos?,
614        })
615    }
616}
617
618/// Options for the [`client-kill`](crate::ConnectionCommands::client-kill) command.
619#[derive(Default)]
620pub struct ClientKillOptions {
621    command_args: CommandArgs,
622}
623
624impl ClientKillOptions {
625    #[must_use]
626    pub fn id(self, client_id: i64) -> Self {
627        Self {
628            command_args: self.command_args.arg("ID").arg(client_id),
629        }
630    }
631
632    #[must_use]
633    pub fn client_type(self, client_type: ClientType) -> Self {
634        Self {
635            command_args: self.command_args.arg("TYPE").arg(client_type),
636        }
637    }
638
639    #[must_use]
640    pub fn user<U: Into<CommandArg>>(self, username: U) -> Self {
641        Self {
642            command_args: self.command_args.arg("USER").arg(username),
643        }
644    }
645
646    /// Address in the format of `ip:port`
647    ///
648    /// The ip:port should match a line returned by the
649    /// [`client_list`](crate::ConnectionCommands::client_list) command (addr field).
650    #[must_use]
651    pub fn addr<A: Into<CommandArg>>(self, addr: A) -> Self {
652        Self {
653            command_args: self.command_args.arg("ADDR").arg(addr),
654        }
655    }
656
657    /// Kill all clients connected to specified local (bind) address.
658    #[must_use]
659    pub fn laddr<A: Into<CommandArg>>(self, laddr: A) -> Self {
660        Self {
661            command_args: self.command_args.arg("LADDR").arg(laddr),
662        }
663    }
664
665    /// By default this option is set to yes, that is, the client calling the command will not get killed,
666    /// however setting this option to no will have the effect of also killing the client calling the command.
667    #[must_use]
668    pub fn skip_me(self, skip_me: bool) -> Self {
669        Self {
670            command_args: self
671                .command_args
672                .arg("SKIPME")
673                .arg(if skip_me { "YES" } else { "NO" }),
674        }
675    }
676}
677
678impl IntoArgs for ClientKillOptions {
679    fn into_args(self, args: CommandArgs) -> CommandArgs {
680        args.arg(self.command_args)
681    }
682}
683
684/// Mode options for the [`client_pause`](crate::ConnectionCommands::client_pause) command.
685pub enum ClientPauseMode {
686    /// Clients are only blocked if they attempt to execute a write command.
687    Write,
688    /// This is the default mode. All client commands are blocked.
689    All,
690}
691
692impl IntoArgs for ClientPauseMode {
693    fn into_args(self, args: CommandArgs) -> CommandArgs {
694        args.arg(match self {
695            ClientPauseMode::Write => CommandArg::Str("WRITE"),
696            ClientPauseMode::All => CommandArg::Str("ALL"),
697        })
698    }
699}
700
701impl Default for ClientPauseMode {
702    fn default() -> Self {
703        ClientPauseMode::All
704    }
705}
706
707/// Mode options for the [`client_reply`](crate::ConnectionCommands::client_reply) command.
708pub enum ClientReplyMode {
709    On,
710    Off,
711    Skip,
712}
713
714impl IntoArgs for ClientReplyMode {
715    fn into_args(self, args: CommandArgs) -> CommandArgs {
716        args.arg(match self {
717            ClientReplyMode::On => CommandArg::Str("ON"),
718            ClientReplyMode::Off => CommandArg::Str("OFF"),
719            ClientReplyMode::Skip => CommandArg::Str("SKIP"),
720        })
721    }
722}
723
724/// Status options for the [`client_tracking`](crate::ConnectionCommands::client_tracking) command.
725pub enum ClientTrackingStatus {
726    On,
727    Off,
728}
729
730impl IntoArgs for ClientTrackingStatus {
731    fn into_args(self, args: CommandArgs) -> CommandArgs {
732        args.arg(match self {
733            ClientTrackingStatus::On => CommandArg::Str("ON"),
734            ClientTrackingStatus::Off => CommandArg::Str("OFF"),
735        })
736    }
737}
738
739/// Options for the [`client_tracking`](crate::ConnectionCommands::client_tracking) command.
740#[derive(Default)]
741pub struct ClientTrackingOptions {
742    command_args: CommandArgs,
743}
744
745impl ClientTrackingOptions {
746    #[must_use]
747    /// send invalidation messages to the connection with the specified ID.
748    pub fn redirect(self, client_id: i64) -> Self {
749        Self {
750            command_args: self.command_args.arg("REDIRECT").arg(client_id),
751        }
752    }
753
754    /// enable tracking in broadcasting mode.
755    pub fn broadcasting(self) -> Self {
756        Self {
757            command_args: self.command_args.arg("BCAST"),
758        }
759    }
760
761    /// for broadcasting, register a given key prefix, so that notifications
762    /// will be provided only for keys starting with this string.
763    ///
764    /// This option can be given multiple times to register multiple prefixes.
765    pub fn prefix<P: Into<CommandArg>>(self, prefix: P) -> Self {
766        Self {
767            command_args: self.command_args.arg("PREFIX").arg(prefix),
768        }
769    }
770
771    /// when broadcasting is NOT active, normally don't track keys in read only commands,
772    /// unless they are called immediately after a `CLIENT CACHING yes` command.
773    pub fn optin(self) -> Self {
774        Self {
775            command_args: self.command_args.arg("OPTIN"),
776        }
777    }
778
779    /// when broadcasting is NOT active, normally track keys in read only commands,
780    /// unless they are called immediately after a `CLIENT CACHING no` command.
781    pub fn optout(self) -> Self {
782        Self {
783            command_args: self.command_args.arg("OPTOUT"),
784        }
785    }
786
787    /// don't send notifications about keys modified by this connection itself.
788    pub fn no_loop(self) -> Self {
789        Self {
790            command_args: self.command_args.arg("NOLOOP"),
791        }
792    }
793}
794
795impl IntoArgs for ClientTrackingOptions {
796    fn into_args(self, args: CommandArgs) -> CommandArgs {
797        args.arg(self.command_args)
798    }
799}
800
801/// Result for the [`client_trackinginfo`](crate::ConnectionCommands::client_trackinginfo) command.
802pub struct ClientTrackingInfo {
803    /// A list of tracking flags used by the connection.
804    pub flags: Vec<String>,
805
806    /// The client ID used for notifications redirection, or -1 when none.
807    pub redirect: i64,
808
809    /// A list of key prefixes for which notifications are sent to the client.
810    pub prefixes: Vec<String>,
811}
812
813impl FromValue for ClientTrackingInfo {
814    fn from_value(value: Value) -> Result<Self> {
815        fn into_result(values: &mut HashMap<String, Value>) -> Option<ClientTrackingInfo> {
816            Some(ClientTrackingInfo {
817                flags: values.remove("flags")?.into().ok()?,
818                redirect: values.remove("redirect")?.into().ok()?,
819                prefixes: values.remove("prefixes")?.into().ok()?,
820            })
821        }
822
823        into_result(&mut value.into()?).ok_or_else(|| {
824            Error::Client(
825                "Cannot parse 
826            "
827                .to_owned(),
828            )
829        })
830    }
831}
832
833/// Mode options for the [`client_unblock`](crate::ConnectionCommands::client_unblock) command.
834pub enum ClientUnblockMode {
835    /// By default the client is unblocked as if the timeout of the command was reached,
836    Timeout,
837    /// the behavior is to unblock the client returning as error the fact that the client was force-unblocked.
838    Error,
839}
840
841impl IntoArgs for ClientUnblockMode {
842    fn into_args(self, args: CommandArgs) -> CommandArgs {
843        args.arg(match self {
844            ClientUnblockMode::Timeout => CommandArg::Str("TIMEOUT"),
845            ClientUnblockMode::Error => CommandArg::Str("ERROR"),
846        })
847    }
848}
849
850impl Default for ClientUnblockMode {
851    fn default() -> Self {
852        ClientUnblockMode::Timeout
853    }
854}
855
856/// Options for the [`hello`](crate::ConnectionCommands::hello) command.
857#[derive(Default)]
858pub struct HelloOptions {
859    command_args: CommandArgs,
860}
861
862impl HelloOptions {
863    #[must_use]
864    pub fn new(protover: usize) -> Self {
865        Self {
866            command_args: CommandArgs::default().arg(protover),
867        }
868    }
869
870    #[must_use]
871    pub fn auth<U, P>(self, username: U, password: P) -> Self
872    where
873        U: Into<CommandArg>,
874        P: Into<CommandArg>,
875    {
876        Self {
877            command_args: self.command_args.arg("AUTH").arg(username).arg(password),
878        }
879    }
880
881    #[must_use]
882    pub fn set_name<C>(self, client_name: C) -> Self
883    where
884        C: Into<CommandArg>,
885    {
886        Self {
887            command_args: self.command_args.arg("SETNAME").arg(client_name),
888        }
889    }
890}
891
892impl IntoArgs for HelloOptions {
893    fn into_args(self, args: CommandArgs) -> CommandArgs {
894        args.arg(self.command_args)
895    }
896}
897
898pub struct HelloResult {
899    pub server: String,
900    pub version: String,
901    pub proto: usize,
902    pub id: i64,
903    pub mode: String,
904    pub role: String,
905    pub modules: Vec<String>,
906}
907
908impl FromValue for HelloResult {
909    fn from_value(value: Value) -> Result<Self> {
910        match &value {
911            Value::Array(Some(v)) if v.len() == 14 => {
912                fn into_result(values: &mut HashMap<String, Value>) -> Option<HelloResult> {
913                    Some(HelloResult {
914                        server: values.remove("server")?.into().ok()?,
915                        version: values.remove("version")?.into().ok()?,
916                        proto: values.remove("proto")?.into().ok()?,
917                        id: values.remove("id")?.into().ok()?,
918                        mode: values.remove("mode")?.into().ok()?,
919                        role: values.remove("role")?.into().ok()?,
920                        modules: values.remove("modules")?.into().ok()?,
921                    })
922                }
923
924                into_result(&mut value.into()?)
925                    .ok_or_else(|| Error::Client("Cannot parse HelloResult".to_owned()))
926            }
927            _ => Err(Error::Client("Cannot parse HelloResult".to_owned())),
928        }
929    }
930}
931
932/// Options for the [`ping`](crate::ConnectionCommands::ping) command.
933#[derive(Default)]
934pub struct PingOptions {
935    command_args: CommandArgs,
936}
937
938impl PingOptions {
939    #[must_use]
940    pub fn message<M: Into<CommandArg>>(self, message: M) -> Self {
941        Self {
942            command_args: self.command_args.arg(message),
943        }
944    }
945}
946
947impl IntoArgs for PingOptions {
948    fn into_args(self, args: CommandArgs) -> CommandArgs {
949        args.arg(self.command_args)
950    }
951}