Skip to main content

embedded_redis/
lib.rs

1//! This crate offers an asynchronous Redis client for `no_std` targets.
2//! Both RESP2 and RESP3 protocol are supported.
3//!
4//! This crate consists of three parts:
5//! * [network module](crate::network) for network details (connection handling, response management, etc.) + regular command client
6//! * [commands module](crate::commands) for Redis command abstractions
7//! * [subscription module][crate::subscription] for Redis subscription client
8//!
9//! ```
10//! # async_std::task::block_on(async {
11//!# use core::str::FromStr;
12//!# use core::net::SocketAddr;
13//!# use std_embedded_nal_async::Stack;
14//!# use std_embedded_time::StandardClock;
15//!# use embedded_redis::network::ConnectionHandler;
16//!#
17//! let stack = Stack::default();
18//! let clock = StandardClock::default();
19//!
20//! let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
21//! let mut connection_handler = ConnectionHandler::resp2(server_address);
22//! let client = connection_handler.connect(&stack, Some(&clock)).await.unwrap();
23//!
24//! let response = client.set("key", "value").await.unwrap();
25//! # });
26//! ```
27#![cfg_attr(all(not(test), not(feature = "mock")), no_std)]
28#![cfg_attr(feature = "strict", deny(warnings))]
29#![cfg_attr(feature = "benchmarks", feature(test))]
30#[cfg(feature = "benchmarks")]
31extern crate test;
32
33extern crate alloc;
34extern crate core;
35
36/// # Redis command abstractions
37///
38/// This crates includes abstractions for some Redis commands like
39/// [AUTH](crate::commands::auth),
40/// [HELLO](crate::commands::hello),
41/// [GET](crate::commands::set),
42/// [SET](crate::commands::set),
43/// [PUBLISH](crate::commands::publish), ...
44///
45/// Each abstraction is implementing the [Command](crate::commands::Command) trait.
46///
47/// For executing arbitrary (not yet implemented) commands, [CustomCommand](crate::commands::custom)
48/// may be used. As alternative you can create new commands by implementing the [Command](crate::commands::Command) trait.
49///
50/// *Please consider contributing new command abstractions*.
51pub mod commands;
52
53/// # Connection and regular Client logic
54///
55/// ## Connection handling
56///
57/// Redis connection is managed by [ConnectionHandler](crate::network::ConnectionHandler).
58/// Both [RESP2](https://redis.io/docs/reference/protocol-spec/) and [RESP3](https://github.com/antirez/RESP3/blob/master/spec.md) protocol
59/// are supported.
60///
61/// Creating a new connection requires the following two things:
62/// * A network stack implementing [embedded-nal-async](<https://docs.rs/embedded-nal-async/latest/embedded_nal_async/>)
63/// * A clock implementing [embedded-time](<https://docs.rs/embedded-time/latest/embedded_time/>). Optional if no Timeout is configured.
64/// ```
65/// # async_std::task::block_on(async {
66///# use core::str::FromStr;
67///# use core::net::SocketAddr;
68///# use std_embedded_nal_async::Stack;
69///# use std_embedded_time::StandardClock;
70///# use embedded_redis::network::ConnectionHandler;
71///#
72/// let network_stack = Stack::default();
73/// let clock = StandardClock::default();
74///
75/// // RESP2 protocol
76/// let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
77/// let _client = connection_handler.connect(&network_stack, Some(&clock)).await.unwrap();
78///
79/// // RESP3 protocol
80/// let mut connection_handler = ConnectionHandler::resp3(SocketAddr::from_str("127.0.0.1:6379").unwrap());
81/// let _client = connection_handler.connect(&network_stack, Some(&clock)).await.unwrap();
82/// # });
83/// ```
84///
85/// Each `connect()` call opens a new connection owned by the returned client.
86///
87/// ### Authentication
88///
89/// Authentication is done in the following way:
90/// ```
91/// # async_std::task::block_on(async {
92///# use core::str::FromStr;
93///# use core::net::SocketAddr;
94///# use std_embedded_nal_async::Stack;
95///# use std_embedded_time::StandardClock;
96///# use embedded_redis::network::{ConnectionHandler, Credentials};
97///#
98///# let network_stack = Stack::default();
99///# let clock = StandardClock::default();
100///#
101///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
102/// // Password only authentication
103/// let mut connection_handler = ConnectionHandler::resp2(server_address);
104/// connection_handler.auth(Credentials::password_only("secret123!"));
105///
106/// # let _client = connection_handler.connect(&network_stack, Some(&clock)).await;
107///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
108///
109/// // ACL based authentication
110/// let mut connection_handler = ConnectionHandler::resp2(server_address);
111/// connection_handler.auth(Credentials::acl("user01", "secret123!"));
112/// # let _client = connection_handler.connect(&network_stack, Some(&clock)).await;
113/// # });
114/// ```
115/// ### Timeout
116///
117/// The client includes a timeout mechanism. This allows setting a time limit for responses from the Redis server:
118///
119/// ```
120/// # async_std::task::block_on(async {
121///# use core::str::FromStr;
122///# use core::net::SocketAddr;
123///# use std_embedded_nal_async::Stack;
124///# use std_embedded_time::StandardClock;
125///# use embedded_redis::network::{ConnectionHandler, Credentials};
126///# use embedded_time::duration::Extensions;
127///#
128///# let network_stack = Stack::default();
129///# let clock = StandardClock::default();
130///#
131///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
132/// let mut connection_handler = ConnectionHandler::resp2(server_address);
133/// connection_handler.timeout(500_000.microseconds());
134/// # let _client = connection_handler.connect(&network_stack, Some(&clock)).await.unwrap();
135/// # });
136/// ```
137/// ### Ping
138///
139/// Optionally, the PING command can also be used to test the connection.
140/// PING is then used to verify every newly opened connection.
141///
142/// It is recommended to use this option only if a Timeout is configured.
143///
144/// ```
145/// # async_std::task::block_on(async {
146///# use core::str::FromStr;
147///# use core::net::SocketAddr;
148///# use std_embedded_nal_async::Stack;
149///# use std_embedded_time::StandardClock;
150///# use embedded_redis::network::{ConnectionHandler, Credentials};
151///# use embedded_time::duration::Extensions;
152///#
153///# let network_stack = Stack::default();
154///# let clock = StandardClock::default();
155///#
156///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
157/// let mut connection_handler = ConnectionHandler::resp2(server_address);
158/// connection_handler.timeout(500_000.microseconds());
159/// connection_handler.use_ping();
160/// # let _client = connection_handler.connect(&network_stack, Some(&clock)).await.unwrap();
161/// # let _client = connection_handler.connect(&network_stack, Some(&clock)).await.unwrap();
162/// # });
163/// ```
164///
165/// ### Memory optimization
166///
167/// The following parameters can be used to optimize memory usage respectively to improve heap allocation.
168/// The right parameters can also protect against DOS scenarios, when the received data can
169/// potentially exceed the memory resources.
170/// See [MemoryParameters](crate::network::MemoryParameters) for more details.
171///
172/// ````
173/// # async_std::task::block_on(async {
174///# use core::str::FromStr;
175///# use core::net::SocketAddr;
176///# use std_embedded_nal_async::Stack;
177///# use std_embedded_time::StandardClock;
178///# use embedded_redis::commands::set::SetCommand;
179///# use embedded_redis::network::{ConnectionHandler, MemoryParameters};
180///#
181///# let stack = Stack::default();
182///# let clock = StandardClock::default();
183///#
184///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
185/// let mut connection_handler = ConnectionHandler::resp3(server_address);
186///
187/// connection_handler.memory(MemoryParameters {
188///     buffer_size: 512,
189///     frame_capacity: 4,
190///     memory_limit: Some(4096)
191/// });
192///
193///# let client = connection_handler.connect(&stack, Some(&clock)).await.unwrap();
194/// # });
195/// ````
196///
197/// ### Concurrency
198///
199/// A client owns one connection. Access to that connection is serialized asynchronously, so
200/// multiple command operations may be polled concurrently on the same executor. Whether a client
201/// can be moved between threads depends on the concrete connection, clock, and protocol types.
202///
203/// ## Asynchronous response management
204///
205/// [`Client::send`](crate::network::Client::send) and the command shorthand methods asynchronously
206/// wait for and return the evaluated Redis response directly. Polling several command operations
207/// concurrently enables pipelining:
208/// ```
209/// # async_std::task::block_on(async {
210///# use core::str::FromStr;
211///# use core::net::SocketAddr;
212///# use futures_util::future::join;
213///# use std_embedded_nal_async::Stack;
214///# use std_embedded_time::StandardClock;
215///# use embedded_redis::network::ConnectionHandler;
216///#
217///# let stack = Stack::default();
218///# let clock = StandardClock::default();
219///#
220///# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
221///# let client = connection_handler.connect(&stack, Some(&clock)).await.unwrap();
222///#
223/// let first = client.set("first_key", "first_value");
224/// let second = client.set("second_key", "second_value");
225///
226/// let (first_response, second_response) = join(first, second).await;
227/// first_response.unwrap();
228/// second_response.unwrap();
229/// # });
230/// ```
231///
232/// ### Response type
233///
234/// Response type dependents on executed command abstractions, e.g. [GetResponse](crate::commands::get::GetResponse)
235/// in case of [GET command](crate::commands::get).
236///
237/// ### Timeout error
238///
239/// In the event of a timeout error, all other concurrently pending command operations are
240/// invalidated, as the assignment of responses can no longer be guaranteed. Those operations
241/// return [InvalidFuture](crate::network::CommandErrors::InvalidFuture).
242///
243/// ### Clean state
244///
245/// If an in-flight command operation is cancelled after sending its command, `close().await`
246/// consumes its pending response before the client is dropped. Dropping the client itself closes
247/// its owned connection.
248///
249/// ````
250/// # async_std::task::block_on(async {
251///# use core::str::FromStr;
252///# use core::net::SocketAddr;
253///# use std_embedded_nal_async::Stack;
254///# use std_embedded_time::StandardClock;
255///# use embedded_redis::commands::set::SetCommand;
256///# use embedded_redis::network::ConnectionHandler;
257///#
258///# let stack = Stack::default();
259///# let clock = StandardClock::default();
260///#
261///# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
262///# let client = connection_handler.connect(&stack, Some(&clock)).await.unwrap();
263///#
264/// let _ = client.set("key", "value").await;
265/// client.close().await;
266/// # });
267/// ````
268pub mod network;
269pub mod subscription;