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
/*!
Defines types related to the [`RESP`](https://redis.io/docs/reference/protocol-spec/) protocol and their encoding/decoding
# Object Model
**rustis** provides an object model in the form of a generic data struct, comparable to the XML DOM,
and which matches perfectly the RESP protocol: the enum [`resp::Value`](Value).
Each variant of this enum matches a [`RESP`](https://redis.io/docs/reference/protocol-spec/) type.
Because, navigating through a [`resp::Value`](Value) instance can be verbose and requires a lot of pattern matching,
**rustis** provides a [`resp::Value`](Value) to Rust type conversion with a [serde](https://serde.rs/)
deserializer implementation of a [`resp::Value`](Value) reference.
This conversion is easily accessible through the associate function [`Value::into`](Value::into).
# Command arguments
**rustis** provides an idiomatic way to pass arguments to [commands](crate::commands).
Basically a [`Command`] is a built through a builder which accepts a command name and one ore more command arguments.
The only requirement for the command argument is that they must implement the serde [`Serialize`](serde::Serialize) trait.
It gives to **rustis** a great flexibility to accept many type of arguments for the same command.
#### Example
```
use rustis::{
client::Client,
commands::{FlushingMode, ServerCommands, StringCommands},
Result,
};
use serde::Serialize;
#[derive(Serialize)]
pub struct MyI32(i32);
#[tokio::main]
async fn main() -> Result<()> {
// Connect the client to a Redis server from its IP and port
let client = Client::connect("127.0.0.1:6379").await?;
// Flush all existing data in Redis
client.flushdb(FlushingMode::Sync).await?;
client.set("key", 12).await?;
client.set("key", 12i64).await?;
client.set("key", 12.12).await?;
client.set("key", true).await?;
client.set("key", "value").await?;
client.set("key", "value".to_owned()).await?;
client.set("key", 'c').await?;
client.set("key", MyI32(12)).await?;
Ok(())
}
```
## Byte arguments and serde limitations
Due to how serde handles byte types, passing raw byte values like `&[u8]`,
`Vec<u8>`, or byte literals like `b"val"` directly as command arguments
will **not** produce a single RESP bulk string. Instead, serde serializes
them as sequences of individual integer values, resulting in a runtime error.
This is a fundamental serde limitation: without specialization, there is no
way to distinguish a `&[u8]` from any other `&[T]` at the trait level.
Note that `&str` works correctly because it is a distinct type, not a slice.
To pass raw bytes as a single bulk string argument, use the provided adapter types:
- [`BulkString`] for owned byte data (`Vec<u8>`) — moves ownership, zero allocation
- [`RefBulkString`] for borrowed byte data (`&[u8]`) — zero allocation
#### Example
```
use rustis::{
client::Client,
commands::StringCommands,
resp::{BulkString, RefBulkString},
Result,
};
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::connect("127.0.0.1:6379").await?;
// &[u8]: use RefBulkString (zero allocation, borrowed)
client.set("key", RefBulkString::new(b"val")).await?;
// Vec<u8>: use BulkString (zero allocation, owned)
client.set("key", BulkString::new(b"val".to_vec())).await?;
Ok(())
}
```
# Command results
**rustis** provides an idiomatic way to convert command results into Rust types with the help of [serde](serde.rs)
You will notice that each built-in command returns a [`PreparedCommand<R>`](crate::client::PreparedCommand)
struct where `R` represents the [`Response`] of the command.
The different command traits implementations ([`Client`](crate::client::Client), [`Pipeline`](crate::client::Pipeline)
or [`Transaction`](crate::client::Transaction)) add a constraint on the reponse `R`:
it must implement serde [`Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html) trait.
Indeed, **rustis** provides a serde deserializer over the RESP wire format.
Each custom struct or enum defined as a response of a built-command implements
serde [`Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html) trait,
in order to deserialize it automatically from a RESP Buffer.
## Warning: a `nil` reply decodes as a neutral value
When the response type is not an [`Option`], a `nil` reply is converted to the neutral value of
that type instead of being rejected: `0` for every integer width, `0.0` for `f32`/`f64`, `false`
for `bool`, `'\0'` for `char` and `""` for `String`.
A missing key is therefore indistinguishable from a key holding that neutral value — a counter,
a quota or a balance that expired reads as zero, with no error to tell you.
Declare `Option<R>` to recover the distinction: it is honoured before any conversion and yields
`None` on `nil`.
#### Example
```
use rustis::{
client::Client,
commands::{FlushingMode, ServerCommands, StringCommands},
Result,
};
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::connect("127.0.0.1:6379").await?;
client.flushall(FlushingMode::Sync).await?;
// the key does not exist: `nil` becomes `0`, as if it held `0`
let counter: i64 = client.get("counter").await?;
assert_eq!(0, counter);
// `Option` keeps the two apart
let counter: Option<i64> = client.get("counter").await?;
assert_eq!(None, counter);
client.set("counter", 0).await?;
let counter: Option<i64> = client.get("counter").await?;
assert_eq!(Some(0), counter);
Ok(())
}
```
*/
// This module is fed directly by server bytes: every length, cardinality and
// offset here is attacker-controlled, so an out-of-bounds index is reachable
// from the wire rather than from a local mistake. Indexing is denied, not
// warned; see the panic policy in `lib.rs`.
// Same reasoning applied to `as`, which `arithmetic_side_effects` does not cover:
// on a wire-supplied value a narrowing cast truncates, a signed-to-unsigned one
// wraps and a float-to-integer one saturates or maps NaN to zero — silently, in
// debug as in release. Every conversion of a decoded value must therefore be
// `TryFrom` or a documented-exact `as`, each surviving cast carrying an
// `#[expect(…, reason = "…")]` naming the invariant that makes it exact.
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;
pub use *;