1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
//! This crate offers a non-blocking Redis Client for no_std targets.
//! Both RESP2 and RESP3 protocol are supported.
//!
//! This crate consists of three parts:
//! * [network module](crate::network) for network details (connection handling, response management, etc.) + regular command client
//! * [commands module](crate::commands) for Redis command abstractions
//! * [subscription module][crate::subscription] for Redis subscription client
//!
//! ```
//!# use core::str::FromStr;
//!# use embedded_nal::SocketAddr;
//!# use std_embedded_nal::Stack;
//!# use std_embedded_time::StandardClock;
//!# use embedded_redis::network::ConnectionHandler;
//!#
//! let mut stack = Stack::default();
//! let clock = StandardClock::default();
//!
//! let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
//! let mut connection_handler = ConnectionHandler::resp2(server_address);
//! let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
//!
//! let future = client.set("key", "value").unwrap();
//! let response = future.wait().unwrap();
//! ```
#![cfg_attr(all(not(test), not(feature = "mock")), no_std)]
#![cfg_attr(feature = "strict", deny(warnings))]
#![cfg_attr(feature = "benchmarks", feature(test))]
#[cfg(feature = "benchmarks")]
extern crate test;

extern crate alloc;
extern crate core;

/// # Redis command abstractions
///
/// This crates includes abstractions for some Redis commands like
/// [AUTH](crate::commands::auth),
/// [HELLO](crate::commands::hello),
/// [GET](crate::commands::set),
/// [SET](crate::commands::set),
/// [PUBLISH](crate::commands::publish), ...
///
/// Each abstraction is implementing the [Command](crate::commands::Command) trait.
///
/// For executing arbitrary (not yet implemented) commands, [CustomCommand](crate::commands::custom)
/// may be used. As alternative you can create new commands by implementing the [Command](crate::commands::Command) trait.
///
/// *Please consider contributing new command abstractions*.
pub mod commands;

