Skip to content

Commit d6a9269

Browse files
committed
Refactor into modules
1 parent 673e8ec commit d6a9269

File tree

9 files changed

+448
-418
lines changed

9 files changed

+448
-418
lines changed

examples/data_type.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ use libc::c_int;
77
extern crate redismodule;
88

99
use redismodule::error::Error;
10-
use redismodule::Command;
10+
use redismodule::CommandOld;
1111
use redismodule::raw;
12-
use redismodule::Redis;
12+
use redismodule::Context;
1313
use redismodule::raw::module_init;
14-
use redismodule::types::RedisType;
14+
use redismodule::native_types::RedisType;
1515

1616
const MODULE_NAME: &str = "alloc";
1717
const MODULE_VERSION: c_int = 1;
@@ -25,15 +25,15 @@ static MY_TYPE: RedisType = RedisType::new();
2525

2626
struct AllocSetCommand;
2727

28-
impl Command for AllocSetCommand {
28+
impl CommandOld for AllocSetCommand {
2929
fn name() -> &'static str { "alloc.set" }
3030

3131
fn external_command() -> raw::CommandFunc { AllocSetCommand_Redis }
3232

3333
fn str_flags() -> &'static str { "write" }
3434

3535
// Run the command.
36-
fn run(r: Redis, args: &[&str]) -> Result<(), Error> {
36+
fn run(r: Context, args: &[&str]) -> Result<(), Error> {
3737
if args.len() != 3 {
3838
// FIXME: Use RedisModule_WrongArity instead. Return an ArityError here and
3939
// in the low-level implementation call RM_WrongArity.
@@ -102,15 +102,15 @@ pub extern "C" fn AllocSetCommand_Redis(
102102

103103
struct AllocGetCommand;
104104

105-
impl Command for AllocGetCommand {
105+
impl CommandOld for AllocGetCommand {
106106
fn name() -> &'static str { "alloc.get" }
107107

108108
fn external_command() -> raw::CommandFunc { AllocGetCommand_Redis }
109109

110110
fn str_flags() -> &'static str { "" }
111111

112112
// Run the command.
113-
fn run(r: Redis, args: &[&str]) -> Result<(), Error> {
113+
fn run(r: Context, args: &[&str]) -> Result<(), Error> {
114114
if args.len() != 2 {
115115
// FIXME: Use RedisModule_WrongArity instead. Return an ArityError here and
116116
// in the low-level implementation call RM_WrongArity.
@@ -155,15 +155,15 @@ pub extern "C" fn AllocGetCommand_Redis(
155155

156156
struct AllocDelCommand;
157157

158-
impl Command for AllocDelCommand {
158+
impl CommandOld for AllocDelCommand {
159159
fn name() -> &'static str { "alloc.del" }
160160

161161
fn external_command() -> raw::CommandFunc { AllocDelCommand_Redis }
162162

163163
fn str_flags() -> &'static str { "write" }
164164

165165
// Run the command.
166-
fn run(r: Redis, args: &[&str]) -> Result<(), Error> {
166+
fn run(r: Context, args: &[&str]) -> Result<(), Error> {
167167
if args.len() != 2 {
168168
// FIXME: Use RedisModule_WrongArity instead?
169169
return Err(Error::generic(format!(
@@ -195,8 +195,10 @@ pub extern "C" fn AllocDelCommand_Redis(
195195
fn module_on_load(ctx: *mut raw::RedisModuleCtx) -> Result<(), &'static str> {
196196
module_init(ctx, MODULE_NAME, MODULE_VERSION)?;
197197

198-
// TODO: Call this from inside module_init
199-
redismodule::use_redis_alloc();
198+
// TODO: Call this from inside module_init
199+
if true {
200+
redismodule::alloc::use_redis_alloc();
201+
}
200202

201203
MY_TYPE.create_data_type(ctx, "mytype123")?;
202204

examples/hello.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use libc::c_int;
55
extern crate redismodule;
66

77
use redismodule::error::Error;
8-
use redismodule::Command;
8+
use redismodule::CommandOld;
99
use redismodule::raw;
10-
use redismodule::Redis;
10+
use redismodule::Context;
1111
use redismodule::raw::module_init;
1212

1313
const MODULE_NAME: &str = "hello";
@@ -18,15 +18,15 @@ const MODULE_VERSION: c_int = 1;
1818

1919
struct HelloMulCommand;
2020

21-
impl Command for HelloMulCommand {
21+
impl CommandOld for HelloMulCommand {
2222
fn name() -> &'static str { "hello.mul" }
2323

2424
fn external_command() -> raw::CommandFunc { HelloMulCommand_Redis }
2525

2626
fn str_flags() -> &'static str { "write" }
2727

2828
// Run the command.
29-
fn run(r: Redis, args: &[&str]) -> Result<(), Error> {
29+
fn run(r: Context, args: &[&str]) -> Result<(), Error> {
3030
if args.len() != 3 {
3131
return Err(Error::generic(format!(
3232
"Usage: {} <m1> <m2>", Self::name()
@@ -59,15 +59,15 @@ pub extern "C" fn HelloMulCommand_Redis(
5959

6060
struct HelloAddCommand;
6161

62-
impl Command for HelloAddCommand {
62+
impl CommandOld for HelloAddCommand {
6363
fn name() -> &'static str { "hello.add" }
6464

6565
fn external_command() -> raw::CommandFunc { HelloAddCommand_Redis }
6666

6767
fn str_flags() -> &'static str { "write" }
6868

6969
// Run the command.
70-
fn run(r: Redis, args: &[&str]) -> Result<(), Error> {
70+
fn run(r: Context, args: &[&str]) -> Result<(), Error> {
7171
if args.len() != 3 {
7272
// FIXME: Use RedisModule_WrongArity instead?
7373
return Err(Error::generic(format!(

src/alloc.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use std::os::raw::c_void;
2+
use std::alloc::{System, GlobalAlloc, Layout};
3+
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering::SeqCst};
4+
5+
use crate::raw;
6+
7+
pub struct RedisAlloc;
8+
9+
static USE_REDIS_ALLOC: AtomicBool = ATOMIC_BOOL_INIT;
10+
11+
unsafe impl GlobalAlloc for RedisAlloc {
12+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
13+
let use_redis = USE_REDIS_ALLOC.load(SeqCst);
14+
if use_redis {
15+
return raw::RedisModule_Alloc.unwrap()(layout.size()) as *mut u8;
16+
}
17+
System.alloc(layout)
18+
}
19+
20+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
21+
let use_redis = USE_REDIS_ALLOC.load(SeqCst);
22+
if use_redis {
23+
return raw::RedisModule_Free.unwrap()(ptr as *mut c_void);
24+
}
25+
System.dealloc(ptr, layout);
26+
}
27+
}
28+
29+
pub fn use_redis_alloc() {
30+
eprintln!("Using Redis allocator");
31+
USE_REDIS_ALLOC.store(true, SeqCst);
32+
}

src/command.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use std::ffi::CString;
2+
use std::os::raw::c_int;
3+
4+
use crate::raw;
5+
use crate::error::Error;
6+
use crate::context::Context;
7+
use crate::parse_args;
8+
9+
10+
/*
11+
pub struct Command<F> where F: Fn(&Context, Vec<String>) -> RedisResult {
12+
pub name: &'static str,
13+
pub handler: F,
14+
pub flags: &'static str,
15+
}
16+
*/
17+
18+
type CommandFuncPtr = extern "C" fn(
19+
*mut raw::RedisModuleCtx,
20+
*mut *mut raw::RedisModuleString,
21+
c_int,
22+
) -> c_int;
23+
24+
25+
pub trait CommandOld {
26+
// Should return the name of the command to be registered.
27+
fn name() -> &'static str;
28+
29+
fn external_command() -> CommandFuncPtr;
30+
31+
// Should return any flags to be registered with the name as a string
32+
// separated list. See the Redis module API documentation for a complete
33+
// list of the ones that are available.
34+
fn str_flags() -> &'static str;
35+
36+
// Run the command.
37+
fn run(r: Context, args: &[&str]) -> Result<(), Error>;
38+
39+
/// Provides a basic wrapper for a command's implementation that parses
40+
/// arguments to Rust data types and handles the OK/ERR reply back to Redis.
41+
fn execute(
42+
ctx: *mut raw::RedisModuleCtx,
43+
argv: *mut *mut raw::RedisModuleString,
44+
argc: c_int,
45+
) -> raw::Status {
46+
let args = parse_args(argv, argc).unwrap();
47+
let str_args: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
48+
49+
let r = Context::new(ctx);
50+
51+
match Self::run(r, str_args.as_slice()) {
52+
Ok(_) => raw::Status::Ok,
53+
Err(e) => {
54+
let message = format!("Redis error: {}", e.to_string());
55+
let message = CString::new(message).unwrap();
56+
57+
raw::reply_with_error(
58+
ctx,
59+
message.as_ptr(),
60+
);
61+
62+
raw::Status::Err
63+
}
64+
}
65+
}
66+
67+
fn create(ctx: *mut raw::RedisModuleCtx) -> Result<(), &'static str> {
68+
raw::create_command(
69+
ctx,
70+
Self::name(),
71+
Self::external_command(),
72+
Self::str_flags(),
73+
0, 0, 0,
74+
)
75+
}
76+
}
77+

src/context.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
use std::os::raw::{c_long, c_longlong};
2+
use std::ffi::CString;
3+
4+
use crate::raw;
5+
use crate::error::Error;
6+
use crate::{RedisString, LogLevel, Reply};
7+
use crate::key::{RedisKey,RedisKeyWritable};
8+
9+
/// Redis is a structure that's designed to give us a high-level interface to
10+
/// the Redis module API by abstracting away the raw C FFI calls.
11+
pub struct Context {
12+
ctx: *mut raw::RedisModuleCtx,
13+
}
14+
15+
impl Context {
16+
pub fn new(ctx: *mut raw::RedisModuleCtx) -> Self {
17+
Self {ctx}
18+
}
19+
20+
/// Coerces a Redis string as an integer.
21+
///
22+
/// Redis is pretty dumb about data types. It nominally supports strings
23+
/// versus integers, but an integer set in the store will continue to look
24+
/// like a string (i.e. "1234") until some other operation like INCR forces
25+
/// its coercion.
26+
///
27+
/// This method coerces a Redis string that looks like an integer into an
28+
/// integer response. All other types of replies are passed through
29+
/// unmodified.
30+
pub fn coerce_integer(
31+
&self,
32+
reply_res: Result<Reply, Error>,
33+
) -> Result<Reply, Error> {
34+
match reply_res {
35+
Ok(Reply::String(s)) => match s.parse::<i64>() {
36+
Ok(n) => Ok(Reply::Integer(n)),
37+
_ => Ok(Reply::String(s)),
38+
},
39+
_ => reply_res,
40+
}
41+
}
42+
43+
pub fn create_string(&self, s: &str) -> RedisString {
44+
RedisString::create(self.ctx, s)
45+
}
46+
47+
pub fn log(&self, level: LogLevel, message: &str) {
48+
let level = CString::new(format!("{:?}", level).to_lowercase()).unwrap();
49+
let fmt = CString::new(message).unwrap();
50+
raw::log(
51+
self.ctx,
52+
level.as_ptr(),
53+
fmt.as_ptr(),
54+
);
55+
}
56+
57+
pub fn log_debug(&self, message: &str) {
58+
// Note that we log our debug messages as notice level in Redis. This
59+
// is so that they'll show up with default configuration. Our debug
60+
// logging will get compiled out in a release build so this won't
61+
// result in undue noise in production.
62+
self.log(LogLevel::Notice, message);
63+
}
64+
65+
/// Opens a Redis key for read access.
66+
pub fn open_key(&self, key: &str) -> RedisKey {
67+
RedisKey::open(self.ctx, key)
68+
}
69+
70+
/// Opens a Redis key for read and write access.
71+
pub fn open_key_writable(&self, key: &str) -> RedisKeyWritable {
72+
RedisKeyWritable::open(self.ctx, key)
73+
}
74+
75+
/// Tells Redis that we're about to reply with an (Redis) array.
76+
///
77+
/// Used by invoking once with the expected length and then calling any
78+
/// combination of the other reply_* methods exactly that number of times.
79+
pub fn reply_array(&self, len: i64) -> Result<(), Error> {
80+
handle_status(
81+
raw::reply_with_array(self.ctx, len as c_long),
82+
"Could not reply with long",
83+
)
84+
}
85+
86+
pub fn reply_integer(&self, integer: i64) -> Result<(), Error> {
87+
handle_status(
88+
raw::reply_with_long_long(self.ctx, integer as c_longlong),
89+
"Could not reply with longlong",
90+
)
91+
}
92+
93+
pub fn reply_string(&self, message: &str) -> Result<(), Error> {
94+
let redis_str = self.create_string(message);
95+
handle_status(
96+
raw::reply_with_string(self.ctx, redis_str.str_inner),
97+
"Could not reply with string",
98+
)
99+
}
100+
}
101+
102+
fn handle_status(status: raw::Status, message: &str) -> Result<(), Error> {
103+
match status {
104+
raw::Status::Ok => Ok(()),
105+
raw::Status::Err => Err(error!(message)),
106+
}
107+
}

0 commit comments

Comments
 (0)