@@ -7,19 +7,19 @@ use crate::{key::RedisKey, raw, RedisString};
77
88/// A cursor to scan field/value pairs of a (hash) key.
99///
10- /// This is a wrapper around the [RedisModule_ScanKey](https://redis.io/docs/latest/develop/reference/modules/modules-api-ref/#redismodule_scankey)
11- /// function from the C API. It provides access via a closure given to [`ScanKeyCursor::foreach`]
12- ///
10+ /// It provides access via a closure given to [`ScanKeyCursor::foreach`] or if you need more control, you can use [`ScanKeyCursor::scan`]
11+ /// and implement your own loop, e.g. to allow an early stop.
12+ ///
1313/// ## Example usage
14- ///
14+ ///
1515/// Here we show how to extract values to communicate them back to the Redis client. We assume that the following hash key is setup before:
16- ///
16+ ///
1717/// ```text
1818/// HSET user:123 name Alice age 29 location Austin
1919/// ```
20- ///
20+ ///
2121/// The following example command implementation scans all fields and values in the hash key and returns them as an array of RedisString.
22- ///
22+ ///
2323/// ```ignore
2424/// fn example_scan_key_foreach(ctx: &Context) -> RedisResult {
2525/// let key = ctx.open_key_with_flags("user:123", KeyFlags::NOEFFECTS | KeyFlags::NOEXPIRE | KeyFlags::ACCESS_EXPIRED );
@@ -35,9 +35,9 @@ use crate::{key::RedisKey, raw, RedisString};
3535/// Ok(RedisValue::Array(res.take()))
3636/// }
3737/// ```
38- ///
38+ ///
3939/// The method will produce the following output:
40- ///
40+ ///
4141/// ```text
4242/// 1) "name"
4343/// 2) "Alice"
@@ -63,22 +63,31 @@ impl ScanKeyCursor {
6363 unsafe { raw:: RedisModule_ScanCursorRestart . unwrap ( ) ( self . inner_cursor ) } ;
6464 }
6565
66- /// Implements a callback based foreach loop over all fields and values in the hash key, use that for optimal performance.
67- pub fn foreach < F : FnMut ( & RedisKey , & RedisString , & RedisString ) > ( & self , f : F ) {
68- // Safety: Assumption: c-side initialized the function ptr and it is is never changed,
66+ pub fn scan < F : FnMut ( & RedisKey , & RedisString , & RedisString ) > ( & self , f : F ) -> bool {
67+ // the following is the callback definition
68+ // foreach `ScanKey` call the callback may be called multiple times
69+ use pimpl:: scan_callback;
70+
71+ // Safety: The c-side initialized the function ptr and it is is never changed,
6972 // i.e. after module initialization the function pointers stay valid till the end of the program.
70- let scan_key = unsafe { raw:: RedisModule_ScanKey . unwrap ( ) } ;
73+ let res = unsafe {
74+ raw:: RedisModule_ScanKey . unwrap ( ) (
75+ self . key . key_inner ,
76+ self . inner_cursor ,
77+ Some ( scan_callback :: < F > ) ,
78+ & f as * const F as * mut c_void ,
79+ )
80+ } ;
81+
82+ res != 0
83+ }
7184
72- let mut res = 1 ;
73- while res != 0 {
74- res = unsafe {
75- scan_key (
76- self . key . key_inner ,
77- self . inner_cursor ,
78- Some ( foreach_callback :: < F > ) ,
79- & f as * const F as * mut c_void ,
80- )
81- }
85+ /// Implements a callback based foreach loop over all fields and values in the hash key, use that for optimal performance.
86+ pub fn foreach < F : FnMut ( & RedisKey , & RedisString , & RedisString ) > ( & self , mut f : F ) {
87+ // the following is the callback definition
88+ // foreach `ScanKey` call the callback may be called multiple times
89+ while self . scan ( & mut f) {
90+ // do nothing, the callback does the work
8291 }
8392 }
8493}
@@ -89,28 +98,35 @@ impl Drop for ScanKeyCursor {
8998 }
9099}
91100
92- /// The callback that is used by [`ScanKeyCursor::foreach`] as argument to `RedisModule_ScanKey`.
93- ///
94- /// The `data` pointer is the closure given to [`ScanKeyCursor::foreach`] and the callback forwards
95- /// references to the key, field and value to that closure.
96- unsafe extern "C" fn foreach_callback < F : FnMut ( & RedisKey , & RedisString , & RedisString ) > (
97- key : * mut raw:: RedisModuleKey ,
98- field : * mut raw:: RedisModuleString ,
99- value : * mut raw:: RedisModuleString ,
100- data : * mut c_void ,
101- ) {
102- let ctx = ptr:: null_mut ( ) ;
103- let key = RedisKey :: from_raw_parts ( ctx, key) ;
101+ // the module contains the private implementation details of the cursor.
102+ mod pimpl {
103+ use super :: * ;
104+
105+ /// The callback that is used by [`ScanKeyCursor::scan`] and [`ScanKeyCursor::foreach`] as argument to `RedisModule_ScanKey`.
106+ ///
107+ /// The `data` pointer is the closure given to [`ScanKeyCursor::foreach`] and the callback forwards
108+ /// references to the key, field and value to that closure.
109+ pub ( super ) unsafe extern "C" fn scan_callback <
110+ F : FnMut ( & RedisKey , & RedisString , & RedisString ) ,
111+ > (
112+ key : * mut raw:: RedisModuleKey ,
113+ field : * mut raw:: RedisModuleString ,
114+ value : * mut raw:: RedisModuleString ,
115+ data : * mut c_void ,
116+ ) {
117+ let ctx = ptr:: null_mut ( ) ;
118+ let key = RedisKey :: from_raw_parts ( ctx, key) ;
104119
105- let field = RedisString :: from_redis_module_string ( ctx, field) ;
106- let value = RedisString :: from_redis_module_string ( ctx, value) ;
120+ let field = RedisString :: from_redis_module_string ( ctx, field) ;
121+ let value = RedisString :: from_redis_module_string ( ctx, value) ;
107122
108- let callback = unsafe { & mut * ( data. cast :: < F > ( ) ) } ;
109- callback ( & key, & field, & value) ;
123+ let callback = unsafe { & mut * ( data. cast :: < F > ( ) ) } ;
124+ callback ( & key, & field, & value) ;
110125
111- // we're not the owner of field and value strings
112- field. take ( ) ;
113- value. take ( ) ;
126+ // we're not the owner of field and value strings
127+ field. take ( ) ;
128+ value. take ( ) ;
114129
115- key. take ( ) ; // we're not the owner of the key either
130+ key. take ( ) ; // we're not the owner of the key either
131+ }
116132}
0 commit comments