redis/
lib.rs

1//! redis-rs is a Rust implementation of a client library for Redis.  It exposes
2//! a general purpose interface to Redis and also provides specific helpers for
3//! commonly used functionality.
4//!
5//! The crate is called `redis` and you can depend on it via cargo:
6//!
7//! ```ini
8//! [dependencies.redis]
9//! version = "*"
10//! ```
11//!
12//! If you want to use the git version:
13//!
14//! ```ini
15//! [dependencies.redis]
16//! git = "https://github.com/redis-rs/redis-rs.git"
17//! ```
18//!
19//! # Basic Operation
20//!
21//! redis-rs exposes two API levels: a low- and a high-level part.
22//! The high-level part does not expose all the functionality of redis and
23//! might take some liberties in how it speaks the protocol.  The low-level
24//! part of the API allows you to express any request on the redis level.
25//! You can fluently switch between both API levels at any point.
26//!
27//! # TLS / SSL
28//!
29//! The user can enable TLS support using either RusTLS or native support (usually OpenSSL),
30//! using the `tls-rustls` or `tls-native-tls` features respectively. In order to enable TLS
31//! for async usage, the user must enable matching features for their runtime - either `tokio-native-tls-comp`,
32//! `tokio-rustls-comp`, `async-std-native-tls-comp`, or `async-std-rustls-comp`. Additionally, the
33//! `tls-rustls-webpki-roots` allows usage of of webpki-roots for the root certificate store.
34//!
35//! # TCP settings
36//!
37//! The user can set parameters of the underlying TCP connection by using the `tcp_nodelay` and `keep-alive` features.
38//! Alternatively, users of async connections can set [crate::io::tcp::TcpSettings] on the connection configuration objects,
39//! and set the TCP parameters in a more specific manner there.
40//!
41//! ## Connection Handling
42//!
43//! For connecting to redis you can use a client object which then can produce
44//! actual connections.  Connections and clients as well as results of
45//! connections and clients are considered `ConnectionLike` objects and
46//! can be used anywhere a request is made.
47//!
48//! The full canonical way to get a connection is to create a client and
49//! to ask for a connection from it:
50//!
51//! ```rust,no_run
52//! extern crate redis;
53//!
54//! fn do_something() -> redis::RedisResult<()> {
55//!     let client = redis::Client::open("redis://127.0.0.1/")?;
56//!     let mut con = client.get_connection()?;
57//!
58//!     /* do something here */
59//!
60//!     Ok(())
61//! }
62//! ```
63//!
64//! ## Connection Pooling
65//!
66//! When using a sync connection, it is recommended to use a connection pool in order to handle
67//! disconnects or multi-threaded usage. This can be done using the `r2d2` feature.
68//!
69//! ```rust,no_run
70//! # #[cfg(feature = "r2d2")]
71//! # fn do_something() {
72//! use redis::Commands;
73//!
74//! let client = redis::Client::open("redis://127.0.0.1/").unwrap();
75//! let pool = r2d2::Pool::builder().build(client).unwrap();
76//! let mut conn = pool.get().unwrap();
77//!
78//! let _: () = conn.set("KEY", "VALUE").unwrap();
79//! let val: String = conn.get("KEY").unwrap();
80//! # }
81//! ```
82//!
83//! For async connections, connection pooling isn't necessary. The `MultiplexedConnection` is
84//! cheap to clone and can be used safely concurrently from multiple threads, so a single connection can be easily
85//! reused. For automatic reconnections consider using `ConnectionManager` with the `connection-manager` feature.
86//! Async cluster connections also don't require pooling and are thread-safe and reusable.
87//!
88//! ## Optional Features
89//!
90//! There are a few features defined that can enable additional functionality
91//! if so desired.  Some of them are turned on by default.
92//!
93//! * `acl`: enables acl support (enabled by default)
94//! * `tokio-comp`: enables support for async usage with the Tokio runtime (optional)
95//! * `async-std-comp`: enables support for async usage with any runtime which is async-std compliant. (optional)
96//! * `smol-comp`: enables support for async usage with the Smol runtime (optional)
97//! * `geospatial`: enables geospatial support (enabled by default)
98//! * `script`: enables script support (enabled by default)
99//! * `streams`: enables high-level interface for interaction with Redis streams (enabled by default)
100//! * `r2d2`: enables r2d2 connection pool support (optional)
101//! * `ahash`: enables ahash map/set support & uses ahash internally (+7-10% performance) (optional)
102//! * `cluster`: enables redis cluster support (optional)
103//! * `cluster-async`: enables async redis cluster support (optional)
104//! * `connection-manager`: enables support for automatic reconnection (optional)
105//! * `keep-alive`: enables keep-alive option on socket by means of `socket2` crate (enabled by default)
106//! * `tcp_nodelay`: enables the no-delay flag on  communication sockets (optional)
107//! * `rust_decimal`, `bigdecimal`, `num-bigint`: enables type conversions to large number representation from different crates (optional)
108//! * `uuid`: enables type conversion to UUID (optional)
109//! * `sentinel`: enables high-level interfaces for communication with Redis sentinels (optional)
110//! * `json`: enables high-level interfaces for communication with the JSON module (optional)
111//! * `cache-aio`: enables **experimental** client side caching for MultiplexedConnection, ConnectionManager and async ClusterConnection (optional)
112//! * `disable-client-setinfo`: disables the `CLIENT SETINFO` handshake during connection initialization
113//!
114//! ## Connection Parameters
115//!
116//! redis-rs knows different ways to define where a connection should
117//! go.  The parameter to `Client::open` needs to implement the
118//! `IntoConnectionInfo` trait of which there are three implementations:
119//!
120//! * string slices in `redis://` URL format.
121//! * URL objects from the redis-url crate.
122//! * `ConnectionInfo` objects.
123//!
124//! The URL format is `redis://[<username>][:<password>@]<hostname>[:port][/[<db>][?protocol=<protocol>]]`
125//!
126//! If Unix socket support is available you can use a unix URL in this format:
127//!
128//! `redis+unix:///<path>[?db=<db>[&pass=<password>][&user=<username>][&protocol=<protocol>]]`
129//!
130//! For compatibility with some other libraries for Redis, the "unix" scheme
131//! is also supported:
132//!
133//! `unix:///<path>[?db=<db>][&pass=<password>][&user=<username>][&protocol=<protocol>]]`
134//!
135//! ## Executing Low-Level Commands
136//!
137//! To execute low-level commands you can use the `cmd` function which allows
138//! you to build redis requests.  Once you have configured a command object
139//! to your liking you can send a query into any `ConnectionLike` object:
140//!
141//! ```rust,no_run
142//! fn do_something(con: &mut redis::Connection) -> redis::RedisResult<()> {
143//!     redis::cmd("SET").arg("my_key").arg(42).exec(con)?;
144//!     Ok(())
145//! }
146//! ```
147//!
148//! Upon querying the return value is a result object.  If you do not care
149//! about the actual return value (other than that it is not a failure)
150//! you can always type annotate it to the unit type `()`.
151//!
152//! Note that commands with a sub-command (like "MEMORY USAGE", "ACL WHOAMI",
153//! "LATENCY HISTORY", etc) must specify the sub-command as a separate `arg`:
154//!
155//! ```rust,no_run
156//! fn do_something(con: &mut redis::Connection) -> redis::RedisResult<usize> {
157//!     // This will result in a server error: "unknown command `MEMORY USAGE`"
158//!     // because "USAGE" is technically a sub-command of "MEMORY".
159//!     redis::cmd("MEMORY USAGE").arg("my_key").query::<usize>(con)?;
160//!
161//!     // However, this will work as you'd expect
162//!     redis::cmd("MEMORY").arg("USAGE").arg("my_key").query(con)
163//! }
164//! ```
165//!
166//! ## Executing High-Level Commands
167//!
168//! The high-level interface is similar.  For it to become available you
169//! need to use the `Commands` trait in which case all `ConnectionLike`
170//! objects the library provides will also have high-level methods which
171//! make working with the protocol easier:
172//!
173//! ```rust,no_run
174//! extern crate redis;
175//! use redis::Commands;
176//!
177//! fn do_something(con: &mut redis::Connection) -> redis::RedisResult<()> {
178//!     let _: () = con.set("my_key", 42)?;
179//!     Ok(())
180//! }
181//! ```
182//!
183//! Note that high-level commands are work in progress and many are still
184//! missing!
185//!
186//! ## Type Conversions
187//!
188//! Because redis inherently is mostly type-less and the protocol is not
189//! exactly friendly to developers, this library provides flexible support
190//! for casting values to the intended results.  This is driven through the `FromRedisValue` and `ToRedisArgs` traits.
191//!
192//! The `arg` method of the command will accept a wide range of types through
193//! the `ToRedisArgs` trait and the `query` method of a command can convert the
194//! value to what you expect the function to return through the `FromRedisValue`
195//! trait.  This is quite flexible and allows vectors, tuples, hashsets, hashmaps
196//! as well as optional values:
197//!
198//! ```rust,no_run
199//! # use redis::Commands;
200//! # use std::collections::{HashMap, HashSet};
201//! # fn do_something() -> redis::RedisResult<()> {
202//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
203//! # let mut con = client.get_connection().unwrap();
204//! let count : i32 = con.get("my_counter")?;
205//! let count = con.get("my_counter").unwrap_or(0i32);
206//! let k : Option<String> = con.get("missing_key")?;
207//! let name : String = con.get("my_name")?;
208//! let bin : Vec<u8> = con.get("my_binary")?;
209//! let map : HashMap<String, i32> = con.hgetall("my_hash")?;
210//! let keys : Vec<String> = con.hkeys("my_hash")?;
211//! let mems : HashSet<i32> = con.smembers("my_set")?;
212//! let (k1, k2) : (String, String) = con.get(&["k1", "k2"])?;
213//! # Ok(())
214//! # }
215//! ```
216//!
217//! ## Pre-typed Commands
218//!
219//! In some cases, you may not have a desired return type for a high-level command, and would
220//! instead like to use defaults provided by the library, to avoid the clutter and development overhead
221//! of specifying types for each command.
222//!
223//! The library facilitates this by providing the `TypedCommands` and `AsyncTypedCommands`
224//! as alternatives to `Commands` and `AsyncCommands` respectively. These traits provide functions
225//! with pre-defined and opinionated return types. For example, `set` returns `()`, avoiding the need
226//! for developers to explicitly type each call as returning `()`.
227//!
228//! ```rust,no_run
229//! use redis::TypedCommands;
230//!
231//! fn fetch_an_integer() -> redis::RedisResult<isize> {
232//!     // connect to redis
233//!     let client = redis::Client::open("redis://127.0.0.1/")?;
234//!     let mut con = client.get_connection()?;
235//!     // `set` returns a `()`, so we don't need to specify the return type manually unlike in the previous example.
236//!     con.set("my_key", 42)?;
237//!     // `get_int` returns Result<Option<isize>>, as the key may not be found, or some error may occur.
238//!     Ok(con.get_int("my_key").unwrap().unwrap())
239//! }
240//! ```
241//!
242//! # RESP3 support
243//! Since Redis / Valkey version 6, a newer communication protocol called RESP3 is supported.
244//! Using this protocol allows the user both to receive a more varied `Value` results, for users
245//! who use the low-level `Value` type, and to receive out of band messages on the same connection. This allows the user to receive PubSub
246//! messages on the same connection, instead of creating a new PubSub connection (see "RESP3 async pubsub").
247//!
248//! # Iteration Protocol
249//!
250//! In addition to sending a single query, iterators are also supported.  When
251//! used with regular bulk responses they don't give you much over querying and
252//! converting into a vector (both use a vector internally) but they can also
253//! be used with `SCAN` like commands in which case iteration will send more
254//! queries until the cursor is exhausted:
255//!
256//! ```rust,ignore
257//! # fn do_something() -> redis::RedisResult<()> {
258//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
259//! # let mut con = client.get_connection().unwrap();
260//! let mut iter : redis::Iter<isize> = redis::cmd("SSCAN").arg("my_set")
261//!     .cursor_arg(0).clone().iter(&mut con)?;
262//! for x in iter {
263//!     // do something with the item
264//! }
265//! # Ok(()) }
266//! ```
267//!
268//! As you can see the cursor argument needs to be defined with `cursor_arg`
269//! instead of `arg` so that the library knows which argument needs updating
270//! as the query is run for more items.
271//!
272//! # Pipelining
273//!
274//! In addition to simple queries you can also send command pipelines.  This
275//! is provided through the `pipe` function.  It works very similar to sending
276//! individual commands but you can send more than one in one go.  This also
277//! allows you to ignore individual results so that matching on the end result
278//! is easier:
279//!
280//! ```rust,no_run
281//! # fn do_something() -> redis::RedisResult<()> {
282//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
283//! # let mut con = client.get_connection().unwrap();
284//! let (k1, k2) : (i32, i32) = redis::pipe()
285//!     .cmd("SET").arg("key_1").arg(42).ignore()
286//!     .cmd("SET").arg("key_2").arg(43).ignore()
287//!     .cmd("GET").arg("key_1")
288//!     .cmd("GET").arg("key_2").query(&mut con)?;
289//! # Ok(()) }
290//! ```
291//!
292//! If you want the pipeline to be wrapped in a `MULTI`/`EXEC` block you can
293//! easily do that by switching the pipeline into `atomic` mode.  From the
294//! caller's point of view nothing changes, the pipeline itself will take
295//! care of the rest for you:
296//!
297//! ```rust,no_run
298//! # fn do_something() -> redis::RedisResult<()> {
299//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
300//! # let mut con = client.get_connection().unwrap();
301//! let (k1, k2) : (i32, i32) = redis::pipe()
302//!     .atomic()
303//!     .cmd("SET").arg("key_1").arg(42).ignore()
304//!     .cmd("SET").arg("key_2").arg(43).ignore()
305//!     .cmd("GET").arg("key_1")
306//!     .cmd("GET").arg("key_2").query(&mut con)?;
307//! # Ok(()) }
308//! ```
309//!
310//! You can also use high-level commands on pipelines:
311//!
312//! ```rust,no_run
313//! # fn do_something() -> redis::RedisResult<()> {
314//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
315//! # let mut con = client.get_connection().unwrap();
316//! let (k1, k2) : (i32, i32) = redis::pipe()
317//!     .atomic()
318//!     .set("key_1", 42).ignore()
319//!     .set("key_2", 43).ignore()
320//!     .get("key_1")
321//!     .get("key_2").query(&mut con)?;
322//! # Ok(()) }
323//! ```
324//!
325//! # Transactions
326//!
327//! Transactions are available through atomic pipelines.  In order to use
328//! them in a more simple way you can use the `transaction` function of a
329//! connection:
330//!
331//! ```rust,no_run
332//! # fn do_something() -> redis::RedisResult<()> {
333//! use redis::Commands;
334//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
335//! # let mut con = client.get_connection().unwrap();
336//! let key = "the_key";
337//! let (new_val,) : (isize,) = redis::transaction(&mut con, &[key], |con, pipe| {
338//!     let old_val : isize = con.get(key)?;
339//!     pipe
340//!         .set(key, old_val + 1).ignore()
341//!         .get(key).query(con)
342//! })?;
343//! println!("The incremented number is: {}", new_val);
344//! # Ok(()) }
345//! ```
346//!
347//! For more information see the `transaction` function.
348//!
349//! # PubSub
350//!
351//! Pubsub is provided through the `PubSub` connection object for sync usage, or the `aio::PubSub`
352//! for async usage.
353//!
354//! Example usage:
355//!
356//! ```rust,no_run
357//! # fn do_something() -> redis::RedisResult<()> {
358//! let client = redis::Client::open("redis://127.0.0.1/")?;
359//! let mut con = client.get_connection()?;
360//! let mut pubsub = con.as_pubsub();
361//! pubsub.subscribe(&["channel_1", "channel_2"])?;
362//!
363//! loop {
364//!     let msg = pubsub.get_message()?;
365//!     let payload : String = msg.get_payload()?;
366//!     println!("channel '{}': {}", msg.get_channel_name(), payload);
367//! }
368//! # }
369//! ```
370//! In order to update subscriptions while concurrently waiting for messages, the async PubSub can be split into separate sink & stream components. The sink can be receive subscription requests while the stream is awaited for messages.
371//!
372//! ```rust,no_run
373//! # #[cfg(feature = "aio")]
374//! use futures_util::StreamExt;
375//! # #[cfg(feature = "aio")]
376//! # async fn do_something() -> redis::RedisResult<()> {
377//! let client = redis::Client::open("redis://127.0.0.1/")?;
378//! let (mut sink, mut stream) = client.get_async_pubsub().await?.split();
379//! sink.subscribe("channel_1").await?;
380//!
381//! loop {
382//!     let msg = stream.next().await.unwrap();
383//!     let payload : String = msg.get_payload().unwrap();
384//!     println!("channel '{}': {}", msg.get_channel_name(), payload);
385//! }
386//! # Ok(()) }
387//! ```
388//!
389//! ## RESP3 async pubsub
390//! If you're targeting a Redis/Valkey server of version 6 or above, you can receive
391//! pubsub messages from it without creating another connection, by setting a push sender on the connection.
392//!
393//! ```rust,no_run
394//! # #[cfg(feature = "aio")]
395//! # {
396//! # use futures::prelude::*;
397//! # use redis::AsyncCommands;
398//!
399//! # async fn func() -> redis::RedisResult<()> {
400//! let client = redis::Client::open("redis://127.0.0.1/?protocol=resp3").unwrap();
401//! let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
402//! let config = redis::AsyncConnectionConfig::new().set_push_sender(tx);
403//! let mut con = client.get_multiplexed_async_connection_with_config(&config).await?;
404//! con.subscribe(&["channel_1", "channel_2"]).await?;
405//!
406//! loop {
407//!   println!("Received {:?}", rx.recv().await.unwrap());
408//! }
409//! # Ok(()) }
410//! # }
411//! ```
412//!
413#![cfg_attr(
414    feature = "script",
415    doc = r##"
416# Scripts
417
418Lua scripts are supported through the `Script` type in a convenient
419way.  It will automatically load the script if it does not exist and invoke it.
420
421Example:
422
423```rust,no_run
424# fn do_something() -> redis::RedisResult<()> {
425# let client = redis::Client::open("redis://127.0.0.1/").unwrap();
426# let mut con = client.get_connection().unwrap();
427let script = redis::Script::new(r"
428    return tonumber(ARGV[1]) + tonumber(ARGV[2]);
429");
430let result: isize = script.arg(1).arg(2).invoke(&mut con)?;
431assert_eq!(result, 3);
432# Ok(()) }
433```
434
435Scripts can also be pipelined:
436
437```rust,no_run
438# fn do_something() -> redis::RedisResult<()> {
439# let client = redis::Client::open("redis://127.0.0.1/").unwrap();
440# let mut con = client.get_connection().unwrap();
441let script = redis::Script::new(r"
442    return tonumber(ARGV[1]) + tonumber(ARGV[2]);
443");
444let (a, b): (isize, isize) = redis::pipe()
445    .invoke_script(script.arg(1).arg(2))
446    .invoke_script(script.arg(2).arg(3))
447    .query(&mut con)?;
448
449assert_eq!(a, 3);
450assert_eq!(b, 5);
451# Ok(()) }
452```
453
454Note: unlike a call to [`invoke`](ScriptInvocation::invoke), if the script isn't loaded during the pipeline operation,
455it will not automatically be loaded and retried. The script can be loaded using the
456[`load`](ScriptInvocation::load) operation.
457"##
458)]
459//!
460#![cfg_attr(
461    feature = "aio",
462    doc = r##"
463# Async
464
465In addition to the synchronous interface that's been explained above there also exists an
466asynchronous interface based on [`futures`][] and [`tokio`][], [`smol`](https://docs.rs/smol/latest/smol/), or [`async-std`][].
467 All async connections are cheap to clone, and clones can be used concurrently from multiple threads.
468
469This interface exists under the `aio` (async io) module (which requires that the `aio` feature
470is enabled) and largely mirrors the synchronous with a few concessions to make it fit the
471constraints of `futures`. 
472
473```rust,no_run
474use futures::prelude::*;
475use redis::AsyncCommands;
476
477# #[tokio::main]
478# async fn main() -> redis::RedisResult<()> {
479let client = redis::Client::open("redis://127.0.0.1/").unwrap();
480let mut con = client.get_multiplexed_async_connection().await?;
481
482let _: () = con.set("key1", b"foo").await?;
483
484redis::cmd("SET").arg(&["key2", "bar"]).exec_async(&mut con).await?;
485
486let result = redis::cmd("MGET")
487 .arg(&["key1", "key2"])
488 .query_async(&mut con)
489 .await;
490assert_eq!(result, Ok(("foo".to_string(), b"bar".to_vec())));
491# Ok(()) }
492```
493
494## Runtime support
495The crate supports multiple runtimes, including `tokio`, `async-std`, and `smol`. For Tokio, the crate will
496spawn tasks on the current thread runtime. For async-std & smol, the crate will spawn tasks on the the global runtime.
497It is recommended that the crate be used with support only for a single runtime. If the crate is compiled with multiple runtimes,
498the user should call [`crate::aio::prefer_tokio`], [`crate::aio::prefer_async_std`] or [`crate::aio::prefer_smol`] to set the preferred runtime.
499These functions set global state which automatically chooses the correct runtime for the async connection.
500
501"##
502)]
503//!
504//! [`futures`]:https://crates.io/crates/futures
505//! [`tokio`]:https://tokio.rs
506//! [`async-std`]:https://async.rs/
507#![cfg_attr(
508    feature = "sentinel",
509    doc = r##"
510# Sentinel
511Sentinel types allow users to connect to Redis sentinels and find primaries and replicas.
512
513```rust,no_run
514use redis::{ Commands, RedisConnectionInfo };
515use redis::sentinel::{ SentinelServerType, SentinelClient, SentinelNodeConnectionInfo };
516
517let nodes = vec!["redis://127.0.0.1:6379/", "redis://127.0.0.1:6378/", "redis://127.0.0.1:6377/"];
518let mut sentinel = SentinelClient::build(
519    nodes,
520    String::from("primary1"),
521    Some(SentinelNodeConnectionInfo {
522        tls_mode: Some(redis::TlsMode::Insecure),
523        redis_connection_info: None,
524    }),
525    redis::sentinel::SentinelServerType::Master,
526)
527.unwrap();
528
529let primary = sentinel.get_connection().unwrap();
530```
531
532An async API also exists:
533
534```rust,no_run
535use futures::prelude::*;
536use redis::{ Commands, RedisConnectionInfo };
537use redis::sentinel::{ SentinelServerType, SentinelClient, SentinelNodeConnectionInfo };
538
539# #[tokio::main]
540# async fn main() -> redis::RedisResult<()> {
541let nodes = vec!["redis://127.0.0.1:6379/", "redis://127.0.0.1:6378/", "redis://127.0.0.1:6377/"];
542let mut sentinel = SentinelClient::build(
543    nodes,
544    String::from("primary1"),
545    Some(SentinelNodeConnectionInfo {
546        tls_mode: Some(redis::TlsMode::Insecure),
547        redis_connection_info: None,
548    }),
549    redis::sentinel::SentinelServerType::Master,
550)
551.unwrap();
552
553let primary = sentinel.get_async_connection().await.unwrap();
554# Ok(()) }
555"##
556)]
557//!
558
559#![deny(non_camel_case_types)]
560#![warn(missing_docs)]
561#![cfg_attr(docsrs, warn(rustdoc::broken_intra_doc_links))]
562// When on docs.rs we want to show tuple variadics in the docs.
563// This currently requires internal/unstable features in Rustdoc.
564#![cfg_attr(
565    docsrs,
566    feature(doc_cfg, rustdoc_internals),
567    expect(
568        internal_features,
569        reason = "rustdoc_internals is needed for fake_variadic"
570    )
571)]
572
573// public api
574#[cfg(feature = "aio")]
575pub use crate::client::AsyncConnectionConfig;
576pub use crate::client::Client;
577#[cfg(feature = "cache-aio")]
578pub use crate::cmd::CommandCacheConfig;
579pub use crate::cmd::{cmd, pack_command, pipe, Arg, Cmd, Iter};
580pub use crate::commands::{
581    Commands, ControlFlow, CopyOptions, Direction, FlushAllOptions, FlushDbOptions,
582    HashFieldExpirationOptions, LposOptions, PubSubCommands, ScanOptions, SetOptions,
583    SortedSetAddOptions, TypedCommands, UpdateCheck,
584};
585pub use crate::connection::{
586    parse_redis_url, transaction, Connection, ConnectionAddr, ConnectionInfo, ConnectionLike,
587    IntoConnectionInfo, Msg, PubSub, RedisConnectionInfo, TlsMode,
588};
589pub use crate::parser::{parse_redis_value, Parser};
590pub use crate::pipeline::Pipeline;
591
592#[cfg(feature = "script")]
593#[cfg_attr(docsrs, doc(cfg(feature = "script")))]
594pub use crate::script::{Script, ScriptInvocation};
595
596// preserve grouping and order
597#[rustfmt::skip]
598pub use crate::types::{
599    // utility functions
600    from_redis_value,
601    from_owned_redis_value,
602    make_extension_error,
603
604    // error kinds
605    ErrorKind,
606    RetryMethod,
607
608    // conversion traits
609    FromRedisValue,
610
611    // utility types
612    InfoDict,
613    NumericBehavior,
614    Expiry,
615    SetExpiry,
616    ExistenceCheck,
617    FieldExistenceCheck,
618    ExpireOption,
619    Role,
620    ReplicaInfo,
621    IntegerReplyOrNoOp,
622	ValueType,
623
624    // error and result types
625    RedisError,
626    RedisResult,
627    RedisWrite,
628    ToRedisArgs,
629
630    // low level values
631    Value,
632    PushKind,
633    VerbatimFormat,
634    ProtocolVersion,
635    PushInfo,
636};
637
638#[cfg(feature = "aio")]
639#[cfg_attr(docsrs, doc(cfg(feature = "aio")))]
640pub use crate::{
641    cmd::AsyncIter, commands::AsyncCommands, commands::AsyncTypedCommands,
642    parser::parse_redis_value_async, types::RedisFuture,
643};
644
645mod macros;
646mod pipeline;
647
648#[cfg(feature = "acl")]
649#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
650pub mod acl;
651
652#[cfg(feature = "aio")]
653#[cfg_attr(docsrs, doc(cfg(feature = "aio")))]
654pub mod aio;
655
656#[cfg(feature = "json")]
657#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
658pub use crate::commands::JsonCommands;
659
660#[cfg(all(feature = "json", feature = "aio"))]
661#[cfg_attr(docsrs, doc(cfg(all(feature = "json", feature = "aio"))))]
662pub use crate::commands::JsonAsyncCommands;
663
664#[cfg(feature = "vector-sets")]
665#[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
666pub use crate::commands::{
667    EmbeddingInput, VAddOptions, VEmbOptions, VSimOptions, VectorAddInput, VectorQuantization,
668    VectorSimilaritySearchInput,
669};
670
671#[cfg(feature = "geospatial")]
672#[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
673pub mod geo;
674
675#[cfg(any(feature = "connection-manager", feature = "cluster-async"))]
676mod subscription_tracker;
677
678#[cfg(feature = "cluster")]
679mod cluster_topology;
680
681#[cfg(feature = "cluster")]
682#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
683pub mod cluster;
684
685#[cfg(feature = "cluster")]
686#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
687mod cluster_client;
688
689#[cfg(feature = "cluster")]
690#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
691mod cluster_pipeline;
692
693/// Routing information for cluster commands.
694#[cfg(feature = "cluster")]
695#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
696pub mod cluster_routing;
697
698#[cfg(feature = "r2d2")]
699#[cfg_attr(docsrs, doc(cfg(feature = "r2d2")))]
700mod r2d2;
701
702#[cfg(all(feature = "bb8", feature = "aio"))]
703#[cfg_attr(docsrs, doc(cfg(all(feature = "bb8", feature = "aio"))))]
704mod bb8;
705
706#[cfg(feature = "streams")]
707#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
708pub mod streams;
709
710#[cfg(feature = "cluster-async")]
711#[cfg_attr(docsrs, doc(cfg(all(feature = "cluster", feature = "aio"))))]
712pub mod cluster_async;
713
714#[cfg(feature = "sentinel")]
715#[cfg_attr(docsrs, doc(cfg(feature = "sentinel")))]
716pub mod sentinel;
717
718#[cfg(feature = "tls-rustls")]
719mod tls;
720
721#[cfg(feature = "tls-rustls")]
722#[cfg_attr(docsrs, doc(cfg(feature = "tls-rustls")))]
723pub use crate::tls::{ClientTlsConfig, TlsCertificates};
724
725#[cfg(feature = "cache-aio")]
726#[cfg_attr(docsrs, doc(cfg(feature = "cache-aio")))]
727pub mod caching;
728
729mod client;
730mod cmd;
731mod commands;
732mod connection;
733/// Module for defining I/O behavior.
734pub mod io;
735mod parser;
736mod script;
737mod types;
738
739#[cfg(test)]
740mod tests {
741    use super::*;
742    #[test]
743    fn test_is_send() {
744        const fn assert_send<T: Send>() {}
745
746        assert_send::<Connection>();
747        #[cfg(feature = "cluster")]
748        assert_send::<cluster::ClusterConnection>();
749    }
750}