Skip to content

Commit 232abb8

Browse files
committed
Use derive(Primite) for Redis module enums
1 parent 87276a3 commit 232abb8

File tree

5 files changed

+143
-80
lines changed

5 files changed

+143
-80
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ crate-type = ["cdylib"]
2020
bitflags = "1.0"
2121
libc = "0.2"
2222
time = "0.1"
23+
enum-primitive-derive = "^0.1"
24+
num-traits = "^0.1"
2325

2426
[build-dependencies]
2527
bindgen = "0.47"

examples/data_type.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ use redismodule::types::RedisModuleType;
1414
const MODULE_NAME: &str = "alloc";
1515
const MODULE_VERSION: c_int = 1;
1616

17-
// TODO: Can we use a safe smart pointer instead of an unsafe mutable static variable?
18-
static mut MY_TYPE: RedisModuleType = RedisModuleType::new();
17+
static MY_TYPE: RedisModuleType = RedisModuleType::new();
1918

2019
struct AllocSetCommand;
2120

@@ -29,6 +28,8 @@ impl Command for AllocSetCommand {
2928
// Run the command.
3029
fn run(r: Redis, args: &[&str]) -> Result<(), Error> {
3130
if args.len() != 3 {
31+
// FIXME: Use RedisModule_WrongArity instead. Return an ArityError here and
32+
// in the low-level implementation call RM_WrongArity.
3233
return Err(Error::generic(format!(
3334
"Usage: {} <key> <size>", Self::name()
3435
).as_str()));
@@ -43,8 +44,13 @@ impl Command for AllocSetCommand {
4344
// 2. Allocate data
4445
// 3. Set the key to the data
4546
// 4. Activate custom allocator and compare Redis memory usage
46-
let data: Vec<u8> = Vec::with_capacity(size as usize);
47-
let k = r.open_key_writable(key);
47+
48+
//let data: Vec<u8> = Vec::with_capacity(size as usize);
49+
50+
let key = r.open_key_writable(key);
51+
52+
//key.check_type(MY_TYPE)?;
53+
key.write(size.to_string().as_str())?;
4854

4955
/*
5056
raw::RedisModule_ModuleTypeSetValue.unwrap()(
@@ -113,8 +119,7 @@ pub extern "C" fn AllocDelCommand_Redis(
113119
fn module_on_load(ctx: *mut raw::RedisModuleCtx) -> Result<(), &'static str> {
114120
module_init(ctx, MODULE_NAME, MODULE_VERSION)?;
115121

116-
// FIXME: Make this safe (use a smart pointer?)
117-
unsafe { MY_TYPE.create_data_type(ctx, "mytype123") }?;
122+
MY_TYPE.create_data_type(ctx, "mytype123")?;
118123

119124
AllocSetCommand::create(ctx)?;
120125
AllocDelCommand::create(ctx)?;

src/lib.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,22 @@ extern crate time;
2727

2828
use libc::size_t;
2929

30+
#[macro_use]
31+
extern crate enum_primitive_derive;
32+
extern crate num_traits;
33+
3034
#[macro_use]
3135
mod macros;
3236

3337
pub mod error;
3438

35-
use crate::error::Error;
36-
37-
////////////////////////////////////////////////////////////
3839
use std::alloc::{System, GlobalAlloc, Layout};
3940
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering::SeqCst};
4041
use std::ffi::c_void;
4142

43+
use crate::error::Error;
44+
use crate::types::RedisModuleType;
45+
4246
struct RedisAlloc;
4347

4448
static USE_REDIS_ALLOC: AtomicBool = ATOMIC_BOOL_INIT;
@@ -357,6 +361,20 @@ impl RedisKeyWritable {
357361
raw::Status::Err => Err(error!("Error while setting key")),
358362
}
359363
}
364+
365+
pub fn check_type(&self, redis_type: RedisModuleType) -> Result<(), Error> {
366+
/*
367+
int type = RedisModule_KeyType(key);
368+
if (type != REDISMODULE_KEYTYPE_EMPTY &&
369+
RedisModule_ModuleTypeGetType(key) != HelloType)
370+
{
371+
return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
372+
}
373+
*/
374+
//raw::key_type(key)
375+
376+
Ok(())
377+
}
360378
}
361379

362380
impl Drop for RedisKeyWritable {

src/raw.rs

Lines changed: 90 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ use std::ffi::CString;
66
use std::os::raw::{c_char, c_int, c_long, c_longlong};
77

88
extern crate libc;
9+
extern crate enum_primitive_derive;
10+
extern crate num_traits;
11+
12+
use num_traits::{FromPrimitive, ToPrimitive};
913

1014
use libc::size_t;
1115

@@ -18,64 +22,53 @@ bitflags! {
1822
}
1923
}
2024

25+
#[derive(Primitive)]
26+
pub enum KeyType {
27+
Empty = REDISMODULE_KEYTYPE_EMPTY as isize,
28+
String = REDISMODULE_KEYTYPE_STRING as isize,
29+
List = REDISMODULE_KEYTYPE_LIST as isize,
30+
Hash = REDISMODULE_KEYTYPE_HASH as isize,
31+
Set = REDISMODULE_KEYTYPE_SET as isize,
32+
ZSet = REDISMODULE_KEYTYPE_ZSET as isize,
33+
Module = REDISMODULE_KEYTYPE_MODULE as isize,
34+
}
35+
36+
impl From<c_int> for KeyType {
37+
fn from(v: c_int) -> Self { KeyType::from_i32(v).unwrap() }
38+
}
2139

22-
#[derive(Debug, PartialEq)]
23-
#[repr(i32)]
40+
// This hack is required since derive(Primitive) requires all values to have the same type,
41+
// and REDISMODULE_REPLY_UNKNOWN is i32 while the rest are u32.
42+
// Casting to isize inside the enum definition breaks the derive(Primitive) macro.
43+
const REDISMODULE_REPLY_UNKNOWN_ISIZE: isize = REDISMODULE_REPLY_UNKNOWN as isize;
44+
const REDISMODULE_REPLY_STRING_ISIZE: isize = REDISMODULE_REPLY_STRING as isize;
45+
const REDISMODULE_REPLY_ERROR_ISIZE: isize = REDISMODULE_REPLY_ERROR as isize;
46+
const REDISMODULE_REPLY_INTEGER_ISIZE: isize = REDISMODULE_REPLY_INTEGER as isize;
47+
const REDISMODULE_REPLY_ARRAY_ISIZE: isize = REDISMODULE_REPLY_ARRAY as isize;
48+
const REDISMODULE_REPLY_NULL_ISIZE: isize = REDISMODULE_REPLY_NULL as isize;
49+
50+
#[derive(Primitive, Debug, PartialEq)]
2451
pub enum ReplyType {
25-
Unknown = REDISMODULE_REPLY_UNKNOWN as i32,
26-
String = REDISMODULE_REPLY_STRING as i32,
27-
Error = REDISMODULE_REPLY_ERROR as i32,
28-
Integer = REDISMODULE_REPLY_INTEGER as i32,
29-
Array = REDISMODULE_REPLY_ARRAY as i32,
30-
Nil = REDISMODULE_REPLY_NULL as i32,
31-
}
32-
33-
// Tools that can automate this:
34-
// https://crates.io/crates/enum_primitive
35-
// https://crates.io/crates/num_enum
36-
// https://crates.io/crates/enum-primitive-derive
37-
38-
impl From<i32> for ReplyType {
39-
fn from(v: i32) -> Self {
40-
use crate::raw::ReplyType::*;
41-
42-
// TODO: Is there a way to do this with a `match`? We have different types of constants here.
43-
if v == Unknown as i32 {
44-
Unknown
45-
} else if v == String as i32 {
46-
String
47-
} else if v == Error as i32 {
48-
Error
49-
} else if v == Integer as i32 {
50-
Integer
51-
} else if v == Array as i32 {
52-
Array
53-
} else if v == Nil as i32 {
54-
Nil
55-
} else {
56-
panic!("Received unexpected reply type from Redis: {}", v)
57-
}
58-
}
52+
Unknown = REDISMODULE_REPLY_UNKNOWN_ISIZE,
53+
String = REDISMODULE_REPLY_STRING_ISIZE,
54+
Error = REDISMODULE_REPLY_ERROR_ISIZE,
55+
Integer = REDISMODULE_REPLY_INTEGER_ISIZE,
56+
Array = REDISMODULE_REPLY_ARRAY_ISIZE,
57+
Nil = REDISMODULE_REPLY_NULL_ISIZE,
5958
}
6059

61-
#[derive(Clone, Copy, Debug, PartialEq)]
62-
#[repr(i32)]
60+
impl From<c_int> for ReplyType {
61+
fn from(v: c_int) -> Self { ReplyType::from_i32(v).unwrap() }
62+
}
63+
64+
#[derive(Primitive, Debug, PartialEq)]
6365
pub enum Status {
64-
Ok = REDISMODULE_OK as i32,
65-
Err = REDISMODULE_ERR as i32,
66+
Ok = REDISMODULE_OK as isize,
67+
Err = REDISMODULE_ERR as isize,
6668
}
6769

6870
impl From<c_int> for Status {
69-
fn from(v: c_int) -> Self {
70-
// TODO: Is there a way to do this with a `match`? We have different types of constants here.
71-
if v == REDISMODULE_OK as c_int {
72-
Status::Ok
73-
} else if v == REDISMODULE_ERR as c_int {
74-
Status::Err
75-
} else {
76-
panic!("Received unexpected status from Redis: {}", v)
77-
}
78-
}
71+
fn from(v: c_int) -> Self { Status::from_i32(v).unwrap() }
7972
}
8073

8174
impl From<Status> for c_int {
@@ -93,6 +86,47 @@ impl From<Status> for Result<(), &str> {
9386
}
9487
}
9588

89+
90+
#[derive(Debug)]
91+
pub enum CommandFlag {
92+
Write,
93+
Readonly,
94+
Denyoom,
95+
Admin,
96+
Pubsub,
97+
Noscript,
98+
Random,
99+
SortForScript,
100+
Loading,
101+
Stale,
102+
SkipMonitor,
103+
Asking,
104+
Fast,
105+
Movablekeys,
106+
}
107+
108+
109+
fn command_flag_repr(flag: &CommandFlag) -> &'static str {
110+
use crate::raw::CommandFlag::*;
111+
match flag {
112+
Write => "write",
113+
Readonly => "readonly",
114+
Denyoom => "denyoom",
115+
Admin => "admin",
116+
Pubsub => "pubsub",
117+
Noscript => "noscript",
118+
Random => "random",
119+
SortForScript => "sort_for_script",
120+
Loading => "loading",
121+
Stale => "stale",
122+
SkipMonitor => "skip_monitor",
123+
Asking => "asking",
124+
Fast => "fast",
125+
Movablekeys => "movablekeys",
126+
}
127+
}
128+
129+
96130
pub type CommandFunc = extern "C" fn(
97131
ctx: *mut RedisModuleCtx,
98132
argv: *mut *mut RedisModuleString,
@@ -120,8 +154,8 @@ pub fn create_command(
120154
firstkey,
121155
lastkey,
122156
keystep,
123-
).into()
124-
};
157+
)
158+
}.into();
125159