/// # Connection and regular Client logic
///
/// ## Connection handling
///
/// Redis connection is managed by [ConnectionHandler](crate::network::ConnectionHandler).
/// Both [RESP2](https://redis.io/docs/reference/protocol-spec/) and [RESP3](https://github.com/antirez/RESP3/blob/master/spec.md) protocol
/// are supported.
///
/// Creating a new connection requires the following two things:
/// * A network stack implementing [embedded-nal](<https://docs.rs/embedded-nal/latest/embedded_nal/>)
/// * A clock implementing [embedded-time](<https://docs.rs/embedded-time/latest/embedded_time/>). Optional if no Timeout is configured.
/// ```
///# use core::str::FromStr;
///# use embedded_nal::SocketAddr;
///# use std_embedded_nal::Stack;
///# use std_embedded_time::StandardClock;
///# use embedded_redis::network::ConnectionHandler;
///#
/// let mut network_stack = Stack::default();
/// let clock = StandardClock::default();
///
/// // RESP2 protocol
/// let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
/// let _client = connection_handler.connect(&mut network_stack, Some(&clock)).unwrap();
///
/// // RESP3 protocol
/// let mut connection_handler = ConnectionHandler::resp3(SocketAddr::from_str("127.0.0.1:6379").unwrap());
/// let _client = connection_handler.connect(&mut network_stack, Some(&clock)).unwrap();
/// ```
///
/// ConnectionHandler is caching the connection, so later recreation of new Clients is cheap.
///
/// ### Authentication
///
/// Authentication is done in the following way:
/// ```
///# use core::str::FromStr;
///# use embedded_nal::SocketAddr;
///# use std_embedded_nal::Stack;
///# use std_embedded_time::StandardClock;
///# use embedded_redis::network::{ConnectionHandler, Credentials};
///#
///# let mut network_stack = Stack::default();
///# let clock = StandardClock::default();
///#
///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
/// // Password only authentication
/// let mut connection_handler = ConnectionHandler::resp2(server_address);
/// connection_handler.auth(Credentials::password_only("secret123!"));
///
/// # let _client = connection_handler.connect(&mut network_stack, Some(&clock));
///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
///
/// // ACL based authentication
/// let mut connection_handler = ConnectionHandler::resp2(server_address);
/// connection_handler.auth(Credentials::acl("user01", "secret123!"));
/// # let _client = connection_handler.connect(&mut network_stack, Some(&clock));
/// ```
/// ### Timeout
///
/// The client includes a timeout mechanism. This allows setting a time limit for responses from the Redis server:
///
/// ```
///# use core::str::FromStr;
///# use embedded_nal::SocketAddr;
///# use std_embedded_nal::Stack;
///# use std_embedded_time::StandardClock;
///# use embedded_redis::network::{ConnectionHandler, Credentials};
///# use embedded_time::duration::Extensions;
///#
///# let mut network_stack = Stack::default();
///# let clock = StandardClock::default();
///#
///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
/// let mut connection_handler = ConnectionHandler::resp2(server_address);
/// connection_handler.timeout(500_000.microseconds());
/// # let _client = connection_handler.connect(&mut network_stack, Some(&clock)).unwrap();
/// ```
/// ### Ping
///
/// Optionally, the PING command can also be used to test the connection.
/// PING is then used every time `connect()` is called after the socket has been cached.
///
/// It is recommended to use this option only if a Timeout is configured.
///
/// ```
///# use core::str::FromStr;
///# use embedded_nal::SocketAddr;
///# use std_embedded_nal::Stack;
///# use std_embedded_time::StandardClock;
///# use embedded_redis::network::{ConnectionHandler, Credentials};
///# use embedded_time::duration::Extensions;
///#
///# let mut network_stack = Stack::default();
///# let clock = StandardClock::default();
///#
///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
/// let mut connection_handler = ConnectionHandler::resp2(server_address);
/// connection_handler.timeout(500_000.microseconds());
/// connection_handler.use_ping();
/// # let _client = connection_handler.connect(&mut network_stack, Some(&clock)).unwrap();
/// # let _client = connection_handler.connect(&mut network_stack, Some(&clock)).unwrap();
/// ```
///
/// ### Memory optimization
///
/// The following parameters can be used to optimize memory usage respectively to improve heap allocation.
/// See [MemoryParameters](crate::network::MemoryParameters) for more details.
///
/// ````
///# use core::str::FromStr;
///# use embedded_nal::SocketAddr;
///# use std_embedded_nal::Stack;
///# use std_embedded_time::StandardClock;
///# use embedded_redis::commands::set::SetCommand;
///# use embedded_redis::network::{ConnectionHandler, MemoryParameters};
///#
///# let mut stack = Stack::default();
///# let clock = StandardClock::default();
///#
///# let server_address = SocketAddr::from_str("127.0.0.1:6379").unwrap();
/// let mut connection_handler = ConnectionHandler::resp3(server_address);
///
/// connection_handler.memory(MemoryParameters {
///     buffer_size: 512,
///     frame_capacity: 4
/// });
///
///# let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
/// ````
///
/// ### Concurrency
///
/// While the Client is not Send, the connection handler is.
/// The handler is designed with the approach that the creation of new clients is cheap.
/// Thus, the use of short-lived clients in concurrent applications is not a problem.
///
/// ## Non-blocking response management
///
/// Redis server responses are managed as [Future](crate::network::Future). This allows executing multiple commands non-blocking
/// simultaneously* and handle responses in any order at any point in time:
/// ```
///# use core::str::FromStr;
///# use embedded_nal::SocketAddr;
///# use std_embedded_nal::Stack;
///# use std_embedded_time::StandardClock;
///# use embedded_redis::commands::set::SetCommand;
///# use embedded_redis::network::ConnectionHandler;
///#
///# let mut stack = Stack::default();
///# let clock = StandardClock::default();
///#
///# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
///# let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
///#
/// let future1 = client.set("key", "value").unwrap();
/// let future2 = client.set("other", "key").unwrap();
///
/// let _ = future2.wait();
/// let _ = future1.wait();
/// ```
///
/// ### Ready
/// In order to check whether a future is ready, (the corresponding response has arrived),
/// the method `ready()` can be used.
/// If `ready()` returns true, then next call to `wait()` is not expected to block.
/// ```
///# use core::str::FromStr;
///# use embedded_nal::SocketAddr;
///# use std_embedded_nal::Stack;
///# use std_embedded_time::StandardClock;
///# use embedded_redis::commands::set::SetCommand;
///# use embedded_redis::network::ConnectionHandler;
///#
///# let mut stack = Stack::default();
///# let clock = StandardClock::default();
///#
///# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
///# let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
///#
/// let mut future = client.set("key", "value").unwrap();
///
/// if future.ready() {
///    let _ = future.wait();
/// }
/// ```
///
/// ### Response type
///
/// Response type dependents on executed command abstractions, e.g. [GetResponse](crate::commands::get::GetResponse)
/// in case of [GET command](crate::commands::get).
///
/// ### Timeout error
///
/// In the event of a timeout error, all remaining futures will be invalidated, as the assignment of
/// responses can no longer be guaranteed. In case of a invalidated future [InvalidFuture](crate::network::CommandErrors::InvalidFuture)
/// error is returned when calling `wait()`.
///
/// ### Clean state
///
/// If the futures are not waited for, it is recommended to call the `close` method before client goes out-of-scope.
/// This ensures a clean state if a new client is later created with the same network connection.
///
/// ````
///# use core::str::FromStr;
///# use embedded_nal::SocketAddr;
///# use std_embedded_nal::Stack;
///# use std_embedded_time::StandardClock;
///# use embedded_redis::commands::set::SetCommand;
///# use embedded_redis::network::ConnectionHandler;
///#
///# let mut stack = Stack::default();
///# let clock = StandardClock::default();
///#
///# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
///# let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
///#
/// let _ = client.set("key", "value");
/// client.close();
/// ````
pub mod network;
pub mod subscription;