redis_module_ext/data_type/
methods.rs

1use std::borrow::Borrow;
2use std::ffi::{CStr, CString};
3use std::ops::Add;
4use std::ptr::NonNull;
5
6use redis_module::defrag::DefragContext;
7use redis_module::{RedisResult, RedisString, raw};
8
9use crate::data_type::io::{AofRewriteIo, RdbLoadIo, RdbSaveIo, RedisModuleDigest, RedisModuleKeyOptCtx};
10
11#[doc(hidden)]
12pub trait CStrConv {
13    fn into_cstr(self) -> Option<impl Borrow<std::ffi::CStr>>;
14}
15
16impl CStrConv for String {
17    fn into_cstr(self) -> Option<impl Borrow<std::ffi::CStr>> {
18        std::ffi::CString::new(self).ok()
19    }
20}
21
22impl CStrConv for &String {
23    fn into_cstr(self) -> Option<impl Borrow<std::ffi::CStr>> {
24        std::ffi::CString::new(self.as_bytes()).ok()
25    }
26}
27
28impl CStrConv for &str {
29    fn into_cstr(self) -> Option<impl Borrow<std::ffi::CStr>> {
30        std::ffi::CString::new(self.as_bytes()).ok()
31    }
32}
33
34impl CStrConv for CString {
35    fn into_cstr(self) -> Option<impl Borrow<std::ffi::CStr>> {
36        Some(self)
37    }
38}
39
40impl CStrConv for &CString {
41    fn into_cstr(self) -> Option<impl Borrow<std::ffi::CStr>> {
42        Some(self.as_ref())
43    }
44}
45
46impl CStrConv for &CStr {
47    fn into_cstr(self) -> Option<impl Borrow<std::ffi::CStr>> {
48        Some(self)
49    }
50}
51
52impl<'a, T> CStrConv for &&'a T
53where
54    &'a T: CStrConv,
55{
56    fn into_cstr(self) -> Option<impl Borrow<std::ffi::CStr>> {
57        (**self).into_cstr()
58    }
59}
60
61pub type RdbLoadFn<T> = fn(&mut RdbLoadIo, i32) -> RedisResult<T>;
62
63pub trait RdbLoad<T>: Sized {
64    const FN: RdbLoadFn<Self>;
65
66    fn extern_fn() -> raw::RedisModuleTypeLoadFunc {
67        unsafe extern "C" fn rdb_load<T, S: RdbLoad<T>>(
68            rdb: *mut raw::RedisModuleIO,
69            encver: ::std::os::raw::c_int,
70        ) -> *mut ::std::os::raw::c_void {
71            let Some(rdb) = NonNull::new(rdb) else {
72                return std::ptr::null_mut();
73            };
74
75            let mut io = unsafe { RdbLoadIo::new(rdb) };
76            match S::FN(&mut io, encver) {
77                Err(err) => {
78                    io.log_warning(err.to_string());
79                    std::ptr::null_mut()
80                }
81                Ok(v) => Box::into_raw(Box::new(v)).cast(),
82            }
83        }
84
85        Some(rdb_load::<T, Self>)
86    }
87}
88
89pub type RdbSaveFn<T> = fn(&mut T, &mut RdbSaveIo);
90
91pub trait RdbSave<T>: Sized {
92    const FN: RdbSaveFn<Self>;
93
94    fn extern_fn() -> raw::RedisModuleTypeSaveFunc {
95        unsafe extern "C" fn rdb_save<T, S: RdbSave<T>>(rdb: *mut raw::RedisModuleIO, value: *mut ::std::os::raw::c_void) {
96            let Some(rdb) = NonNull::new(rdb) else {
97                return;
98            };
99
100            if let Some(value) = unsafe { value.cast::<S>().as_mut() } {
101                let mut io = unsafe { RdbSaveIo::new(rdb) };
102                S::FN(value, &mut io)
103            }
104        }
105
106        Some(rdb_save::<T, Self>)
107    }
108}
109
110pub type AofRewriteFn<T> = fn(&mut T, &mut AofRewriteIo, RedisString) -> RedisResult<()>;
111
112pub trait AofRewrite<T>: Sized {
113    const FN: AofRewriteFn<Self>;
114
115    fn extern_fn() -> raw::RedisModuleTypeRewriteFunc {
116        unsafe extern "C" fn aof_rewrite<T, S: AofRewrite<T>>(
117            aof: *mut raw::RedisModuleIO,
118            key: *mut raw::RedisModuleString,
119            value: *mut ::std::os::raw::c_void,
120        ) {
121            let Some(aof) = NonNull::new(aof) else {
122                return;
123            };
124
125            if let Some(value) = unsafe { value.cast::<S>().as_mut() } {
126                let mut io = unsafe { AofRewriteIo::new(aof) };
127                if let Err(err) = S::FN(value, &mut io, RedisString::new(None, key)) {
128                    io.log_warning(err.to_string());
129                }
130            }
131        }
132
133        Some(aof_rewrite::<T, Self>)
134    }
135}
136
137pub type MemUsageFn<T> = fn(&T) -> usize;
138
139pub trait MemUsage<T>: Sized {
140    const FN: MemUsageFn<Self>;
141
142    fn extern_fn() -> raw::RedisModuleTypeMemUsageFunc {
143        unsafe extern "C" fn mem_usage<T, S: MemUsage<T>>(value: *const ::std::os::raw::c_void) -> usize {
144            if let Some(value) = unsafe { value.cast::<S>().as_ref() } {
145                S::FN(value)
146            } else {
147                0
148            }
149        }
150
151        Some(mem_usage::<T, Self>)
152    }
153}
154
155pub type DigestFn<T> = fn(&mut T, &mut RedisModuleDigest);
156
157pub trait Digest<T>: Sized {
158    const FN: DigestFn<Self>;
159
160    fn extern_fn() -> raw::RedisModuleTypeDigestFunc {
161        unsafe extern "C" fn digest<T, S: Digest<T>>(
162            digest: *mut raw::RedisModuleDigest,
163            value: *mut ::std::os::raw::c_void,
164        ) {
165            let Some(digest) = NonNull::new(digest) else {
166                return;
167            };
168
169            if let Some(value) = unsafe { value.cast::<S>().as_mut() } {
170                let mut io = unsafe { RedisModuleDigest::new(digest) };
171                S::FN(value, &mut io)
172            }
173        }
174
175        Some(digest::<T, Self>)
176    }
177}
178
179pub type AuxLoadFn = fn(&mut RdbLoadIo, i32, i32) -> RedisResult<i32>;
180
181pub trait AuxLoad<T>: Sized {
182    const FN: AuxLoadFn;
183
184    fn extern_fn() -> raw::RedisModuleTypeAuxLoadFunc {
185        unsafe extern "C" fn aux_load<T, S: AuxLoad<T>>(rdb: *mut raw::RedisModuleIO, version: i32, when: i32) -> i32 {
186            let Some(rdb) = NonNull::new(rdb) else {
187                return 0;
188            };
189
190            let mut io = unsafe { RdbLoadIo::new(rdb) };
191            match S::FN(&mut io, version, when) {
192                Ok(v) => v,
193                Err(err) => {
194                    io.log_warning(err.to_string());
195                    0
196                }
197            }
198        }
199
200        Some(aux_load::<T, Self>)
201    }
202}
203
204pub type AuxSaveFn = fn(&mut RdbSaveIo, i32);
205
206pub trait AuxSave<T>: Sized {
207    const FN: AuxSaveFn;
208
209    fn extern_fn() -> raw::RedisModuleTypeAuxSaveFunc {
210        unsafe extern "C" fn aux_save<T, S: AuxSave<T>>(rdb: *mut raw::RedisModuleIO, when: i32) {
211            let Some(rdb) = NonNull::new(rdb) else {
212                return;
213            };
214
215            let mut io = unsafe { RdbSaveIo::new(rdb) };
216            S::FN(&mut io, when)
217        }
218
219        Some(aux_save::<T, Self>)
220    }
221}
222
223pub type FreeEffortFn<T> = fn(&T, RedisString) -> usize;
224
225pub trait FreeEffort<T>: Sized {
226    const FN: FreeEffortFn<Self>;
227
228    fn extern_fn() -> raw::RedisModuleTypeFreeEffortFunc {
229        unsafe extern "C" fn free_effort<T, S: FreeEffort<T>>(
230            key: *mut raw::RedisModuleString,
231            value: *const ::std::os::raw::c_void,
232        ) -> usize {
233            if let Some(value) = unsafe { value.cast::<S>().as_ref() } {
234                S::FN(value, RedisString::new(None, key))
235            } else {
236                0
237            }
238        }
239
240        Some(free_effort::<T, Self>)
241    }
242}
243
244pub type UnlinkFn<T> = fn(&T, RedisString);
245
246pub trait Unlink<T>: Sized {
247    const FN: UnlinkFn<Self>;
248
249    fn extern_fn() -> raw::RedisModuleTypeUnlinkFunc {
250        unsafe extern "C" fn unlink<T, S: Unlink<T>>(
251            key: *mut raw::RedisModuleString,
252            value: *const ::std::os::raw::c_void,
253        ) {
254            if let Some(value) = unsafe { value.cast::<S>().as_ref() } {
255                S::FN(value, RedisString::new(None, key))
256            }
257        }
258
259        Some(unlink::<T, Self>)
260    }
261}
262
263pub type CopyFn<T> = fn(&T, RedisString, RedisString) -> Option<T>;
264
265pub trait Copy<T>: Sized {
266    const FN: CopyFn<Self>;
267
268    fn extern_fn() -> raw::RedisModuleTypeCopyFunc {
269        unsafe extern "C" fn copy<T, S: Copy<T>>(
270            from_key: *mut raw::RedisModuleString,
271            to_key: *mut raw::RedisModuleString,
272            value: *const ::std::os::raw::c_void,
273        ) -> *mut ::std::os::raw::c_void {
274            let value = if let Some(value) = unsafe { value.cast::<S>().as_ref() } {
275                S::FN(value, RedisString::new(None, from_key), RedisString::new(None, to_key))
276            } else {
277                None
278            };
279
280            value.map(Box::new).map(Box::into_raw).unwrap_or(std::ptr::null_mut()).cast()
281        }
282
283        Some(copy::<T, Self>)
284    }
285}
286
287pub type DefragFn<T> = fn(&mut T, DefragContext, RedisString) -> i32;
288
289pub trait Defrag<T>: Sized {
290    const FN: DefragFn<Self>;
291
292    fn extern_fn() -> raw::RedisModuleTypeDefragFunc {
293        unsafe extern "C" fn defrag<T, S: Defrag<T>>(
294            ctx: *mut raw::RedisModuleDefragCtx,
295            key: *mut raw::RedisModuleString,
296            value: *mut *mut ::std::os::raw::c_void,
297        ) -> i32 {
298            if value.is_null() {
299                return 0;
300            }
301
302            let value = value as *mut *mut S;
303
304            let ctx = unsafe { DefragContext::new(ctx) };
305            unsafe { value.write(ctx.defrag_realloc(*value)) };
306
307            if let Some(value) = unsafe { (*value).as_mut() } {
308                S::FN(value, ctx, RedisString::new(None, key))
309            } else {
310                0
311            }
312            .add(1)
313        }
314
315        Some(defrag::<T, Self>)
316    }
317}
318
319pub type MemUsage2Fn<T> = fn(&T, &mut RedisModuleKeyOptCtx, usize) -> usize;
320
321pub trait MemUsage2<T>: Sized {
322    const FN: MemUsage2Fn<Self>;
323
324    fn extern_fn() -> raw::RedisModuleTypeMemUsageFunc2 {
325        unsafe extern "C" fn mem_usage2<T, S: MemUsage2<T>>(
326            ctx: *mut raw::RedisModuleKeyOptCtx,
327            value: *const ::std::os::raw::c_void,
328            sample_size: usize,
329        ) -> usize {
330            let Some(ctx) = NonNull::new(ctx) else {
331                return 0;
332            };
333
334            if let Some(value) = unsafe { value.cast::<S>().as_ref() } {
335                let mut io = unsafe { RedisModuleKeyOptCtx::new(ctx) };
336                S::FN(value, &mut io, sample_size)
337            } else {
338                0
339            }
340        }
341
342        Some(mem_usage2::<T, Self>)
343    }
344}
345
346pub type FreeEffort2Fn<T> = fn(&T, &mut RedisModuleKeyOptCtx) -> usize;
347
348pub trait FreeEffort2<T>: Sized {
349    const FN: FreeEffort2Fn<Self>;
350
351    fn extern_fn() -> raw::RedisModuleTypeFreeEffortFunc2 {
352        unsafe extern "C" fn free_effort2<T, S: FreeEffort2<T>>(
353            ctx: *mut raw::RedisModuleKeyOptCtx,
354            value: *const ::std::os::raw::c_void,
355        ) -> usize {
356            let Some(ctx) = NonNull::new(ctx) else {
357                return 0;
358            };
359
360            if let Some(value) = unsafe { value.cast::<S>().as_ref() } {
361                let mut io = unsafe { RedisModuleKeyOptCtx::new(ctx) };
362                S::FN(value, &mut io)
363            } else {
364                0
365            }
366        }
367
368        Some(free_effort2::<T, Self>)
369    }
370}
371
372pub type Unlink2Fn<T> = fn(&T, &mut RedisModuleKeyOptCtx);
373
374pub trait Unlink2<T>: Sized {
375    const FN: Unlink2Fn<Self>;
376
377    fn extern_fn() -> raw::RedisModuleTypeUnlinkFunc2 {
378        unsafe extern "C" fn unlink2<T, S: Unlink2<T>>(
379            ctx: *mut raw::RedisModuleKeyOptCtx,
380            value: *const ::std::os::raw::c_void,
381        ) {
382            let Some(ctx) = NonNull::new(ctx) else {
383                return;
384            };
385
386            if let Some(value) = unsafe { value.cast::<S>().as_ref() } {
387                let mut io = unsafe { RedisModuleKeyOptCtx::new(ctx) };
388                S::FN(value, &mut io)
389            }
390        }
391
392        Some(unlink2::<T, Self>)
393    }
394}
395
396pub type Copy2Fn<T> = fn(&T, &mut RedisModuleKeyOptCtx) -> Option<T>;
397
398pub trait Copy2<T>: Sized {
399    const FN: Copy2Fn<Self>;
400
401    fn extern_fn() -> raw::RedisModuleTypeCopyFunc2 {
402        unsafe extern "C" fn copy2<T, S: Copy2<T>>(
403            ctx: *mut raw::RedisModuleKeyOptCtx,
404            value: *const ::std::os::raw::c_void,
405        ) -> *mut ::std::os::raw::c_void {
406            let Some(ctx) = NonNull::new(ctx) else {
407                return std::ptr::null_mut();
408            };
409
410            let value = if let Some(value) = unsafe { value.cast::<S>().as_ref() } {
411                let mut io = unsafe { RedisModuleKeyOptCtx::new(ctx) };
412                S::FN(value, &mut io)
413            } else {
414                None
415            };
416
417            value.map(Box::new).map(Box::into_raw).unwrap_or(std::ptr::null_mut()).cast()
418        }
419
420        Some(copy2::<T, Self>)
421    }
422}
423
424pub type AuxSave2Fn = fn(&mut RdbSaveIo, i32);
425
426pub trait AuxSave2<T>: Sized {
427    const FN: AuxSave2Fn;
428
429    fn extern_fn() -> raw::RedisModuleTypeAuxSaveFunc {
430        unsafe extern "C" fn aux_save2<T, S: AuxSave2<T>>(rdb: *mut raw::RedisModuleIO, when: i32) {
431            let Some(rdb) = NonNull::new(rdb) else {
432                return;
433            };
434
435            let mut io = unsafe { RdbSaveIo::new(rdb) };
436            S::FN(&mut io, when)
437        }
438
439        Some(aux_save2::<T, Self>)
440    }
441}