126160
status.into()
127161
}
@@ -160,9 +194,7 @@ extern "C" {
160194

161195
///////////////////////////////////////////////////////////////
162196

163-
164197
// Helper functions for the raw bindings.
165-
// Taken from redis-cell.
166198

167199
pub fn call_reply_type(reply: *mut RedisModuleCallReply) -> ReplyType {
168200
unsafe {
@@ -270,3 +302,7 @@ pub fn string_ptr_len(str: *mut RedisModuleString, len: *mut size_t) -> *const c
270302
pub fn string_set(key: *mut RedisModuleKey, str: *mut RedisModuleString) -> Status {
271303
unsafe { RedisModule_StringSet.unwrap()(key, str).into() }
272304
}
305+
306+
pub fn key_type(key: *mut RedisModuleKey) -> KeyType {
307+
unsafe { RedisModule_KeyType.unwrap()(key) }.into()
308+
}

src/types.rs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,30 @@
1-
use core::ptr;
1+
use std::ptr;
2+
use std::cell::RefCell;
23
use std::ffi::CString;
34
use std::os::raw::{c_int, c_void};
45

56
use crate::raw;
67

78
pub struct RedisModuleType {
8-
raw_type: *mut raw::RedisModuleType,
9+
raw_type: RefCell<*mut raw::RedisModuleType>,
910
}
1011

1112
// We want to be able to create static instances of this type,
1213
// which means we need to implement Sync.
1314
unsafe impl Sync for RedisModuleType {}
1415

15-
pub fn redis_log(
16-
ctx: *mut raw::RedisModuleCtx,
17-
msg: &str,
18-
) {
19-
let level = CString::new("notice").unwrap(); // FIXME reuse this
20-
let msg = CString::new(msg).unwrap();
21-
unsafe {
22-
raw::RedisModule_Log.unwrap()(ctx, level.as_ptr(), msg.as_ptr());
23-
}
24-
}
25-
2616
impl RedisModuleType {
2717
pub const fn new() -> Self {
2818
RedisModuleType {
29-
raw_type: ptr::null_mut(),
19+
raw_type: RefCell::new(ptr::null_mut()),
3020
}
3121
}
3222

3323
pub fn create_data_type(
34-
&mut self,
24+
&self,
3525
ctx: *mut raw::RedisModuleCtx,
3626
name: &str,
3727
) -> Result<(), &str> {
38-
3928
if name.len() != 9 {
4029
let msg = "Redis requires the length of native type names to be exactly 9 characters";
4130
redis_log(ctx, format!("{}, name is: '{}'", msg, name).as_str());
@@ -71,7 +60,9 @@ impl RedisModuleType {
7160
return Err("Error: created data type is null");
7261
}
7362

74-
self.raw_type = redis_type;
63+
*self.raw_type.borrow_mut() = redis_type;
64+
65+
redis_log(ctx, format!("Created new data type '{}'", name).as_str());
7566

7667
Ok(())
7768
}
@@ -116,3 +107,14 @@ pub unsafe extern "C" fn MyTypeFree(
116107
// eprintln!("MyTypeFree");
117108
}
118109

110+
pub fn redis_log(
111+
ctx: *mut raw::RedisModuleCtx,
112+
msg: &str,
113+
) {
114+
let level = CString::new("notice").unwrap(); // FIXME reuse this
115+
let msg = CString::new(msg).unwrap();
116+
unsafe {
117+
raw::RedisModule_Log.unwrap()(ctx, level.as_ptr(), msg.as_ptr());
118+
}
119+
}
120+

0 commit comments

Comments
 (0)