Skip to main content

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}