Skip to content

Commit 53f494b

Browse files
gkorlandoshadmi
andauthored
fix #207 add support for RedisModule_GetCurrentCommandName (#208)
* fix #207 add support for RedisModule_GetCurrentCommandName * current_command_name returns Result + add test * Fix test ports, remove temp debug code for redis 6.2.5 * Fix test_command_name for redis<6.2.5 Co-authored-by: oshadmi <[email protected]>
1 parent d6aef18 commit 53f494b

File tree

6 files changed

+113
-59
lines changed

6 files changed

+113
-59
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ required-features = ["experimental-api"]
5454
[[example]]
5555
name = "test_helper"
5656
crate-type = ["cdylib"]
57-
required-features = ["test"]
57+
required-features = ["test","experimental-api"]
5858

5959
[[example]]
6060
name = "info"

examples/hello.rs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
#[macro_use]
22
extern crate redis_module;
33

4-
use redis_module::InfoContext;
5-
use redis_module::Status;
6-
74
use redis_module::{Context, RedisError, RedisResult, RedisString};
85
fn hello_mul(_: &Context, args: Vec<RedisString>) -> RedisResult {
96
if args.len() < 2 {
@@ -24,32 +21,13 @@ fn hello_mul(_: &Context, args: Vec<RedisString>) -> RedisResult {
2421
Ok(response.into())
2522
}
2623

27-
fn hello_err(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
28-
if args.is_empty() {
29-
return Err(RedisError::WrongArity);
30-
}
31-
32-
let msg = args.get(1).unwrap();
33-
34-
ctx.reply_error_string(msg.try_as_str().unwrap());
35-
Ok(().into())
36-
}
37-
38-
fn add_info(ctx: &InfoContext, _for_crash_report: bool) {
39-
if ctx.add_info_section(Some("hello")) == Status::Ok {
40-
ctx.add_info_field_str("field", "hello_value");
41-
}
42-
}
43-
4424
//////////////////////////////////////////////////////
4525

4626
redis_module! {
4727
name: "hello",
4828
version: 1,
4929
data_types: [],
50-
info: add_info,
5130
commands: [
5231
["hello.mul", hello_mul, "", 0, 0, 0],
53-
["hello.err", hello_err, "", 0, 0, 0],
5432
],
5533
}

examples/test_helper.rs

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

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

68
fn test_helper_version(ctx: &Context, _args: Vec<RedisString>) -> RedisResult {
79
let ver = ctx.get_redis_version()?;
@@ -18,25 +20,39 @@ fn test_helper_version_rm_call(ctx: &Context, _args: Vec<RedisString>) -> RedisR
1820
Ok(response.into())
1921
}
2022

21-
//////////////////////////////////////////////////////
23+
fn test_helper_command_name(ctx: &Context, _args: Vec<RedisString>) -> RedisResult {
24+
Ok(ctx.current_command_name()?.into())
25+
}
2226

23-
#[cfg(not(feature = "test"))]
24-
redis_module! {
25-
name: "test_helper",
26-
version: 1,
27-
data_types: [],
28-
commands: [
29-
["test_helper.version", test_helper_version, "", 0, 0, 0],
30-
],
27+
fn test_helper_err(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
28+
if args.len() < 1 {
29+
return Err(RedisError::WrongArity);
30+
}
31+
32+
let msg = args.get(1).unwrap();
33+
34+
ctx.reply_error_string(msg.try_as_str().unwrap());
35+
Ok(().into())
3136
}
3237

38+
fn add_info(ctx: &InfoContext, _for_crash_report: bool) {
39+
if ctx.add_info_section(Some("test_helper")) == Status::Ok {
40+
ctx.add_info_field_str("field", "test_helper_value");
41+
}
42+
}
43+
44+
//////////////////////////////////////////////////////
45+
3346
#[cfg(feature = "test")]
3447
redis_module! {
3548
name: "test_helper",
3649
version: 1,
3750
data_types: [],
51+
info: add_info,
3852
commands: [
3953
["test_helper.version", test_helper_version, "", 0, 0, 0],
4054
["test_helper._version_rm_call", test_helper_version_rm_call, "", 0, 0, 0],
55+
["test_helper.name", test_helper_command_name, "", 0, 0, 0],
56+
["test_helper.err", test_helper_err, "", 0, 0, 0],
4157
],
4258
}

src/context/mod.rs

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ use crate::{add_info_field_long_long, add_info_field_str, raw, utils, Status};
88
use crate::{add_info_section, LogLevel};
99
use crate::{RedisError, RedisResult, RedisString, RedisValue};
1010

11+
#[cfg(feature = "experimental-api")]
12+
use std::ffi::CStr;
13+
1114
#[cfg(feature = "experimental-api")]
1215
mod timer;
1316

@@ -294,7 +297,19 @@ impl Context {
294297
unsafe { raw::notify_keyspace_event(self.ctx, event_type, event, keyname) }
295298
}
296299

297-
/// Returns the redis version either by calling ``RedisModule_GetServerVersion`` API,
300+
#[cfg(feature = "experimental-api")]
301+
pub fn current_command_name(&self) -> Result<String, RedisError> {
302+
unsafe {
303+
match raw::RedisModule_GetCurrentCommandName {
304+
Some(cmd) => Ok(CStr::from_ptr(cmd(self.ctx)).to_str().unwrap().to_string()),
305+
None => Err(RedisError::Str(
306+
"API RedisModule_GetCurrentCommandName is not available",
307+
)),
308+
}
309+
}
310+
}
311+
312+
/// Returns the redis version either by calling RedisModule_GetServerVersion API,
298313
/// Or if it is not available, by calling "info server" API and parsing the reply
299314
pub fn get_redis_version(&self) -> Result<Version, RedisError> {
300315
self.get_redis_version_internal(false)
@@ -306,6 +321,22 @@ impl Context {
306321
self.get_redis_version_internal(true)
307322
}
308323

324+
pub fn version_from_info(info: RedisValue) -> Result<Version, RedisError> {
325+
if let RedisValue::SimpleString(info_str) = info {
326+
if let Some(ver) = utils::get_regexp_captures(
327+
info_str.as_str(),
328+
r"(?m)\bredis_version:([0-9]+)\.([0-9]+)\.([0-9]+)\b",
329+
) {
330+
return Ok(Version {
331+
major: ver[0].parse::<c_int>().unwrap(),
332+
minor: ver[1].parse::<c_int>().unwrap(),
333+
patch: ver[2].parse::<c_int>().unwrap(),
334+
});
335+
}
336+
}
337+
Err(RedisError::Str("Error getting redis_version"))
338+
}
339+
309340
#[allow(clippy::not_unsafe_ptr_arg_deref)]
310341
fn get_redis_version_internal(&self, force_use_rm_call: bool) -> Result<Version, RedisError> {
311342
match unsafe { raw::RedisModule_GetServerVersion } {
@@ -315,21 +346,11 @@ impl Context {
315346
}
316347
_ => {
317348
// Call "info server"
318-
if let Ok(RedisValue::SimpleString(s)) = self.call("info", &["server"]) {
319-
if let Some(ver) = utils::get_regexp_captures(
320-
s.as_str(),
321-
r"(?m)\bredis_version:([0-9]+)\.([0-9]+)\.([0-9]+)\b",
322-
) {
323-
return Ok(Version {
324-
major: ver[0].parse::<c_int>().unwrap(),
325-
minor: ver[1].parse::<c_int>().unwrap(),
326-
patch: ver[2].parse::<c_int>().unwrap(),
327-
});
328-
}
349+
if let Ok(info) = self.call("info", &["server"]) {
350+
Context::version_from_info(info)
351+
} else {
352+
Err(RedisError::Str("Error calling \"info server\""))
329353
}
330-
Err(RedisError::Str(
331-
"Error getting redis_version from \"info server\" call",
332-
))
333354
}
334355
}
335356
}

src/include/redismodule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,7 @@ REDISMODULE_API int (*RedisModule_AuthenticateClientWithUser)(RedisModuleCtx *ct
838838
REDISMODULE_API int (*RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id) REDISMODULE_ATTR;
839839
REDISMODULE_API RedisModuleString * (*RedisModule_GetClientCertificate)(RedisModuleCtx *ctx, uint64_t id) REDISMODULE_ATTR;
840840
REDISMODULE_API int *(*RedisModule_GetCommandKeys)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, int *num_keys) REDISMODULE_ATTR;
841+
REDISMODULE_API const char *(*RedisModule_GetCurrentCommandName)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
841842
REDISMODULE_API int (*RedisModule_RegisterDefragFunc)(RedisModuleCtx *ctx, RedisModuleDefragFunc func) REDISMODULE_ATTR;
842843
REDISMODULE_API void *(*RedisModule_DefragAlloc)(RedisModuleDefragCtx *ctx, void *ptr) REDISMODULE_ATTR;
843844
REDISMODULE_API RedisModuleString *(*RedisModule_DefragRedisModuleString)(RedisModuleDefragCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
@@ -1110,6 +1111,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
11101111
REDISMODULE_GET_API(AuthenticateClientWithUser);
11111112
REDISMODULE_GET_API(GetClientCertificate);
11121113
REDISMODULE_GET_API(GetCommandKeys);
1114+
REDISMODULE_GET_API(GetCurrentCommandName);
11131115
REDISMODULE_GET_API(RegisterDefragFunc);
11141116
REDISMODULE_GET_API(DefragAlloc);
11151117
REDISMODULE_GET_API(DefragRedisModuleString);

tests/integration.rs

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1+
use crate::utils::{get_redis_connection, start_redis_server_with_module};
12
use anyhow::Context;
23
use anyhow::Result;
34
use redis::RedisError;
45

5-
use utils::{get_redis_connection, start_redis_server_with_module};
6-
76
mod utils;
87

98
#[test]
@@ -74,38 +73,76 @@ fn test_test_helper_version() -> Result<()> {
7473
Ok(())
7574
}
7675

76+
#[cfg(feature = "experimental-api")]
7777
#[test]
78-
fn test_hello_info() -> Result<()> {
78+
fn test_command_name() -> Result<()> {
79+
use redis_module::RedisValue;
80+
7981
let port: u16 = 6482;
80-
let _guards = vec![start_redis_server_with_module("hello", port)
82+
let _guards = vec![start_redis_server_with_module("test_helper", port)
83+
.with_context(|| "failed to start redis server")?];
84+
let mut con =
85+
get_redis_connection(port).with_context(|| "failed to connect to redis server")?;
86+
87+
// Call the tested command
88+
let res: Result<String, RedisError> = redis::cmd("test_helper.name").query(&mut con);
89+
90+
// The expected result is according to redis version
91+
let info: String = redis::cmd("info")
92+
.arg(&["server"])
93+
.query(&mut con)
94+
.with_context(|| "failed to run test_helper.name")?;
95+
96+
if let Ok(ver) = redis_module::Context::version_from_info(RedisValue::SimpleString(info)) {
97+
if ver.major > 6
98+
|| (ver.major == 6 && ver.minor > 2)
99+
|| (ver.major == 6 && ver.minor == 2 && ver.patch >= 5)
100+
{
101+
assert_eq!(res.unwrap(), String::from("test_helper.name"));
102+
} else {
103+
assert!(res
104+
.err()
105+
.unwrap()
106+
.to_string()
107+
.contains("RedisModule_GetCurrentCommandName is not available"));
108+
}
109+
}
110+
111+
Ok(())
112+
}
113+
114+
#[test]
115+
fn test_test_helper_info() -> Result<()> {
116+
let port: u16 = 6483;
117+
let _guards = vec![start_redis_server_with_module("test_helper", port)
81118
.with_context(|| "failed to start redis server")?];
82119
let mut con =
83120
get_redis_connection(port).with_context(|| "failed to connect to redis server")?;
84121

85122
let res: String = redis::cmd("INFO")
86-
.arg("HELLO")
123+
.arg("TEST_HELPER")
87124
.query(&mut con)
88-
.with_context(|| "failed to run INFO HELLO")?;
89-
assert!(res.contains("hello_field:hello_value"));
125+
.with_context(|| "failed to run INFO TEST_HELPER")?;
126+
assert!(res.contains("test_helper_field:test_helper_value"));
90127

91128
Ok(())
92129
}
93130

94131
#[allow(unused_must_use)]
95132
#[test]
96-
fn test_hello_err() -> Result<()> {
97-
let port: u16 = 6483;
133+
fn test_test_helper_err() -> Result<()> {
134+
let port: u16 = 6484;
98135
let _guards = vec![start_redis_server_with_module("hello", port)
99136
.with_context(|| "failed to start redis server")?];
100137
let mut con =
101138
get_redis_connection(port).with_context(|| "failed to connect to redis server")?;
102139

103140
// Make sure embedded nulls do not cause a crash
104-
redis::cmd("hello.err")
141+
redis::cmd("test_helper.err")
105142
.arg(&["\x00\x00"])
106143
.query::<()>(&mut con);
107144

108-
redis::cmd("hello.err")
145+
redis::cmd("test_helper.err")
109146
.arg(&["no crash\x00"])
110147
.query::<()>(&mut con);
111148

0 commit comments

Comments
 (0)