rustis/commands/array_commands.rs
1use crate::{
2 client::{PreparedCommand, prepare_command},
3 resp::{cmd, serialize_flag},
4};
5use serde::de::DeserializeOwned;
6use serde::{Deserialize, Serialize};
7use smallvec::SmallVec;
8
9/// A group of Redis commands related to [`Arrays`](https://redis.io/docs/latest/commands/?group=array)
10///
11/// An array is a sparse, index-addressed collection: every element sits at an
12/// explicit zero-based index and the gaps between them cost nothing. That is
13/// what separates it from a list — there is no push and pop, only
14/// [`arset`](ArrayCommands::arset) at an index you choose, or
15/// [`arinsert`](ArrayCommands::arinsert) at the position of a cursor the array
16/// carries and you can move with [`arseek`](ArrayCommands::arseek).
17///
18/// Because of the gaps, two lengths matter and never coincide:
19/// [`arlen`](ArrayCommands::arlen) is the highest index plus one, whereas
20/// [`arcount`](ArrayCommands::arcount) is how many slots actually hold a value.
21///
22/// # See Also
23/// [Redis Array Commands](https://redis.io/docs/latest/commands/?group=array)
24pub trait ArrayCommands<'a>: Sized {
25 /// Return the number of non-empty elements in the array.
26 ///
27 /// # See Also
28 /// [<https://redis.io/commands/arcount/>](https://redis.io/commands/arcount/)
29 #[must_use]
30 fn arcount(self, key: impl Serialize) -> PreparedCommand<'a, Self, usize> {
31 prepare_command(self, cmd("ARCOUNT").key(key).readonly())
32 }
33
34 /// Delete the elements at the given indices.
35 ///
36 /// # Return
37 /// The number of elements that were deleted.
38 ///
39 /// # See Also
40 /// [<https://redis.io/commands/ardel/>](https://redis.io/commands/ardel/)
41 #[must_use]
42 fn ardel(
43 self,
44 key: impl Serialize,
45 indices: impl Serialize,
46 ) -> PreparedCommand<'a, Self, usize> {
47 prepare_command(self, cmd("ARDEL").key(key).arg(indices))
48 }
49
50 /// Delete the elements in one or more inclusive `(start, end)` ranges.
51 ///
52 /// # Return
53 /// The number of elements that were deleted.
54 ///
55 /// # See Also
56 /// [<https://redis.io/commands/ardelrange/>](https://redis.io/commands/ardelrange/)
57 #[must_use]
58 fn ardelrange(
59 self,
60 key: impl Serialize,
61 ranges: impl Serialize,
62 ) -> PreparedCommand<'a, Self, usize> {
63 prepare_command(self, cmd("ARDELRANGE").key(key).arg(ranges))
64 }
65
66 /// Get the value at an index.
67 ///
68 /// # Return
69 /// The value, or nil when the slot is empty — so `Option<String>`.
70 ///
71 /// # See Also
72 /// [<https://redis.io/commands/arget/>](https://redis.io/commands/arget/)
73 #[must_use]
74 fn arget<R: DeserializeOwned>(
75 self,
76 key: impl Serialize,
77 index: usize,
78 ) -> PreparedCommand<'a, Self, R> {
79 prepare_command(self, cmd("ARGET").key(key).arg(index).readonly())
80 }
81
82 /// Get the values in the inclusive index range `[start, end]`.
83 ///
84 /// # Return
85 /// One entry per index in the range, empty slots included as nil — so
86 /// `Vec<Option<String>>`.
87 ///
88 /// # See Also
89 /// [<https://redis.io/commands/argetrange/>](https://redis.io/commands/argetrange/)
90 #[must_use]
91 fn argetrange<R: DeserializeOwned>(
92 self,
93 key: impl Serialize,
94 start: usize,
95 end: usize,
96 ) -> PreparedCommand<'a, Self, R> {
97 prepare_command(
98 self,
99 cmd("ARGETRANGE").key(key).arg(start).arg(end).readonly(),
100 )
101 }
102
103 /// Search the elements of a range with textual predicates.
104 ///
105 /// `start` and `end` are indices as strings, so that `-` and `+` can stand
106 /// for the first and last index. Giving a `start` greater than `end` walks
107 /// the range backwards.
108 ///
109 /// # Return
110 /// The indices of the matching elements — `Vec<usize>` — or index/value
111 /// pairs under [`with_values`](ArGrep::with_values), which the server sends
112 /// flat and which therefore deserializes as `Vec<(usize, String)>`.
113 ///
114 /// # See Also
115 /// [<https://redis.io/commands/argrep/>](https://redis.io/commands/argrep/)
116 #[must_use]
117 fn argrep<R: DeserializeOwned>(
118 self,
119 key: impl Serialize,
120 start: impl Serialize,
121 end: impl Serialize,
122 options: ArGrep,
123 ) -> PreparedCommand<'a, Self, R> {
124 prepare_command(
125 self,
126 cmd("ARGREP")
127 .key(key)
128 .arg(start)
129 .arg(end)
130 .arg(options)
131 .readonly(),
132 )
133 }
134
135 /// Return metadata about the array.
136 ///
137 /// # See Also
138 /// [<https://redis.io/commands/arinfo/>](https://redis.io/commands/arinfo/)
139 #[must_use]
140 fn arinfo(
141 self,
142 key: impl Serialize,
143 options: ArInfoOptions,
144 ) -> PreparedCommand<'a, Self, ArrayInfo> {
145 prepare_command(self, cmd("ARINFO").key(key).arg(options).readonly())
146 }
147
148 /// Insert one or more values at consecutive indices, starting at the
149 /// array's insert cursor. The cursor advances by one per value.
150 ///
151 /// # Return
152 /// The last index that was written.
153 ///
154 /// # See Also
155 /// [<https://redis.io/commands/arinsert/>](https://redis.io/commands/arinsert/)
156 #[must_use]
157 fn arinsert(
158 self,
159 key: impl Serialize,
160 values: impl Serialize,
161 ) -> PreparedCommand<'a, Self, usize> {
162 prepare_command(self, cmd("ARINSERT").key(key).arg(values))
163 }
164
165 /// Return the `count` most recently inserted elements.
166 ///
167 /// # See Also
168 /// [<https://redis.io/commands/arlastitems/>](https://redis.io/commands/arlastitems/)
169 #[must_use]
170 fn arlastitems<R: DeserializeOwned>(
171 self,
172 key: impl Serialize,
173 count: usize,
174 options: ArLastItemsOptions,
175 ) -> PreparedCommand<'a, Self, R> {
176 prepare_command(
177 self,
178 cmd("ARLASTITEMS")
179 .key(key)
180 .arg(count)
181 .arg(options)
182 .readonly(),
183 )
184 }
185
186 /// Return the length of the array: the highest index in use, plus one.
187 ///
188 /// Empty slots count towards it; use [`arcount`](ArrayCommands::arcount)
189 /// for the number of elements that actually hold a value.
190 ///
191 /// # See Also
192 /// [<https://redis.io/commands/arlen/>](https://redis.io/commands/arlen/)
193 #[must_use]
194 fn arlen(self, key: impl Serialize) -> PreparedCommand<'a, Self, usize> {
195 prepare_command(self, cmd("ARLEN").key(key).readonly())
196 }
197
198 /// Get the values at several indices.
199 ///
200 /// # Return
201 /// One entry per requested index, nil where the slot is empty — so
202 /// `Vec<Option<String>>`.
203 ///
204 /// # See Also
205 /// [<https://redis.io/commands/armget/>](https://redis.io/commands/armget/)
206 #[must_use]
207 fn armget<R: DeserializeOwned>(
208 self,
209 key: impl Serialize,
210 indices: impl Serialize,
211 ) -> PreparedCommand<'a, Self, R> {
212 prepare_command(self, cmd("ARMGET").key(key).arg(indices).readonly())
213 }
214
215 /// Set several `(index, value)` pairs at once. The pairs need not be
216 /// contiguous nor ordered.
217 ///
218 /// # Return
219 /// The number of slots that were empty before the call.
220 ///
221 /// # See Also
222 /// [<https://redis.io/commands/armset/>](https://redis.io/commands/armset/)
223 #[must_use]
224 fn armset(
225 self,
226 key: impl Serialize,
227 items: impl Serialize,
228 ) -> PreparedCommand<'a, Self, usize> {
229 prepare_command(self, cmd("ARMSET").key(key).arg(items))
230 }
231
232 /// Return the index [`arinsert`](ArrayCommands::arinsert) would write next.
233 ///
234 /// # See Also
235 /// [<https://redis.io/commands/arnext/>](https://redis.io/commands/arnext/)
236 #[must_use]
237 fn arnext(self, key: impl Serialize) -> PreparedCommand<'a, Self, usize> {
238 prepare_command(self, cmd("ARNEXT").key(key).readonly())
239 }
240
241 /// Aggregate the non-empty elements of the inclusive range `[start, end]`.
242 ///
243 /// # Return
244 /// Depends on the operation: a bulk string for
245 /// [`Sum`](ArOperation::Sum), [`Min`](ArOperation::Min) and
246 /// [`Max`](ArOperation::Max), an integer for the bitwise operations,
247 /// [`Match`](ArOperation::Match) and [`Used`](ArOperation::Used). Nil when
248 /// the range holds nothing to aggregate, so the string-valued operations
249 /// want an `Option<String>`.
250 ///
251 /// # See Also
252 /// [<https://redis.io/commands/arop/>](https://redis.io/commands/arop/)
253 #[must_use]
254 fn arop<R: DeserializeOwned>(
255 self,
256 key: impl Serialize,
257 start: usize,
258 end: usize,
259 operation: ArOperation<'_>,
260 ) -> PreparedCommand<'a, Self, R> {
261 prepare_command(
262 self,
263 cmd("AROP")
264 .key(key)
265 .arg(start)
266 .arg(end)
267 .arg(operation)
268 .readonly(),
269 )
270 }
271
272 /// Insert values into a ring buffer of `size` slots.
273 ///
274 /// Each value lands at `insert_index % size`, so once the window is full
275 /// the newest values overwrite the oldest. A `size` smaller than the
276 /// current window truncates the array to fit.
277 ///
278 /// # Return
279 /// The last index that was written.
280 ///
281 /// # See Also
282 /// [<https://redis.io/commands/arring/>](https://redis.io/commands/arring/)
283 #[must_use]
284 fn arring(
285 self,
286 key: impl Serialize,
287 size: usize,
288 values: impl Serialize,
289 ) -> PreparedCommand<'a, Self, usize> {
290 prepare_command(self, cmd("ARRING").key(key).arg(size).arg(values))
291 }
292
293 /// Iterate the non-empty elements of the inclusive range `[start, end]`.
294 ///
295 /// # Return
296 /// The `(index, value)` pairs that exist in the range — `Vec<(usize, String)>`.
297 ///
298 /// # See Also
299 /// [<https://redis.io/commands/arscan/>](https://redis.io/commands/arscan/)
300 #[must_use]
301 fn arscan<R: DeserializeOwned>(
302 self,
303 key: impl Serialize,
304 start: usize,
305 end: usize,
306 limit: impl Into<Option<usize>>,
307 ) -> PreparedCommand<'a, Self, R> {
308 prepare_command(
309 self,
310 cmd("ARSCAN")
311 .key(key)
312 .arg(start)
313 .arg(end)
314 .arg_labeled("LIMIT", limit.into())
315 .readonly(),
316 )
317 }
318
319 /// Move the insert cursor used by [`arinsert`](ArrayCommands::arinsert) and
320 /// [`arring`](ArrayCommands::arring).
321 ///
322 /// # Return
323 /// `true` if the cursor was moved, `false` if the key does not exist.
324 ///
325 /// # See Also
326 /// [<https://redis.io/commands/arseek/>](https://redis.io/commands/arseek/)
327 #[must_use]
328 fn arseek(self, key: impl Serialize, index: usize) -> PreparedCommand<'a, Self, bool> {
329 prepare_command(self, cmd("ARSEEK").key(key).arg(index))
330 }
331
332 /// Set one or more values at consecutive indices, starting at `index`.
333 ///
334 /// # Return
335 /// The number of slots that were empty before the call.
336 ///
337 /// # See Also
338 /// [<https://redis.io/commands/arset/>](https://redis.io/commands/arset/)
339 #[must_use]
340 fn arset(
341 self,
342 key: impl Serialize,
343 index: usize,
344 values: impl Serialize,
345 ) -> PreparedCommand<'a, Self, usize> {
346 prepare_command(self, cmd("ARSET").key(key).arg(index).arg(values))
347 }
348}
349
350/// Options for the [`arinfo`](ArrayCommands::arinfo) command
351#[derive(Default, Serialize)]
352#[serde(rename_all = "UPPERCASE")]
353pub struct ArInfoOptions {
354 #[serde(
355 skip_serializing_if = "std::ops::Not::not",
356 serialize_with = "serialize_flag"
357 )]
358 full: bool,
359}
360
361impl ArInfoOptions {
362 /// Also report the per-slice statistics, which costs a walk of every slice.
363 #[must_use]
364 pub fn full(mut self) -> Self {
365 self.full = true;
366 self
367 }
368}
369
370/// Options for the [`arlastitems`](ArrayCommands::arlastitems) command
371#[derive(Default, Serialize)]
372#[serde(rename_all = "UPPERCASE")]
373pub struct ArLastItemsOptions {
374 #[serde(
375 skip_serializing_if = "std::ops::Not::not",
376 serialize_with = "serialize_flag"
377 )]
378 rev: bool,
379}
380
381impl ArLastItemsOptions {
382 /// Return the most recent element first instead of last.
383 #[must_use]
384 pub fn rev(mut self) -> Self {
385 self.rev = true;
386 self
387 }
388}
389
390/// Aggregate operation of the [`arop`](ArrayCommands::arop) command
391#[derive(Serialize)]
392#[serde(rename_all = "UPPERCASE")]
393#[non_exhaustive]
394pub enum ArOperation<'a> {
395 /// Sum of the numeric values.
396 Sum,
397 /// Smallest numeric value.
398 Min,
399 /// Largest numeric value.
400 Max,
401 /// Bitwise AND, each value truncated towards zero to an integer.
402 And,
403 /// Bitwise OR, each value truncated towards zero to an integer.
404 Or,
405 /// Bitwise XOR, each value truncated towards zero to an integer.
406 Xor,
407 /// Number of elements equal to the given value.
408 Match(&'a str),
409 /// Number of non-empty elements.
410 Used,
411}
412
413/// A textual predicate of the [`argrep`](ArrayCommands::argrep) command
414#[derive(Serialize)]
415#[serde(rename_all = "UPPERCASE")]
416#[non_exhaustive]
417pub enum ArGrepPredicate<'a> {
418 /// The value equals the string.
419 Exact(&'a str),
420 /// The value contains the string.
421 Match(&'a str),
422 /// The value matches the glob-style pattern, with the `*`, `?` and `[...]`
423 /// wildcards of [`SCAN`](crate::commands::GenericCommands::scan)'s `MATCH`.
424 Glob(&'a str),
425 /// The value matches the regular expression.
426 Re(&'a str),
427}
428
429/// Predicates and options of the [`argrep`](ArrayCommands::argrep) command
430///
431/// At least one predicate is required, which is why the type is built from one.
432#[derive(Serialize)]
433#[serde(rename_all = "UPPERCASE")]
434pub struct ArGrep<'a> {
435 #[serde(rename = "")]
436 predicates: SmallVec<[ArGrepPredicate<'a>; 4]>,
437 #[serde(rename = "", skip_serializing_if = "Option::is_none")]
438 combine: Option<ArGrepCombine>,
439 #[serde(skip_serializing_if = "Option::is_none")]
440 limit: Option<usize>,
441 #[serde(
442 skip_serializing_if = "std::ops::Not::not",
443 serialize_with = "serialize_flag"
444 )]
445 withvalues: bool,
446 #[serde(
447 skip_serializing_if = "std::ops::Not::not",
448 serialize_with = "serialize_flag"
449 )]
450 nocase: bool,
451}
452
453/// How [`argrep`](ArrayCommands::argrep) combines several predicates
454#[derive(Serialize)]
455#[serde(rename_all = "UPPERCASE")]
456enum ArGrepCombine {
457 And,
458 Or,
459}
460
461impl<'a> ArGrep<'a> {
462 /// Search with a single predicate.
463 #[must_use]
464 pub fn new(predicate: ArGrepPredicate<'a>) -> Self {
465 let mut predicates = SmallVec::new();
466 predicates.push(predicate);
467 Self {
468 predicates,
469 combine: None,
470 limit: None,
471 withvalues: false,
472 nocase: false,
473 }
474 }
475
476 /// Add another predicate. Combined with
477 /// [`or`](ArGrep::or) unless [`and`](ArGrep::and) says otherwise.
478 #[must_use]
479 pub fn predicate(mut self, predicate: ArGrepPredicate<'a>) -> Self {
480 self.predicates.push(predicate);
481 self
482 }
483
484 /// Match only the elements every predicate accepts.
485 #[must_use]
486 pub fn and(mut self) -> Self {
487 self.combine = Some(ArGrepCombine::And);
488 self
489 }
490
491 /// Match the elements any predicate accepts. This is the default.
492 #[must_use]
493 pub fn or(mut self) -> Self {
494 self.combine = Some(ArGrepCombine::Or);
495 self
496 }
497
498 /// Stop after this many matches.
499 #[must_use]
500 pub fn limit(mut self, limit: usize) -> Self {
501 self.limit = Some(limit);
502 self
503 }
504
505 /// Return each matching value next to its index.
506 #[must_use]
507 pub fn with_values(mut self) -> Self {
508 self.withvalues = true;
509 self
510 }
511
512 /// Compare without regard to case.
513 #[must_use]
514 pub fn nocase(mut self) -> Self {
515 self.nocase = true;
516 self
517 }
518}
519
520/// Result for the [`arinfo`](ArrayCommands::arinfo) command
521///
522/// The last five fields are reported only under
523/// [`full`](ArInfoOptions::full).
524#[derive(Debug, Deserialize)]
525#[serde(rename_all = "kebab-case")]
526#[non_exhaustive]
527pub struct ArrayInfo {
528 /// Number of non-empty elements.
529 pub count: usize,
530 /// Highest index in use, plus one.
531 pub len: usize,
532 /// Index [`arinsert`](ArrayCommands::arinsert) would write next.
533 pub next_insert_index: usize,
534 /// Number of slices the elements are spread over.
535 pub slices: usize,
536 /// Number of entries in the slice directory.
537 pub directory_size: usize,
538 /// Number of entries in the super directory.
539 pub super_dir_entries: usize,
540 /// Number of slots a slice holds.
541 pub slice_size: usize,
542 /// Slices stored as a plain run of slots.
543 #[serde(default)]
544 pub dense_slices: Option<usize>,
545 /// Slices stored as index/value entries, which is what a gap-heavy region
546 /// collapses to.
547 #[serde(default)]
548 pub sparse_slices: Option<usize>,
549 /// Mean number of slots of a dense slice.
550 #[serde(default)]
551 pub avg_dense_size: Option<f64>,
552 /// Mean fraction of a dense slice that holds a value.
553 #[serde(default)]
554 pub avg_dense_fill: Option<f64>,
555 /// Mean number of entries of a sparse slice.
556 #[serde(default)]
557 pub avg_sparse_size: Option<f64>,
558}