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
249//!
250//! ## RESP3 pubsub
251//! If you're targeting a Redis/Valkey server of version 6 or above, you can receive
252//! pubsub messages from it without creating another connection, by setting a push sender on the connection.
253//!
254//! ```rust,no_run
255//! # #[cfg(feature = "aio")]
256//! # {
257//! # use futures::prelude::*;
258//! # use redis::AsyncCommands;
259//!
260//! # async fn func() -> redis::RedisResult<()> {
261//! let client = redis::Client::open("redis://127.0.0.1/?protocol=resp3").unwrap();
262//! let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
263//! let config = redis::AsyncConnectionConfig::new().set_push_sender(tx);
264//! let mut con = client.get_multiplexed_async_connection_with_config(&config).await?;
265//! con.subscribe(&["channel_1", "channel_2"]).await?;
266//!
267//! loop {
268//! println!("Received {:?}", rx.recv().await.unwrap());
269//! }
270//! # Ok(()) }
271//! # }
272//! ```
273//!
274//! sync example:
275//!
276//! ```rust,no_run
277//! # {
278//! # use redis::TypedCommands;
279//!
280//! # async fn func() -> redis::RedisResult<()> {
281//! let client = redis::Client::open("redis://127.0.0.1/?protocol=resp3").unwrap();
282//! let (tx, rx) = std::sync::mpsc::channel();
283//! let mut con = client.get_connection().unwrap();
284//! con.set_push_sender(tx);
285//! con.subscribe_resp3(&["channel_1", "channel_2"])?;
286//!
287//! loop {
288//! std::thread::sleep(std::time::Duration::from_millis(10));
289//! // the connection only reads when actively polled, so it must constantly send and receive requests.
290//! _ = con.ping().unwrap();
291//! println!("Received {:?}", rx.try_recv().unwrap());
292//! }
293//! # Ok(()) }
294//! # }
295//! ```
296//!
297//! # Iteration Protocol
298//!
299//! In addition to sending a single query, iterators are also supported. When
300//! used with regular bulk responses they don't give you much over querying and
301//! converting into a vector (both use a vector internally) but they can also
302//! be used with `SCAN` like commands in which case iteration will send more
303//! queries until the cursor is exhausted:
304//!
305//! ```rust,ignore
306//! # fn do_something() -> redis::RedisResult<()> {
307//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
308//! # let mut con = client.get_connection().unwrap();
309//! let mut iter : redis::Iter<isize> = redis::cmd("SSCAN").arg("my_set")
310//! .cursor_arg(0).clone().iter(&mut con)?;
311//! for x in iter {
312//! // do something with the item
313//! }
314//! # Ok(()) }
315//! ```
316//!
317//! As you can see the cursor argument needs to be defined with `cursor_arg`
318//! instead of `arg` so that the library knows which argument needs updating
319//! as the query is run for more items.
320//!
321//! # Pipelining
322//!
323//! In addition to simple queries you can also send command pipelines. This
324//! is provided through the `pipe` function. It works very similar to sending
325//! individual commands but you can send more than one in one go. This also
326//! allows you to ignore individual results so that matching on the end result
327//! is easier:
328//!
329//! ```rust,no_run
330//! # fn do_something() -> redis::RedisResult<()> {
331//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
332//! # let mut con = client.get_connection().unwrap();
333//! let (k1, k2) : (i32, i32) = redis::pipe()
334//! .cmd("SET").arg("key_1").arg(42).ignore()
335//! .cmd("SET").arg("key_2").arg(43).ignore()
336//! .cmd("GET").arg("key_1")
337//! .cmd("GET").arg("key_2").query(&mut con)?;
338//! # Ok(()) }
339//! ```
340//!
341//! If you want the pipeline to be wrapped in a `MULTI`/`EXEC` block you can
342//! easily do that by switching the pipeline into `atomic` mode. From the
343//! caller's point of view nothing changes, the pipeline itself will take
344//! care of the rest for you:
345//!
346//! ```rust,no_run
347//! # fn do_something() -> redis::RedisResult<()> {
348//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
349//! # let mut con = client.get_connection().unwrap();
350//! let (k1, k2) : (i32, i32) = redis::pipe()
351//! .atomic()
352//! .cmd("SET").arg("key_1").arg(42).ignore()
353//! .cmd("SET").arg("key_2").arg(43).ignore()
354//! .cmd("GET").arg("key_1")
355//! .cmd("GET").arg("key_2").query(&mut con)?;
356//! # Ok(()) }
357//! ```
358//!
359//! You can also use high-level commands on pipelines:
360//!
361//! ```rust,no_run
362//! # fn do_something() -> redis::RedisResult<()> {
363//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
364//! # let mut con = client.get_connection().unwrap();
365//! let (k1, k2) : (i32, i32) = redis::pipe()
366//! .atomic()
367//! .set("key_1", 42).ignore()
368//! .set("key_2", 43).ignore()
369//! .get("key_1")
370//! .get("key_2").query(&mut con)?;
371//! # Ok(()) }
372//! ```
373//!
374//! # Transactions
375//!
376//! Transactions are available through atomic pipelines. In order to use
377//! them in a more simple way you can use the `transaction` function of a
378//! connection:
379//!
380//! ```rust,no_run
381//! # fn do_something() -> redis::RedisResult<()> {
382//! use redis::Commands;
383//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
384//! # let mut con = client.get_connection().unwrap();
385//! let key = "the_key";
386//! let (new_val,) : (isize,) = redis::transaction(&mut con, &[key], |con, pipe| {
387//! let old_val : isize = con.get(key)?;
388//! pipe
389//! .set(key, old_val + 1).ignore()
390//! .get(key).query(con)
391//! })?;
392//! println!("The incremented number is: {}", new_val);
393//! # Ok(()) }
394//! ```
395//!
396//! For more information see the `transaction` function.
397//!
398//! # PubSub
399//!
400//! Pubsub is provided through the `PubSub` connection object for sync usage, or the `aio::PubSub`
401//! for async usage.
402//!
403//! Example usage:
404//!
405//! ```rust,no_run
406//! # fn do_something() -> redis::RedisResult<()> {
407//! let client = redis::Client::open("redis://127.0.0.1/")?;
408//! let mut con = client.get_connection()?;
409//! let mut pubsub = con.as_pubsub();
410//! pubsub.subscribe(&["channel_1", "channel_2"])?;
411//!
412//! loop {
413//! let msg = pubsub.get_message()?;
414//! let payload : String = msg.get_payload()?;
415//! println!("channel '{}': {}", msg.get_channel_name(), payload);
416//! }
417//! # }
418//! ```
419//! 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.
420//!
421//! ```rust,no_run
422//! # #[cfg(feature = "aio")]
423//! use futures_util::StreamExt;
424//! # #[cfg(feature = "aio")]
425//! # async fn do_something() -> redis::RedisResult<()> {
426//! let client = redis::Client::open("redis://127.0.0.1/")?;
427//! let (mut sink, mut stream) = client.get_async_pubsub().await?.split();
428//! sink.subscribe("channel_1").await?;
429//!
430//! loop {
431//! let msg = stream.next().await.unwrap();
432//! let payload : String = msg.get_payload().unwrap();
433//! println!("channel '{}': {}", msg.get_channel_name(), payload);
434//! }
435//! # Ok(()) }
436//! ```
437//!
438#![cfg_attr(
439 feature = "script",
440 doc = r##"
441# Scripts
442
443Lua scripts are supported through the `Script` type in a convenient
444way. It will automatically load the script if it does not exist and invoke it.
445
446Example:
447
448```rust,no_run
449# fn do_something() -> redis::RedisResult<()> {
450# let client = redis::Client::open("redis://127.0.0.1/").unwrap();
451# let mut con = client.get_connection().unwrap();
452let script = redis::Script::new(r"
453 return tonumber(ARGV[1]) + tonumber(ARGV[2]);
454");
455let result: isize = script.arg(1).arg(2).invoke(&mut con)?;
456assert_eq!(result, 3);
457# Ok(()) }
458```
459
460Scripts can also be pipelined:
461
462```rust,no_run
463# fn do_something() -> redis::RedisResult<()> {
464# let client = redis::Client::open("redis://127.0.0.1/").unwrap();
465# let mut con = client.get_connection().unwrap();
466let script = redis::Script::new(r"
467 return tonumber(ARGV[1]) + tonumber(ARGV[2]);
468");
469let (a, b): (isize, isize) = redis::pipe()
470 .invoke_script(script.arg(1).arg(2))
471 .invoke_script(script.arg(2).arg(3))
472 .query(&mut con)?;
473
474assert_eq!(a, 3);
475assert_eq!(b, 5);
476# Ok(()) }
477```
478
479Note: unlike a call to [`invoke`](ScriptInvocation::invoke), if the script isn't loaded during the pipeline operation,
480it will not automatically be loaded and retried. The script can be loaded using the
481[`load`](ScriptInvocation::load) operation.
482"##
483)]
484//!
485#![cfg_attr(
486 feature = "aio",
487 doc = r##"
488# Async
489
490In addition to the synchronous interface that's been explained above there also exists an
491asynchronous interface based on [`futures`][] and [`tokio`][], [`smol`](https://docs.rs/smol/latest/smol/), or [`async-std`][].
492 All async connections are cheap to clone, and clones can be used concurrently from multiple threads.
493
494This interface exists under the `aio` (async io) module (which requires that the `aio` feature
495is enabled) and largely mirrors the synchronous with a few concessions to make it fit the
496constraints of `futures`.
497
498```rust,no_run
499use futures::prelude::*;
500use redis::AsyncCommands;
501
502# #[tokio::main]
503# async fn main() -> redis::RedisResult<()> {
504let client = redis::Client::open("redis://127.0.0.1/").unwrap();
505let mut con = client.get_multiplexed_async_connection().await?;
506
507let _: () = con.set("key1", b"foo").await?;
508
509redis::cmd("SET").arg(&["key2", "bar"]).exec_async(&mut con).await?;
510
511let result = redis::cmd("MGET")
512 .arg(&["key1", "key2"])
513 .query_async(&mut con)
514 .await;
515assert_eq!(result, Ok(("foo".to_string(), b"bar".to_vec())));
516# Ok(()) }
517```
518
519## Runtime support
520The crate supports multiple runtimes, including `tokio`, `async-std`, and `smol`. For Tokio, the crate will
521spawn tasks on the current thread runtime. For async-std & smol, the crate will spawn tasks on the the global runtime.
522It is recommended that the crate be used with support only for a single runtime. If the crate is compiled with multiple runtimes,
523the user should call [`crate::aio::prefer_tokio`], [`crate::aio::prefer_async_std`] or [`crate::aio::prefer_smol`] to set the preferred runtime.
524These functions set global state which automatically chooses the correct runtime for the async connection.
525
526"##
527)]
528//!
529//! [`futures`]:https://crates.io/crates/futures
530//! [`tokio`]:https://tokio.rs
531//! [`async-std`]:https://async.rs/
532#![cfg_attr(
533 feature = "sentinel",
534 doc = r##"
535# Sentinel
536Sentinel types allow users to connect to Redis sentinels and find primaries and replicas.
537
538```rust,no_run
539use redis::{ Commands, RedisConnectionInfo };
540use redis::sentinel::{ SentinelServerType, SentinelClient, SentinelNodeConnectionInfo };
541
542let nodes = vec!["redis://127.0.0.1:6379/", "redis://127.0.0.1:6378/", "redis://127.0.0.1:6377/"];
543let mut sentinel = SentinelClient::build(
544 nodes,
545 String::from("primary1"),
546 Some(SentinelNodeConnectionInfo {
547 tls_mode: Some(redis::TlsMode::Insecure),
548 redis_connection_info: None,
549 }),
550 redis::sentinel::SentinelServerType::Master,
551)
552.unwrap();
553
554let primary = sentinel.get_connection().unwrap();
555```
556
557An async API also exists:
558
559```rust,no_run
560use futures::prelude::*;
561use redis::{ Commands, RedisConnectionInfo };
562use redis::sentinel::{ SentinelServerType, SentinelClient, SentinelNodeConnectionInfo };
563
564# #[tokio::main]
565# async fn main() -> redis::RedisResult<()> {
566let nodes = vec!["redis://127.0.0.1:6379/", "redis://127.0.0.1:6378/", "redis://127.0.0.1:6377/"];
567let mut sentinel = SentinelClient::build(
568 nodes,
569 String::from("primary1"),
570 Some(SentinelNodeConnectionInfo {
571 tls_mode: Some(redis::TlsMode::Insecure),
572 redis_connection_info: None,
573 }),
574 redis::sentinel::SentinelServerType::Master,
575)
576.unwrap();
577
578let primary = sentinel.get_async_connection().await.unwrap();
579# Ok(()) }
580"##
581)]
582//!
583
584#![deny(non_camel_case_types)]
585#![warn(missing_docs)]
586#![cfg_attr(docsrs, warn(rustdoc::broken_intra_doc_links))]
587// When on docs.rs we want to show tuple variadics in the docs.
588// This currently requires internal/unstable features in Rustdoc.
589#![cfg_attr(
590 docsrs,
591 feature(doc_cfg, rustdoc_internals),
592 expect(
593 internal_features,
594 reason = "rustdoc_internals is needed for fake_variadic"
595 )
596)]
597
598// public api
599#[cfg(feature = "aio")]
600pub use crate::client::AsyncConnectionConfig;
601pub use crate::client::Client;
602#[cfg(feature = "cache-aio")]
603pub use crate::cmd::CommandCacheConfig;
604pub use crate::cmd::{cmd, pack_command, pipe, Arg, Cmd, Iter};
605pub use crate::commands::{
606 Commands, ControlFlow, CopyOptions, Direction, FlushAllOptions, FlushDbOptions,
607 HashFieldExpirationOptions, LposOptions, PubSubCommands, ScanOptions, SetOptions,
608 SortedSetAddOptions, TypedCommands, UpdateCheck,
609};
610pub use crate::connection::{
611 parse_redis_url, transaction, Connection, ConnectionAddr, ConnectionInfo, ConnectionLike,
612 IntoConnectionInfo, Msg, PubSub, RedisConnectionInfo, TlsMode,
613};
614pub use crate::parser::{parse_redis_value, Parser};
615pub use crate::pipeline::Pipeline;
616
617#[cfg(feature = "script")]
618#[cfg_attr(docsrs, doc(cfg(feature = "script")))]
619pub use crate::script::{Script, ScriptInvocation};
620
621// preserve grouping and order
622#[rustfmt::skip]
623pub use crate::types::{
624 // utility functions
625 from_redis_value,
626 from_owned_redis_value,
627 make_extension_error,
628
629 // error kinds
630 ErrorKind,
631 RetryMethod,
632
633 // conversion traits
634 FromRedisValue,
635
636 // utility types
637 InfoDict,
638 NumericBehavior,
639 Expiry,
640 SetExpiry,
641 ExistenceCheck,
642 FieldExistenceCheck,
643 ExpireOption,
644 Role,
645 ReplicaInfo,
646 IntegerReplyOrNoOp,
647 ValueType,
648
649 // error and result types
650 RedisError,
651 RedisResult,
652 RedisWrite,
653 ToRedisArgs,
654
655 // low level values
656 Value,
657 PushKind,
658 VerbatimFormat,
659 ProtocolVersion,
660 PushInfo,
661};
662
663#[cfg(feature = "aio")]
664#[cfg_attr(docsrs, doc(cfg(feature = "aio")))]
665pub use crate::{
666 cmd::AsyncIter, commands::AsyncCommands, commands::AsyncTypedCommands,
667 parser::parse_redis_value_async, types::RedisFuture,
668};
669
670mod macros;
671mod pipeline;
672
673#[cfg(feature = "acl")]
674#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
675pub mod acl;
676
677#[cfg(feature = "aio")]
678#[cfg_attr(docsrs, doc(cfg(feature = "aio")))]
679pub mod aio;
680
681#[cfg(feature = "json")]
682#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
683pub use crate::commands::JsonCommands;
684
685#[cfg(all(feature = "json", feature = "aio"))]
686#[cfg_attr(docsrs, doc(cfg(all(feature = "json", feature = "aio"))))]
687pub use crate::commands::JsonAsyncCommands;
688
689#[cfg(feature = "vector-sets")]
690#[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
691pub use crate::commands::{
692 EmbeddingInput, VAddOptions, VEmbOptions, VSimOptions, VectorAddInput, VectorQuantization,
693 VectorSimilaritySearchInput,
694};
695
696#[cfg(feature = "geospatial")]
697#[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
698pub mod geo;
699
700#[cfg(any(feature = "connection-manager", feature = "cluster-async"))]
701mod subscription_tracker;
702
703#[cfg(feature = "cluster")]
704mod cluster_topology;
705
706#[cfg(feature = "cluster")]
707#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
708pub mod cluster;
709
710#[cfg(feature = "cluster")]
711#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
712mod cluster_client;
713
714#[cfg(feature = "cluster")]
715#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
716mod cluster_pipeline;
717
718/// Routing information for cluster commands.
719#[cfg(feature = "cluster")]
720#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
721pub mod cluster_routing;
722
723#[cfg(feature = "r2d2")]
724#[cfg_attr(docsrs, doc(cfg(feature = "r2d2")))]
725mod r2d2;
726
727#[cfg(all(feature = "bb8", feature = "aio"))]
728#[cfg_attr(docsrs, doc(cfg(all(feature = "bb8", feature = "aio"))))]
729mod bb8;
730
731#[cfg(feature = "streams")]
732#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
733pub mod streams;
734
735#[cfg(feature = "cluster-async")]
736#[cfg_attr(docsrs, doc(cfg(all(feature = "cluster", feature = "aio"))))]
737pub mod cluster_async;
738
739#[cfg(feature = "sentinel")]
740#[cfg_attr(docsrs, doc(cfg(feature = "sentinel")))]
741pub mod sentinel;
742
743#[cfg(feature = "tls-rustls")]
744mod tls;
745
746#[cfg(feature = "tls-rustls")]
747#[cfg_attr(docsrs, doc(cfg(feature = "tls-rustls")))]
748pub use crate::tls::{ClientTlsConfig, TlsCertificates};
749
750#[cfg(feature = "cache-aio")]
751#[cfg_attr(docsrs, doc(cfg(feature = "cache-aio")))]
752pub mod caching;
753
754mod client;
755mod cmd;
756mod commands;
757mod connection;
758/// Module for defining I/O behavior.
759pub mod io;
760mod parser;
761mod script;
762mod types;
763
764macro_rules! check_resp3 {
765 ($protocol: expr) => {
766 use crate::types::ProtocolVersion;
767 if $protocol == ProtocolVersion::RESP2 {
768 return Err(RedisError::from((
769 crate::ErrorKind::InvalidClientConfig,
770 "RESP3 is required for this command",
771 )));
772 }
773 };
774
775 ($protocol: expr, $message: expr) => {
776 use crate::types::ProtocolVersion;
777 if $protocol == ProtocolVersion::RESP2 {
778 return Err(RedisError::from((
779 crate::ErrorKind::InvalidClientConfig,
780 $message,
781 )));
782 }
783 };
784}
785
786pub(crate) use check_resp3;
787
788#[cfg(test)]
789mod tests {
790 use super::*;
791 #[test]
792 fn test_is_send() {
793 const fn assert_send<T: Send>() {}
794
795 assert_send::<Connection>();
796 #[cfg(feature = "cluster")]
797 assert_send::<cluster::ClusterConnection>();
798 }
799}