embedded_redis/lib.rs
1//! This crate offers a non-blocking 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//!# use core::str::FromStr;
11//!# use core::net::SocketAddr;
12//!# use std_embedded_nal::Stack;
13//!# use std_embedded_time::StandardClock;
14//!# use embedded_redis::network::ConnectionHandler;
15//!#
16//! let mut stack = Stack::default();
17//! let clock = StandardClock::default();
18//!
19//! let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
20//! let mut connection_handler = ConnectionHandler::resp2(server_address);
21//! let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
22//!
23//! let future = client.set("key", "value").unwrap();
24//! let response = future.wait().unwrap();
25//! ```
26#![cfg_attr(all(not(test), not(feature = "mock")), no_std)]
27#![cfg_attr(feature = "strict", deny(warnings))]
28#![cfg_attr(feature = "benchmarks", feature(test))]
29#[cfg(feature = "benchmarks")]
30extern crate test;
31
32extern crate alloc;
33extern crate core;
34
35/// # Redis command abstractions
36///
37/// This crates includes abstractions for some Redis commands like
38/// [AUTH](crate::commands::auth),
39/// [HELLO](crate::commands::hello),
40/// [GET](crate::commands::set),
41/// [SET](crate::commands::set),
42/// [PUBLISH](crate::commands::publish), ...
43///
44/// Each abstraction is implementing the [Command](crate::commands::Command) trait.
45///
46/// For executing arbitrary (not yet implemented) commands, [CustomCommand](crate::commands::custom)
47/// may be used. As alternative you can create new commands by implementing the [Command](crate::commands::Command) trait.
48///
49/// *Please consider contributing new command abstractions*.
50pub mod commands;
51
52/// # Connection and regular Client logic
53///
54/// ## Connection handling
55///
56/// Redis connection is managed by [ConnectionHandler](crate::network::ConnectionHandler).
57/// Both [RESP2](https://redis.io/docs/reference/protocol-spec/) and [RESP3](https://github.com/antirez/RESP3/blob/master/spec.md) protocol
58/// are supported.
59///
60/// Creating a new connection requires the following two things:
61/// * A network stack implementing [embedded-nal](<https://docs.rs/embedded-nal/latest/embedded_nal/>)
62/// * A clock implementing [embedded-time](<https://docs.rs/embedded-time/latest/embedded_time/>). Optional if no Timeout is configured.
63/// ```
64///# use core::str::FromStr;
65///# use core::net::SocketAddr;
66///# use std_embedded_nal::Stack;
67///# use std_embedded_time::StandardClock;
68///# use embedded_redis::network::ConnectionHandler;
69///#
70/// let mut network_stack = Stack::default();
71/// let clock = StandardClock::default();
72///
73/// // RESP2 protocol
74/// let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
75/// let _client = connection_handler.connect(&mut network_stack, Some(&clock)).unwrap();
76///
77/// // RESP3 protocol
78/// let mut connection_handler = ConnectionHandler::resp3(SocketAddr::from_str("127.0.0.1:6379").unwrap());
79/// let _client = connection_handler.connect(&mut network_stack, Some(&clock)).unwrap();
80/// ```
81///
82/// ConnectionHandler is caching the connection, so later recreation of new Clients is cheap.
83///
84/// ### Authentication
85///
86/// Authentication is done in the following way:
87/// ```
88///# use core::str::FromStr;
89///# use core::net::SocketAddr;
90///# use std_embedded_nal::Stack;
91///# use std_embedded_time::StandardClock;
92///# use embedded_redis::network::{ConnectionHandler, Credentials};
93///#
94///# let mut network_stack = Stack::default();
95///# let clock = StandardClock::default();
96///#
97///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
98/// // Password only authentication
99/// let mut connection_handler = ConnectionHandler::resp2(server_address);
100/// connection_handler.auth(Credentials::password_only("secret123!"));
101///
102/// # let _client = connection_handler.connect(&mut network_stack, Some(&clock));
103///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
104///
105/// // ACL based authentication
106/// let mut connection_handler = ConnectionHandler::resp2(server_address);
107/// connection_handler.auth(Credentials::acl("user01", "secret123!"));
108/// # let _client = connection_handler.connect(&mut network_stack, Some(&clock));
109/// ```
110/// ### Timeout
111///
112/// The client includes a timeout mechanism. This allows setting a time limit for responses from the Redis server:
113///
114/// ```
115///# use core::str::FromStr;
116///# use core::net::SocketAddr;
117///# use std_embedded_nal::Stack;
118///# use std_embedded_time::StandardClock;
119///# use embedded_redis::network::{ConnectionHandler, Credentials};
120///# use embedded_time::duration::Extensions;
121///#
122///# let mut network_stack = Stack::default();
123///# let clock = StandardClock::default();
124///#
125///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
126/// let mut connection_handler = ConnectionHandler::resp2(server_address);
127/// connection_handler.timeout(500_000.microseconds());
128/// # let _client = connection_handler.connect(&mut network_stack, Some(&clock)).unwrap();
129/// ```
130/// ### Ping
131///
132/// Optionally, the PING command can also be used to test the connection.
133/// PING is then used every time `connect()` is called after the socket has been cached.
134///
135/// It is recommended to use this option only if a Timeout is configured.
136///
137/// ```
138///# use core::str::FromStr;
139///# use core::net::SocketAddr;
140///# use std_embedded_nal::Stack;
141///# use std_embedded_time::StandardClock;
142///# use embedded_redis::network::{ConnectionHandler, Credentials};
143///# use embedded_time::duration::Extensions;
144///#
145///# let mut network_stack = Stack::default();
146///# let clock = StandardClock::default();
147///#
148///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
149/// let mut connection_handler = ConnectionHandler::resp2(server_address);
150/// connection_handler.timeout(500_000.microseconds());
151/// connection_handler.use_ping();
152/// # let _client = connection_handler.connect(&mut network_stack, Some(&clock)).unwrap();
153/// # let _client = connection_handler.connect(&mut network_stack, Some(&clock)).unwrap();
154/// ```
155///
156/// ### Memory optimization
157///
158/// The following parameters can be used to optimize memory usage respectively to improve heap allocation.
159/// The right parameters can also protect against DOS scenarios, when the received data can
160/// potentially exceed the memory resources.
161/// See [MemoryParameters](crate::network::MemoryParameters) for more details.
162///
163/// ````
164///# use core::str::FromStr;
165///# use core::net::SocketAddr;
166///# use std_embedded_nal::Stack;
167///# use std_embedded_time::StandardClock;
168///# use embedded_redis::commands::set::SetCommand;
169///# use embedded_redis::network::{ConnectionHandler, MemoryParameters};
170///#
171///# let mut stack = Stack::default();
172///# let clock = StandardClock::default();
173///#
174///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
175/// let mut connection_handler = ConnectionHandler::resp3(server_address);
176///
177/// connection_handler.memory(MemoryParameters {
178/// buffer_size: 512,
179/// frame_capacity: 4,
180/// memory_limit: Some(4096)
181/// });
182///
183///# let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
184/// ````
185///
186/// ### Concurrency
187///
188/// While the Client is not Send, the connection handler is.
189/// The handler is designed with the approach that the creation of new clients is cheap.
190/// Thus, the use of short-lived clients in concurrent applications is not a problem.
191///
192/// ## Non-blocking response management
193///
194/// Redis server responses are managed as [Future](crate::network::Future). This allows executing multiple commands non-blocking
195/// simultaneously* and handle responses in any order at any point in time:
196/// ```
197///# use core::str::FromStr;
198///# use core::net::SocketAddr;
199///# use std_embedded_nal::Stack;
200///# use std_embedded_time::StandardClock;
201///# use embedded_redis::commands::set::SetCommand;
202///# use embedded_redis::network::ConnectionHandler;
203///#
204///# let mut stack = Stack::default();
205///# let clock = StandardClock::default();
206///#
207///# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
208///# let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
209///#
210/// let future1 = client.set("key", "value").unwrap();
211/// let future2 = client.set("other", "key").unwrap();
212///
213/// let _ = future2.wait();
214/// let _ = future1.wait();
215/// ```
216///
217/// ### Ready
218/// In order to check whether a future is ready, (the corresponding response has arrived),
219/// the method `ready()` can be used.
220/// If `ready()` returns true, then next call to `wait()` is not expected to block.
221/// ```
222///# use core::str::FromStr;
223///# use core::net::SocketAddr;
224///# use std_embedded_nal::Stack;
225///# use std_embedded_time::StandardClock;
226///# use embedded_redis::commands::set::SetCommand;
227///# use embedded_redis::network::ConnectionHandler;
228///#
229///# let mut stack = Stack::default();
230///# let clock = StandardClock::default();
231///#
232///# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
233///# let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
234///#
235/// let mut future = client.set("key", "value").unwrap();
236///
237/// if future.ready() {
238/// let _ = future.wait();
239/// }
240/// ```
241///
242/// ### Response type
243///
244/// Response type dependents on executed command abstractions, e.g. [GetResponse](crate::commands::get::GetResponse)
245/// in case of [GET command](crate::commands::get).
246///
247/// ### Timeout error
248///
249/// In the event of a timeout error, all remaining futures will be invalidated, as the assignment of
250/// responses can no longer be guaranteed. In case of a invalidated future [InvalidFuture](crate::network::CommandErrors::InvalidFuture)
251/// error is returned when calling `wait()`.
252///
253/// ### Clean state
254///
255/// If the futures are not waited for, it is recommended to call the `close` method before client goes out-of-scope.
256/// This ensures a clean state if a new client is later created with the same network connection.
257///
258/// ````
259///# use core::str::FromStr;
260///# use core::net::SocketAddr;
261///# use std_embedded_nal::Stack;
262///# use std_embedded_time::StandardClock;
263///# use embedded_redis::commands::set::SetCommand;
264///# use embedded_redis::network::ConnectionHandler;
265///#
266///# let mut stack = Stack::default();
267///# let clock = StandardClock::default();
268///#
269///# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
270///# let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
271///#
272/// let _ = client.set("key", "value");
273/// client.close();
274/// ````
275pub mod network;
276pub mod subscription;