Skip to main content

kevy_embedded/
ops_scan.rs

1//! Cursor-based and iterator-based scanning: `scan` / `hscan` /
2//! `zscan` plus the `keys_iter` / `hash_iter` / `zset_iter` adapters.
3//!
4//! Two API shapes:
5//!
6//! - **Cursor-based** (Redis-shaped): `scan(cursor, pattern, count) ->
7//!   (next_cursor, batch)`. A `cursor` of `0` starts a fresh walk; a
8//!   returned `next_cursor` of `0` means the walk completed.
9//! - **Iterator-based** (Rust-shaped): `keys_iter(pattern) -> impl
10//!   Iterator<Item = Vec<u8>>`, etc.
11//!
12//! Each call snapshots the matching set in one shot and slices into
13//! it by cursor. The snapshot is stable across a single walk even
14//! while other writers mutate concurrently, and the memory cost is
15//! bounded by the matching subset.
16
17use std::io;
18
19use crate::store::{Store, store_err};
20
21impl Store {
22    // ---- keyspace scan ----------------------------------------------
23
24    /// `SCAN cursor [MATCH pattern] [COUNT n]` — return up to `count`
25    /// keys, plus the next cursor. `cursor = 0` starts the walk;
26    /// `next_cursor = 0` means the walk completed.
27    ///
28    /// `count` is the page size; pass `usize::MAX` to drain in one
29    /// call.
30    pub fn scan(
31        &self,
32        cursor: u64,
33        pattern: Option<&[u8]>,
34        count: usize,
35    ) -> (u64, Vec<Vec<u8>>) {
36        let all = self.collect_keys(pattern, None);
37        page_into(all, cursor, count)
38    }
39
40    /// Iterator wrapper around [`Self::scan`] — emits every matching
41    /// key as a `Vec<u8>`. Drains the keyspace in one snapshot at
42    /// construction time; matches Rust idioms.
43    pub fn keys_iter(&self, pattern: Option<&[u8]>) -> std::vec::IntoIter<Vec<u8>> {
44        self.collect_keys(pattern, None).into_iter()
45    }
46
47    // ---- hash scan --------------------------------------------------
48
49    /// `HSCAN key cursor [COUNT n]` — return up to `count` `(field,
50    /// value)` pairs from the hash at `key`, plus the next cursor.
51    /// `cursor = 0` starts; `next_cursor = 0` means complete.
52    pub fn hscan(
53        &self,
54        key: &[u8],
55        cursor: u64,
56        count: usize,
57    ) -> io::Result<(u64, Vec<(Vec<u8>, Vec<u8>)>)> {
58        let pairs = self.hgetall(key)?;
59        Ok(page_into(pairs, cursor, count))
60    }
61
62    /// Iterator wrapper around [`Self::hscan`].
63    pub fn hash_iter(
64        &self,
65        key: &[u8],
66    ) -> io::Result<std::vec::IntoIter<(Vec<u8>, Vec<u8>)>> {
67        Ok(self.hgetall(key)?.into_iter())
68    }
69
70    // ---- zset scan --------------------------------------------------
71
72    /// `ZSCAN key cursor [COUNT n]` — return up to `count` `(member,
73    /// score)` pairs from the sorted set at `key`, in ascending score
74    /// order, plus the next cursor.
75    pub fn zscan(
76        &self,
77        key: &[u8],
78        cursor: u64,
79        count: usize,
80    ) -> io::Result<(u64, Vec<(Vec<u8>, f64)>)> {
81        let pairs = self
82            .wshard(key)
83            .store
84            .zrange(key, 0, -1)
85            .map_err(store_err)?;
86        Ok(page_into(pairs, cursor, count))
87    }
88
89    /// Iterator wrapper around [`Self::zscan`].
90    pub fn zset_iter(
91        &self,
92        key: &[u8],
93    ) -> io::Result<std::vec::IntoIter<(Vec<u8>, f64)>> {
94        let pairs = self
95            .wshard(key)
96            .store
97            .zrange(key, 0, -1)
98            .map_err(store_err)?;
99        Ok(pairs.into_iter())
100    }
101}
102
103/// Slice `data[cursor..cursor+count]` and report the next cursor
104/// (`0` when the walk completed).
105fn page_into<T>(data: Vec<T>, cursor: u64, count: usize) -> (u64, Vec<T>) {
106    let total = data.len();
107    let start = (cursor as usize).min(total);
108    let end = start.saturating_add(count).min(total);
109    let batch = data.into_iter().skip(start).take(end - start).collect();
110    let next_cursor = if end >= total { 0 } else { end as u64 };
111    (next_cursor, batch)
112}