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`, `smol-native-tls-comp`, or `smol-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 setting [io::tcp::TcpSettings] on the connection configuration objects,
38//! and set the TCP parameters in a more specific manner there.
39//!
40//! ## Connection Handling
41//!
42//! For connecting to redis you can use a client object which then can produce
43//! actual connections. Connections and clients as well as results of
44//! connections and clients are considered [ConnectionLike] objects and
45//! can be used anywhere a request is made.
46//!
47//! The full canonical way to get a connection is to create a client and
48//! to ask for a connection from it:
49//!
50//! ```rust,no_run
51//! extern crate redis;
52//!
53//! fn do_something() -> redis::RedisResult<()> {
54//! let client = redis::Client::open("redis://127.0.0.1/")?;
55//! let mut con = client.get_connection()?;
56//!
57//! /* do something here */
58//!
59//! Ok(())
60//! }
61//! ```
62//!
63//! ## Connection Pooling
64//!
65//! When using a sync connection, it is recommended to use a connection pool in order to handle
66//! disconnects or multi-threaded usage. This can be done using the `r2d2` feature.
67//!
68//! ```rust,no_run
69//! # #[cfg(feature = "r2d2")]
70//! # fn do_something() {
71//! use redis::TypedCommands;
72//!
73//! let client = redis::Client::open("redis://127.0.0.1/").unwrap();
74//! let pool = r2d2::Pool::builder().build(client).unwrap();
75//! let mut conn = pool.get().unwrap();
76//!
77//! conn.set("KEY", "VALUE").unwrap();
78//! let val = conn.get("KEY").unwrap();
79//! # }
80//! ```
81//!
82//! For async connections, connection pooling isn't necessary. The `MultiplexedConnection` is
83//! cheap to clone and can be used safely concurrently from multiple threads, so a single connection can be easily
84//! reused. For automatic reconnections consider using `ConnectionManager` with the `connection-manager` feature.
85//! Async cluster connections also don't require pooling and are thread-safe and reusable.
86//!
87//! ## Optional Features
88//!
89//! There are a few features defined that can enable additional functionality
90//! if so desired. Some of them are turned on by default.
91//!
92//! * `acl`: enables acl support (enabled by default)
93//! * `tokio-comp`: enables support for async usage with the Tokio runtime (optional)
94//! * `smol-comp`: enables support for async usage with the Smol runtime (optional)
95//! * `geospatial`: enables geospatial support (enabled by default)
96//! * `script`: enables script support (enabled by default)
97//! * `streams`: enables high-level interface for interaction with Redis streams (enabled by default)
98//! * `r2d2`: enables r2d2 connection pool support (optional)
99//! * `bb8`: enables bb8 connection pool support (optional)
100//! * `ahash`: enables ahash map/set support & uses ahash internally (+7-10% performance) (optional)
101//! * `cluster`: enables redis cluster support (optional)
102//! * `cluster-async`: enables async redis cluster support (optional)
103//! * `connection-manager`: enables support for automatic reconnection (optional)
104//! * `rust_decimal`, `bigdecimal`, `num-bigint`: enables type conversions to large number representation from different crates (optional)
105//! * `uuid`: enables type conversion to UUID (optional)
106//! * `sentinel`: enables high-level interfaces for communication with Redis sentinels (optional)
107//! * `json`: enables high-level interfaces for communication with the JSON module (optional)
108//! * `cache-aio`: enables **experimental** client side caching for MultiplexedConnection, ConnectionManager and async ClusterConnection (optional)
109//!
110//! ## Connection Parameters
111//!
112//! redis-rs knows different ways to define where a connection should
113//! go. The parameter to [Client::open] needs to implement the
114//! [IntoConnectionInfo] trait of which there are three implementations:
115//!
116//! * string slices in `redis://` URL format.
117//! * URL objects from the redis-url crate.
118//! * [ConnectionInfo] objects.
119//!
120//! The URL format is `redis://[<username>][:<password>@]<hostname>[:port][/[<db>][?protocol=<protocol>]]`
121//!
122//! If Unix socket support is available you can use a unix URL in this format:
123//!
124//! `redis+unix:///<path>[?db=<db>[&pass=<password>][&user=<username>][&protocol=<protocol>]]`
125//!
126//! For compatibility with some other libraries for Redis, the "unix" scheme
127//! is also supported:
128//!
129//! `unix:///<path>[?db=<db>][&pass=<password>][&user=<username>][&protocol=<protocol>]]`
130//!
131//! ## Executing Low-Level Commands
132//!
133//! To execute low-level commands you can use the [cmd::cmd] function which allows
134//! you to build redis requests. Once you have configured a command object
135//! to your liking you can send a query into any [ConnectionLike] object:
136//!
137//! ```rust,no_run
138//! fn do_something(con: &mut redis::Connection) -> redis::RedisResult<()> {
139//! redis::cmd("SET").arg("my_key").arg(42).exec(con)?;
140//! Ok(())
141//! }
142//! ```
143//!
144//! Upon querying the return value is a result object. If you do not care
145//! about the actual return value (other than that it is not a failure)
146//! you can always type annotate it to the unit type `()`.
147//!
148//! Note that commands with a sub-command (like "MEMORY USAGE", "ACL WHOAMI",
149//! "LATENCY HISTORY", etc) must specify the sub-command as a separate `arg`:
150//!
151//! ```rust,no_run
152//! fn do_something(con: &mut redis::Connection) -> redis::RedisResult<usize> {
153//! // This will result in a server error: "unknown command `MEMORY USAGE`"
154//! // because "USAGE" is technically a sub-command of "MEMORY".
155//! redis::cmd("MEMORY USAGE").arg("my_key").query::<usize>(con)?;
156//!
157//! // However, this will work as you'd expect
158//! redis::cmd("MEMORY").arg("USAGE").arg("my_key").query(con)
159//! }
160//! ```
161//!
162//! ## Executing High-Level Commands
163//!
164//! The high-level interface is similar. For it to become available you
165//! need to use the `TypedCommands` or `Commands` traits in which case all `ConnectionLike`
166//! objects the library provides will also have high-level methods which
167//! make working with the protocol easier:
168//!
169//! ```rust,no_run
170//! extern crate redis;
171//! use redis::TypedCommands;
172//!
173//! fn do_something(con: &mut redis::Connection) -> redis::RedisResult<()> {
174//! con.set("my_key", 42)?;
175//! Ok(())
176//! }
177//! ```
178//!
179//! Note that high-level commands are work in progress and many are still
180//! missing!
181//!
182//! ## Pre-typed Commands
183//!
184//! Because redis inherently is mostly type-less and the protocol is not
185//! exactly friendly to developers, this library provides flexible support
186//! for casting values to the intended results. This is driven through the [FromRedisValue] and [ToRedisArgs] traits.
187//!
188//! In most cases, you may like to use defaults provided by the library, to avoid the clutter and development overhead
189//! of specifying types for each command.
190//!
191//! The library facilitates this by providing the [commands::TypedCommands] and [commands::AsyncTypedCommands]. These traits provide functions
192//! with pre-defined and opinionated return types. For example, `set` returns `()`, avoiding the need
193//! for developers to explicitly type each call as returning `()`.
194//!
195//! ```rust,no_run
196//! use redis::TypedCommands;
197//!
198//! fn fetch_an_integer() -> redis::RedisResult<isize> {
199//! // connect to redis
200//! let client = redis::Client::open("redis://127.0.0.1/")?;
201//! let mut con = client.get_connection()?;
202//! // `set` returns a `()`, so we don't need to specify the return type manually unlike in the previous example.
203//! con.set("my_key", 42)?;
204//! // `get_int` returns Result<Option<isize>>, as the key may not be found, or some error may occur.
205//! Ok(con.get_int("my_key").unwrap().unwrap())
206//! }
207//! ```
208//!
209//! ## Custom Type Conversions
210//!
211//! In some cases, the user might want to define their own return value types to various Redis calls.
212//! The library facilitates this by providing the [commands::Commands] and [commands::AsyncCommands]
213//! as alternatives to [commands::TypedCommands] and [commands::AsyncTypedCommands] respectively.
214//!
215//! The `arg` method of the command will accept a wide range of types through
216//! the [ToRedisArgs] trait and the `query` method of a command can convert the
217//! value to what you expect the function to return through the [FromRedisValue]
218//! trait. This is quite flexible and allows vectors, tuples, hashsets, hashmaps
219//! as well as optional values:
220//!
221//! ```rust,no_run
222//! # use redis::Commands;
223//! # use std::collections::{HashMap, HashSet};
224//! # fn do_something() -> redis::RedisResult<()> {
225//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
226//! # let mut con = client.get_connection().unwrap();
227//! let count : i32 = con.get("my_counter")?;
228//! let count = con.get("my_counter").unwrap_or(0i32);
229//! let k : Option<String> = con.get("missing_key")?;
230//! let name : String = con.get("my_name")?;
231//! let bin : Vec<u8> = con.get("my_binary")?;
232//! let map : HashMap<String, i32> = con.hgetall("my_hash")?;
233//! let keys : Vec<String> = con.hkeys("my_hash")?;
234//! let mems : HashSet<i32> = con.smembers("my_set")?;
235//! let (k1, k2) : (String, String) = con.mget(&["k1", "k2"])?;
236//! # Ok(())
237//! # }
238//! ```
239//!
240//! # RESP3 support
241//! Since Redis / Valkey version 6, a newer communication protocol called RESP3 is supported.
242//! Using this protocol allows the user both to receive a more varied `Value` results, for users
243//! 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
244//! messages on the same connection, instead of creating a new PubSub connection (see "RESP3 async pubsub").
245//!
246
247//!
248//! ## RESP3 pubsub
249//! If you're targeting a Redis/Valkey server of version 6 or above, you can receive
250//! pubsub messages from it without creating another connection, by setting a push sender on the connection.
251//!
252//! ```rust,no_run
253//! # #[cfg(feature = "aio")]
254//! # {
255//! # use futures::prelude::*;
256//! # use redis::AsyncTypedCommands;
257//!
258//! # async fn func() -> redis::RedisResult<()> {
259//! let client = redis::Client::open("redis://127.0.0.1/?protocol=resp3").unwrap();
260//! let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
261//! let config = redis::AsyncConnectionConfig::new().set_push_sender(tx);
262//! let mut con = client.get_multiplexed_async_connection_with_config(&config).await?;
263//! con.subscribe(&["channel_1", "channel_2"]).await?;
264//!
265//! loop {
266//! println!("Received {:?}", rx.recv().await.unwrap());
267//! }
268//! # Ok(()) }
269//! # }
270//! ```
271//!
272//! sync example:
273//!
274//! ```rust,no_run
275//! # {
276//! # use redis::TypedCommands;
277//!
278//! # async fn func() -> redis::RedisResult<()> {
279//! let client = redis::Client::open("redis://127.0.0.1/?protocol=resp3").unwrap();
280//! let (tx, rx) = std::sync::mpsc::channel();
281//! let mut con = client.get_connection().unwrap();
282//! con.set_push_sender(tx);
283//! con.subscribe_resp3(&["channel_1", "channel_2"])?;
284//!
285//! loop {
286//! std::thread::sleep(std::time::Duration::from_millis(10));
287//! // the connection only reads when actively polled, so it must constantly send and receive requests.
288//! _ = con.ping().unwrap();
289//! println!("Received {:?}", rx.try_recv().unwrap());
290//! }
291//! # Ok(()) }
292//! # }
293//! ```
294//!
295//! # Iteration Protocol
296//!
297//! In addition to sending a single query, iterators are also supported. When
298//! used with regular bulk responses they don't give you much over querying and
299//! converting into a vector (both use a vector internally) but they can also
300//! be used with `SCAN` like commands in which case iteration will send more
301//! queries until the cursor is exhausted:
302//!
303//! ```rust,ignore
304//! # fn do_something() -> redis::RedisResult<()> {
305//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
306//! # let mut con = client.get_connection().unwrap();
307//! let mut iter : redis::Iter<isize> = redis::cmd("SSCAN").arg("my_set")
308//! .cursor_arg(0).clone().iter(&mut con)?;
309//! for x in iter {
310//! // do something with the item
311//! }
312//! # Ok(()) }
313//! ```
314//!
315//! As you can see the cursor argument needs to be defined with `cursor_arg`
316//! instead of `arg` so that the library knows which argument needs updating
317//! as the query is run for more items.
318//!
319//! # Pipelining
320//!
321//! In addition to simple queries you can also send command pipelines. This
322//! is provided through the `pipe` function. It works very similar to sending
323//! individual commands but you can send more than one in one go. This also
324//! allows you to ignore individual results so that matching on the end result
325//! is easier:
326//!
327//! ```rust,no_run
328//! # fn do_something() -> redis::RedisResult<()> {
329//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
330//! # let mut con = client.get_connection().unwrap();
331//! let (k1, k2) : (i32, i32) = redis::pipe()
332//! .cmd("SET").arg("key_1").arg(42).ignore()
333//! .cmd("SET").arg("key_2").arg(43).ignore()
334//! .cmd("GET").arg("key_1")
335//! .cmd("GET").arg("key_2").query(&mut con)?;
336//! # Ok(()) }
337//! ```
338//!
339//! If you want the pipeline to be wrapped in a `MULTI`/`EXEC` block you can
340//! easily do that by switching the pipeline into `atomic` mode. From the
341//! caller's point of view nothing changes, the pipeline itself will take
342//! care of the rest for you:
343//!
344//! ```rust,no_run
345//! # fn do_something() -> redis::RedisResult<()> {
346//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
347//! # let mut con = client.get_connection().unwrap();
348//! let (k1, k2) : (i32, i32) = redis::pipe()
349//! .atomic()
350//! .cmd("SET").arg("key_1").arg(42).ignore()
351//! .cmd("SET").arg("key_2").arg(43).ignore()
352//! .cmd("GET").arg("key_1")
353//! .cmd("GET").arg("key_2").query(&mut con)?;
354//! # Ok(()) }
355//! ```
356//!
357//! You can also use high-level commands on pipelines:
358//!
359//! ```rust,no_run
360//! # fn do_something() -> redis::RedisResult<()> {
361//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
362//! # let mut con = client.get_connection().unwrap();
363//! let (k1, k2) : (i32, i32) = redis::pipe()
364//! .atomic()
365//! .set("key_1", 42).ignore()
366//! .set("key_2", 43).ignore()
367//! .get("key_1")
368//! .get("key_2").query(&mut con)?;
369//! # Ok(()) }
370//! ```
371//!
372//! NOTE: Pipelines return a collection of results, even when there's only a single response.
373//! Make sure to wrap single-result pipeline responses in a collection. For example:
374//!
375//! ```rust,no_run
376//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
377//! # let mut con = client.get_connection().unwrap();
378//! let (k1,): (i32,) = redis::pipe()
379//! .cmd("SET").arg("key_1").arg(42).ignore()
380//! .cmd("GET").arg("key_1").query(&mut con).unwrap();
381//! ```
382//!
383//! # Transactions
384//!
385//! Transactions are available through atomic pipelines. In order to use
386//! them in a more simple way you can use the `transaction` function of a
387//! connection:
388//!
389//! ```rust,no_run
390//! # fn do_something() -> redis::RedisResult<()> {
391//! use redis::Commands;
392//! # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
393//! # let mut con = client.get_connection().unwrap();
394//! let key = "the_key";
395//! let (new_val,) : (isize,) = redis::transaction(&mut con, &[key], |con, pipe| {
396//! let old_val : isize = con.get(key)?;
397//! pipe
398//! .set(key, old_val + 1).ignore()
399//! .get(key).query(con)
400//! })?;
401//! println!("The incremented number is: {}", new_val);
402//! # Ok(()) }
403//! ```
404//!
405//! For more information see the `transaction` function.
406//!
407//! # PubSub
408//!
409//! Pubsub is provided through the `PubSub` connection object for sync usage, or the `aio::PubSub`
410//! for async usage.
411//!
412//! Example usage:
413//!
414//! ```rust,no_run
415//! # fn do_something() -> redis::RedisResult<()> {
416//! let client = redis::Client::open("redis://127.0.0.1/")?;
417//! let mut con = client.get_connection()?;
418//! let mut pubsub = con.as_pubsub();
419//! pubsub.subscribe(&["channel_1", "channel_2"])?;
420//!
421//! loop {
422//! let msg = pubsub.get_message()?;
423//! let payload : String = msg.get_payload()?;
424//! println!("channel '{}': {}", msg.get_channel_name(), payload);
425//! }
426//! # }
427//! ```
428//! 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.
429//!
430//! ```rust,no_run
431//! # #[cfg(feature = "aio")]
432//! use futures_util::StreamExt;
433//! # #[cfg(feature = "aio")]
434//! # async fn do_something() -> redis::RedisResult<()> {
435//! let client = redis::Client::open("redis://127.0.0.1/")?;
436//! let (mut sink, mut stream) = client.get_async_pubsub().await?.split();
437//! sink.subscribe("channel_1").await?;
438//!
439//! loop {
440//! let msg = stream.next().await.unwrap();
441//! let payload : String = msg.get_payload().unwrap();
442//! println!("channel '{}': {}", msg.get_channel_name(), payload);
443//! }
444//! # Ok(()) }
445//! ```
446//!
447#![cfg_attr(
448 feature = "script",
449 doc = r##"
450# Scripts
451
452Lua scripts are supported through the `Script` type in a convenient
453way. It will automatically load the script if it does not exist and invoke it.
454
455Example:
456
457```rust,no_run
458# fn do_something() -> redis::RedisResult<()> {
459# let client = redis::Client::open("redis://127.0.0.1/").unwrap();
460# let mut con = client.get_connection().unwrap();
461let script = redis::Script::new(r"
462 return tonumber(ARGV[1]) + tonumber(ARGV[2]);
463");
464let result: isize = script.arg(1).arg(2).invoke(&mut con)?;
465assert_eq!(result, 3);
466# Ok(()) }
467```
468
469Scripts can also be pipelined:
470
471```rust,no_run
472# fn do_something() -> redis::RedisResult<()> {
473# let client = redis::Client::open("redis://127.0.0.1/").unwrap();
474# let mut con = client.get_connection().unwrap();
475let script = redis::Script::new(r"
476 return tonumber(ARGV[1]) + tonumber(ARGV[2]);
477");
478let (a, b): (isize, isize) = redis::pipe()
479 .invoke_script(script.arg(1).arg(2))
480 .invoke_script(script.arg(2).arg(3))
481 .query(&mut con)?;
482
483assert_eq!(a, 3);
484assert_eq!(b, 5);
485# Ok(()) }
486```
487
488Note: unlike a call to [`invoke`](ScriptInvocation::invoke), if the script isn't loaded during the pipeline operation,
489it will not automatically be loaded and retried. The script can be loaded using the
490[`load`](ScriptInvocation::load) operation.
491"##
492)]
493//!
494#![cfg_attr(
495 feature = "aio",
496 doc = r##"
497# Async
498
499In addition to the synchronous interface that's been explained above there also exists an
500asynchronous interface based on [`futures`][] and [`tokio`][] or [`smol`](https://docs.rs/smol/latest/smol/).
501 All async connections are cheap to clone, and clones can be used concurrently from multiple threads.
502
503This interface exists under the `aio` (async io) module (which requires that the `aio` feature
504is enabled) and largely mirrors the synchronous with a few concessions to make it fit the
505constraints of `futures`.
506
507```rust,no_run
508use futures::prelude::*;
509use redis::AsyncTypedCommands;
510
511# #[tokio::main]
512# async fn main() -> redis::RedisResult<()> {
513let client = redis::Client::open("redis://127.0.0.1/").unwrap();
514let mut con = client.get_multiplexed_async_connection().await?;
515
516con.set("key1", b"foo").await?;
517
518redis::cmd("SET").arg(&["key2", "bar"]).exec_async(&mut con).await?;
519
520let result = redis::cmd("MGET")
521 .arg(&["key1", "key2"])
522 .query_async(&mut con)
523 .await;
524assert_eq!(result, Ok(("foo".to_string(), b"bar".to_vec())));
525# Ok(()) }
526```
527
528## Runtime support
529The crate supports multiple runtimes, including `tokio` and `smol`. For Tokio, the crate will
530spawn tasks on the current thread runtime. For smol, the crate will spawn tasks on the the global runtime.
531It is recommended that the crate be used with support only for a single runtime. If the crate is compiled with multiple runtimes,
532the user should call [`crate::aio::prefer_tokio`] or [`crate::aio::prefer_smol`] to set the preferred runtime.
533These functions set global state which automatically chooses the correct runtime for the async connection.
534
535"##
536)]
537//!
538//! [`futures`]:https://crates.io/crates/futures
539//! [`tokio`]:https://tokio.rs
540#![cfg_attr(
541 feature = "sentinel",
542 doc = r##"
543# Sentinel
544Sentinel types allow users to connect to Redis sentinels and find primaries and replicas.
545
546```rust,no_run
547use redis::{ Commands, RedisConnectionInfo };
548use redis::sentinel::{ SentinelServerType, SentinelClient, SentinelNodeConnectionInfo };
549
550let nodes = vec!["redis://127.0.0.1:6379/", "redis://127.0.0.1:6378/", "redis://127.0.0.1:6377/"];
551let sentinel_node_connection_info = SentinelNodeConnectionInfo::default()
552 .set_tls_mode(redis::TlsMode::Insecure);
553let mut sentinel = SentinelClient::build(
554 nodes,
555 String::from("primary1"),
556 Some(sentinel_node_connection_info),
557 redis::sentinel::SentinelServerType::Master,
558)
559.unwrap();
560
561let primary = sentinel.get_connection().unwrap();
562```
563
564An async API also exists:
565
566```rust,no_run
567use futures::prelude::*;
568use redis::{ Commands, RedisConnectionInfo };
569use redis::sentinel::{ SentinelServerType, SentinelClient, SentinelNodeConnectionInfo };
570
571# #[tokio::main]
572# async fn main() -> redis::RedisResult<()> {
573let nodes = vec!["redis://127.0.0.1:6379/", "redis://127.0.0.1:6378/", "redis://127.0.0.1:6377/"];
574let sentinel_node_connection_info = SentinelNodeConnectionInfo::default()
575 .set_tls_mode(redis::TlsMode::Insecure);
576let mut sentinel = SentinelClient::build(
577 nodes,
578 String::from("primary1"),
579 Some(sentinel_node_connection_info),
580 redis::sentinel::SentinelServerType::Master,
581)
582.unwrap();
583
584let primary = sentinel.get_async_connection().await.unwrap();
585# Ok(()) }
586```
587"##
588)]
589//!
590//! # Upgrading to version 1
591//!
592//! * Iterators are now safe by default, without an opt out. This means that the iterators return `RedisResult<Value>` instead of `Value`. See [this PR](https://github.com/redis-rs/redis-rs/pull/1641) for background. If you previously used the "safe_iterators" feature to opt-in to this behavior, just remove the feature declaration. Otherwise you will need to adjust your usage of iterators to account for potential conversion failures.
593//! * Parsing values using [FromRedisValue] no longer returns [RedisError] on failure, in order to save the users checking for various server & client errors in such scenarios. if you rely on the error type when using this trait, you will need to adjust your error handling code. [ParsingError] should only be printed, since it does not contain any user actionable info outside of its error message.
594//! * If you used the `tcp_nodelay` or `keep-alive` features, you'll need to set these values on the connection info you pass to the client use [ConnectionInfo::set_tcp_settings].
595//! * If you used the `disable-client-setinfo` features, you'll need to set [RedisConnectionInfo::skip_set_lib_name].
596//! * If you create [ConnectionInfo], [RedisConnectionInfo], or [sentinel::SentinelNodeConnectionInfo] objects explicitly, now you need to use the builder pattern setters instead of setting fields.
597//! * if you used `MultiplexedConnection::new_with_response_timeout`, it is replaced by [aio::MultiplexedConnection::new_with_config]. `Client::get_multiplexed_tokio_connection_with_response_timeouts`, `Client::get_multiplexed_tokio_connection`, `Client::create_multiplexed_tokio_connection_with_response_timeout`, `Client::create_multiplexed_tokio_connection` were replaced by [Client::get_multiplexed_async_connection_with_config].
598//! * If you're using `tokio::time::pause()` or otherwise manipulating time, you might need to opt out of timeouts using `AsyncConnectionConfig::new().set_connection_timeout(None).set_response_timeout(None)`.
599//! * Async connections now have default timeouts. If you're using blocking commands or other potentially long running commands, you should adjust the timeouts accordingly.
600//! * If you're manually setting `ConnectionManager`'s retry setting, then please re-examine the values you set. `exponential_base` has been made a f32, and `factor` was replaced by `min_delay`, in order to match the documented behavior, instead of the actual erroneous behavior of past versions.
601//! * Vector set types have been moved into the `vector_sets` module, instead of being exposed directly.
602//! * ErrorKind::TypeError was renamed ErrorKind::UnexpectedReturnType, to clarify its meaning. Also fixed some cases where it and ErrorKind::Parse were used interchangeably.
603//! * Connecting to a wildcard address (`0.0.0.0` or `::`) is now explicitly disallowed and will return an error. This change prevents connection timeouts and provides a clearer error message. This affects both standalone and cluster connections. Users relying on this behavior should now connect to a specific, non-wildcard address.
604//! * If you implemented [crate::FromRedisValue] directly, or used `FromRedisValue::from_redis_value`/`FromRedisValue::from_owned_redis_value`, notice that the trait's semantics changed - now the trait requires an owned value by default, instead of a reference. See [the PR](https://github.com/redis-rs/redis-rs/pull/1784) for details.
605//! * The implicit replacement of `GET` with `MGET` or `SET` with `MSET` has been replaced, and limited these and other commands to only take values that serialize into single redis values, as enforced by a compilation failure. Example:
606//!
607//! ```rust,no_run,compile_fail
608//! use redis::Commands;
609//! fn main() -> redis::RedisResult<()> {
610//! let client = redis::Client::open("redis://127.0.0.1/")?;
611//! let mut con = client.get_connection()?;
612//! // `get` should fail compilation, because it receives multiple values
613//! _ = con.get(["foo","bar"]);
614//! Ok(())
615//! }
616//! ```
617//!
618//!
619
620#![deny(non_camel_case_types)]
621#![warn(missing_docs)]
622#![cfg_attr(docsrs, warn(rustdoc::broken_intra_doc_links))]
623// When on docs.rs we want to show tuple variadics in the docs.
624// This currently requires internal/unstable features in Rustdoc.
625#![cfg_attr(
626 docsrs,
627 feature(doc_cfg, rustdoc_internals),
628 expect(
629 internal_features,
630 reason = "rustdoc_internals is needed for fake_variadic"
631 )
632)]
633
634// public api
635#[cfg(feature = "aio")]
636pub use crate::client::AsyncConnectionConfig;
637pub use crate::client::Client;
638#[cfg(feature = "cache-aio")]
639pub use crate::cmd::CommandCacheConfig;
640pub use crate::cmd::{Arg, Cmd, Iter, cmd, pack_command, pipe};
641pub use crate::commands::{
642 Commands, ControlFlow, CopyOptions, Direction, FlushAllOptions, FlushDbOptions,
643 HashFieldExpirationOptions, LposOptions, MSetOptions, PubSubCommands, ScanOptions, SetOptions,
644 SortedSetAddOptions, TypedCommands, UpdateCheck,
645};
646pub use crate::connection::{
647 Connection, ConnectionAddr, ConnectionInfo, ConnectionLike, IntoConnectionInfo, Msg, PubSub,
648 RedisConnectionInfo, TlsMode, parse_redis_url, transaction,
649};
650pub use crate::parser::{Parser, parse_redis_value};
651pub use crate::pipeline::Pipeline;
652#[cfg(feature = "script")]
653#[cfg_attr(docsrs, doc(cfg(feature = "script")))]
654pub use crate::script::{Script, ScriptInvocation};
655#[cfg(feature = "token-based-authentication")]
656pub use crate::{
657 auth::{BasicAuth, StreamingCredentialsProvider},
658 auth_management::{RetryConfig, TokenRefreshConfig},
659};
660#[cfg(feature = "entra-id")]
661pub use {
662 crate::entra_id::{ClientCertificate, EntraIdCredentialsProvider, REDIS_SCOPE_DEFAULT},
663 azure_identity::{
664 ClientCertificateCredentialOptions, ClientSecretCredentialOptions,
665 DeveloperToolsCredentialOptions, ManagedIdentityCredentialOptions, UserAssignedId,
666 },
667};
668
669// preserve grouping and order
670#[rustfmt::skip]
671pub use crate::types::{
672 // utility functions
673 from_redis_value_ref,
674 from_redis_value,
675
676 // conversion traits
677 FromRedisValue,
678
679 // utility types
680 InfoDict,
681 NumericBehavior,
682 Expiry,
683 SetExpiry,
684 ExistenceCheck,
685 FieldExistenceCheck,
686 ExpireOption,
687 Role,
688 ReplicaInfo,
689 IntegerReplyOrNoOp,
690 ValueType,
691 RedisResult,
692 RedisWrite,
693 ToRedisArgs,
694 ToSingleRedisArg,
695 ValueComparison,
696
697 // low level values
698 Value,
699 PushKind,
700 VerbatimFormat,
701 ProtocolVersion,
702 PushInfo,
703};
704
705pub use crate::types::{calculate_value_digest, is_valid_16_bytes_hex_digest};
706
707pub use crate::errors::{
708 ErrorKind, ParsingError, RedisError, RetryMethod, ServerError, ServerErrorKind,
709 make_extension_error,
710};
711
712#[cfg(feature = "aio")]
713#[cfg_attr(docsrs, doc(cfg(feature = "aio")))]
714pub use crate::{
715 cmd::AsyncIter, commands::AsyncCommands, commands::AsyncTypedCommands,
716 parser::parse_redis_value_async, types::RedisFuture,
717};
718
719mod macros;
720mod pipeline;
721
722#[cfg(feature = "acl")]
723#[cfg_attr(docsrs, doc(cfg(feature = "acl")))]
724pub use commands::acl;
725
726#[cfg(feature = "aio")]
727#[cfg_attr(docsrs, doc(cfg(feature = "aio")))]
728pub mod aio;
729
730#[cfg(feature = "json")]
731#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
732pub use crate::commands::JsonCommands;
733
734#[cfg(all(feature = "json", feature = "aio"))]
735#[cfg_attr(docsrs, doc(cfg(all(feature = "json", feature = "aio"))))]
736pub use crate::commands::JsonAsyncCommands;
737
738#[cfg(feature = "vector-sets")]
739#[cfg_attr(docsrs, doc(cfg(feature = "vector-sets")))]
740pub use crate::commands::vector_sets;
741
742#[cfg(feature = "geospatial")]
743#[cfg_attr(docsrs, doc(cfg(feature = "geospatial")))]
744pub use commands::geo;
745
746#[cfg(any(feature = "connection-manager", feature = "cluster-async"))]
747mod subscription_tracker;
748
749#[cfg(feature = "cluster")]
750mod cluster_handling;
751
752#[cfg(feature = "cluster")]
753#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
754pub use cluster_handling::sync_connection as cluster;
755
756/// Routing information for cluster commands.
757#[cfg(feature = "cluster")]
758#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
759pub use cluster_handling::routing as cluster_routing;
760
761/// Pluggable read routing strategies for cluster connections.
762#[cfg(feature = "cluster")]
763#[cfg_attr(docsrs, doc(cfg(feature = "cluster")))]
764pub use cluster_handling::read_routing as cluster_read_routing;
765
766#[cfg(feature = "r2d2")]
767#[cfg_attr(docsrs, doc(cfg(feature = "r2d2")))]
768mod r2d2;
769
770#[cfg(all(feature = "bb8", feature = "aio"))]
771#[cfg_attr(docsrs, doc(cfg(all(feature = "bb8", feature = "aio"))))]
772mod bb8;
773
774#[cfg(feature = "streams")]
775#[cfg_attr(docsrs, doc(cfg(feature = "streams")))]
776pub use commands::streams;
777
778#[cfg(feature = "cluster-async")]
779#[cfg_attr(docsrs, doc(cfg(all(feature = "cluster", feature = "aio"))))]
780pub use cluster_handling::async_connection as cluster_async;
781
782#[cfg(feature = "sentinel")]
783#[cfg_attr(docsrs, doc(cfg(feature = "sentinel")))]
784pub mod sentinel;
785
786#[cfg(feature = "tls-rustls")]
787mod tls;
788
789#[cfg(feature = "tls-rustls")]
790#[cfg_attr(docsrs, doc(cfg(feature = "tls-rustls")))]
791pub use crate::tls::{ClientTlsConfig, TlsCertificates};
792
793#[cfg(feature = "cache-aio")]
794#[cfg_attr(docsrs, doc(cfg(feature = "cache-aio")))]
795pub mod caching;
796
797#[cfg(feature = "entra-id")]
798#[cfg_attr(docsrs, doc(cfg(feature = "entra-id")))]
799pub mod entra_id;
800
801#[cfg(feature = "token-based-authentication")]
802#[cfg_attr(docsrs, doc(cfg(feature = "token-based-authentication")))]
803pub mod auth;
804#[cfg(feature = "token-based-authentication")]
805#[cfg_attr(docsrs, doc(cfg(feature = "token-based-authentication")))]
806pub mod auth_management;
807
808mod client;
809mod cmd;
810mod commands;
811mod connection;
812mod errors;
813/// Module for defining I/O behavior.
814pub mod io;
815mod parser;
816mod script;
817mod types;
818
819macro_rules! check_resp3 {
820 ($protocol: expr) => {
821 if !$protocol.supports_resp3() {
822 return Err(RedisError::from((
823 crate::ErrorKind::InvalidClientConfig,
824 "RESP3 is required for this command",
825 )));
826 }
827 };
828
829 ($protocol: expr, $message: expr) => {
830 if !$protocol.supports_resp3() {
831 return Err(RedisError::from((
832 crate::ErrorKind::InvalidClientConfig,
833 $message,
834 )));
835 }
836 };
837}
838
839pub(crate) use check_resp3;
840
841#[cfg(test)]
842mod tests {
843 use super::*;
844 #[test]
845 fn test_is_send() {
846 const fn assert_send<T: Send>() {}
847
848 assert_send::<Connection>();
849 #[cfg(feature = "cluster")]
850 assert_send::<cluster::ClusterConnection>();
851 }
852}