Skip to content

Commit b8cb920

Browse files
authored
Add optional info function to redis_module macro (#211)
* Add optional info function to redis_module macro * Add InfoContext to abstract raw RedisModuleInfoCtx * Add InfoContext::add_info_field_long_long
1 parent b1ba591 commit b8cb920

File tree

6 files changed

+93
-12
lines changed

6 files changed

+93
-12
lines changed

examples/hello.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#[macro_use]
22
extern crate redis_module;
33

4-
use redis_module::{Context, RedisError, RedisResult, RedisString};
4+
use redis_module::InfoContext;
5+
use redis_module::Status;
56

7+
use redis_module::{Context, RedisError, RedisResult, RedisString};
68
fn hello_mul(_: &Context, args: Vec<RedisString>) -> RedisResult {
79
if args.len() < 2 {
810
return Err(RedisError::WrongArity);
@@ -22,12 +24,19 @@ fn hello_mul(_: &Context, args: Vec<RedisString>) -> RedisResult {
2224
return Ok(response.into());
2325
}
2426

27+
fn add_info(ctx: &InfoContext, _for_crash_report: bool) {
28+
if ctx.add_info_section(Some("hello")) == Status::Ok {
29+
ctx.add_info_field_str("field", "hello_value");
30+
}
31+
}
32+
2533
//////////////////////////////////////////////////////
2634

2735
redis_module! {
2836
name: "hello",
2937
version: 1,
3038
data_types: [],
39+
info: add_info,
3140
commands: [
3241
["hello.mul", hello_mul, "", 0, 0, 0],
3342
],

src/context/mod.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use std::ffi::CString;
2-
use std::os::raw::{c_char, c_int, c_long};
2+
use std::os::raw::{c_char, c_int, c_long, c_longlong};
33
use std::ptr;
44

55
use crate::key::{RedisKey, RedisKeyWritable};
6-
use crate::raw;
76
use crate::raw::ModuleOptions;
8-
use crate::LogLevel;
7+
use crate::{add_info_field_long_long, add_info_field_str, raw, Status};
8+
use crate::{add_info_section, LogLevel};
99
use crate::{RedisError, RedisResult, RedisString, RedisValue};
1010

1111
#[cfg(feature = "experimental-api")]
@@ -286,3 +286,36 @@ impl Context {
286286
unsafe { raw::RedisModule_SetModuleOptions.unwrap()(self.ctx, options.bits()) };
287287
}
288288
}
289+
290+
pub struct InfoContext {
291+
pub ctx: *mut raw::RedisModuleInfoCtx,
292+
}
293+
294+
impl InfoContext {
295+
pub fn new(ctx: *mut raw::RedisModuleInfoCtx) -> Self {
296+
Self { ctx }
297+
}
298+
299+
pub fn add_info_section(
300+
&self,
301+
name: Option<&str>, // assume NULL terminated
302+
) -> Status {
303+
add_info_section(self.ctx, name)
304+
}
305+
306+
pub fn add_info_field_str(
307+
&self,
308+
name: &str, // assume NULL terminated
309+
content: &str,
310+
) -> Status {
311+
add_info_field_str(self.ctx, name, content)
312+
}
313+
314+
pub fn add_info_field_long_long(
315+
&self,
316+
name: &str, // assume NULL terminated
317+
value: c_longlong,
318+
) -> Status {
319+
add_info_field_long_long(self.ctx, name, value)
320+
}
321+
}

src/lib.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//#![allow(dead_code)]
22

3+
pub use crate::context::InfoContext;
34
use std::os::raw::c_char;
45
use std::str::Utf8Error;
56
use strum_macros::AsRefStr;
6-
77
extern crate num_traits;
88

99
use libc::size_t;
@@ -61,14 +61,21 @@ fn from_byte_string(byte_str: *const c_char, length: size_t) -> Result<String, U
6161
String::from_utf8(vec_str).map_err(|e| e.utf8_error())
6262
}
6363

64-
pub fn base_info_func(ctx: *mut RedisModuleInfoCtx, for_crash_report: bool) {
64+
pub fn base_info_func(
65+
ctx: &InfoContext,
66+
for_crash_report: bool,
67+
extended_info_func: Option<fn(&InfoContext, bool)>,
68+
) {
6569
if !for_crash_report {
66-
return;
70+
if let Some(func) = extended_info_func {
71+
func(ctx, for_crash_report);
72+
return;
73+
}
6774
}
6875
// add rust trace into the crash report
69-
if add_info_section(ctx, Some("trace")) == Status::Ok {
76+
if ctx.add_info_section(Some("trace")) == Status::Ok {
7077
let current_backtrace = Backtrace::new();
7178
let trace = format!("{:?}", current_backtrace);
72-
raw::add_info_field_str(ctx, "trace", &trace);
79+
ctx.add_info_field_str("trace", &trace);
7380
}
7481
}

src/macros.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ macro_rules! redis_module {
9292
],
9393
$(init: $init_func:ident,)* $(,)*
9494
$(deinit: $deinit_func:ident,)* $(,)*
95+
$(info: $info_func:ident,)?
9596
commands: [
9697
$([
9798
$name:expr,
@@ -113,7 +114,10 @@ macro_rules! redis_module {
113114
ctx: *mut $crate::raw::RedisModuleInfoCtx,
114115
for_crash_report: i32,
115116
) {
116-
$crate::base_info_func(ctx, for_crash_report == 1);
117+
use $crate::InfoContext;
118+
let mut __info_func__cb : Option<fn(&InfoContext, bool)> = None;
119+
$( __info_func__cb = Some($info_func); )?
120+
$crate::base_info_func(&$crate::InfoContext::new(ctx), for_crash_report == 1, __info_func__cb);
117121
}
118122

119123
#[no_mangle]

src/raw.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,18 @@ pub fn add_info_field_str(
604604
}
605605
}
606606

607+
#[allow(clippy::not_unsafe_ptr_arg_deref)]
608+
pub fn add_info_field_long_long(
609+
ctx: *mut RedisModuleInfoCtx,
610+
name: &str, // assume NULL terminated
611+
value: c_longlong,
612+
) -> Status {
613+
let name = CString::new(name).unwrap();
614+
unsafe {
615+
RedisModule_InfoAddFieldLongLong.unwrap()(ctx, name.as_ptr() as *mut c_char, value).into()
616+
}
617+
}
618+
607619
#[cfg(feature = "experimental-api")]
608620
pub fn export_shared_api(
609621
ctx: *mut RedisModuleCtx,

tests/integration.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,30 @@ fn test_keys_pos() -> Result<()> {
3737
let res: Vec<String> = redis::cmd("keys_pos")
3838
.arg(&["a", "1", "b", "2"])
3939
.query(&mut con)
40-
.with_context(|| "failed to run hello.mul")?;
40+
.with_context(|| "failed to run keys_pos")?;
4141
assert_eq!(res, vec!["a", "b"]);
4242

4343
let res: Result<Vec<String>, RedisError> =
4444
redis::cmd("keys_pos").arg(&["a", "1", "b"]).query(&mut con);
4545
if res.is_ok() {
46-
return Err(anyhow::Error::msg("Shold return an error"));
46+
return Err(anyhow::Error::msg("Shuold return an error"));
4747
}
4848

4949
Ok(())
5050
}
51+
52+
#[test]
53+
fn test_hello_info() -> Result<()> {
54+
let _guards = vec![start_redis_server_with_module("hello", 6481)
55+
.with_context(|| "failed to start redis server")?];
56+
let mut con =
57+
get_redis_connection(6481).with_context(|| "failed to connect to redis server")?;
58+
59+
let res: String = redis::cmd("INFO")
60+
.arg("HELLO")
61+
.query(&mut con)
62+
.with_context(|| "failed to run hello.mul")?;
63+
assert!(res.contains("hello_field:hello_value"));
64+
65+
Ok(())
66+
}

0 commit comments

Comments
 (0)