-
-
Save rightfold/00cb9558f0a7a3c483fd4b86a980eadb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::ffi::CString; | |
use std::io; | |
use std::slice; | |
use libc::{c_char, c_int, c_longlong, c_void, size_t}; | |
pub struct Redis(*mut c_void); | |
impl Redis { | |
pub fn connect(host: &str, port: u16) -> io::Result<Self> { | |
let c_host = try!(CString::new(host)); | |
let c_redis = unsafe { redis_connect(c_host.into_raw(), port as c_int) }; | |
if c_redis.is_null() { | |
Err(io::Error::new(io::ErrorKind::Other, "redis")) | |
} else { | |
Ok(Redis(c_redis)) | |
} | |
} | |
pub fn command(&mut self, parts: &[&[u8]]) -> io::Result<ReplyBox> { | |
let c_parts: Vec<*const c_char> = | |
parts.iter() | |
.map(|p| p.as_ptr() as *const c_char) | |
.collect(); | |
let c_part_lens: Vec<size_t> = | |
parts.iter() | |
.map(|p| p.len()) | |
.collect(); | |
let c_reply = unsafe { | |
redisCommandArgv( | |
self.0, | |
parts.len() as c_int, | |
c_parts.as_ptr(), | |
c_part_lens.as_ptr() | |
) | |
}; | |
if c_reply.is_null() { | |
Err(io::Error::new(io::ErrorKind::Other, "redis")) | |
} else { | |
Ok(ReplyBox(ReplyRef(c_reply))) | |
} | |
} | |
} | |
impl Drop for Redis { | |
fn drop(&mut self) { | |
unsafe { redisFree(self.0); } | |
} | |
} | |
pub struct ReplyRef(*mut c_void); | |
impl ReplyRef { | |
pub fn deref<'a>(&'a self) -> Reply<'a> { | |
unsafe { | |
match redis_reply_type(self.0) { | |
0 => { | |
let slice = slice::from_raw_parts( | |
redis_reply_str(self.0) as *const u8, | |
redis_reply_len(self.0) | |
); | |
Reply::Status(slice) | |
}, | |
1 => { | |
let slice = slice::from_raw_parts( | |
redis_reply_str(self.0) as *const u8, | |
redis_reply_len(self.0) | |
); | |
Reply::Error(slice) | |
}, | |
2 => Reply::Integer(redis_reply_integer(self.0)), | |
3 => Reply::Nil, | |
4 => { | |
let slice = slice::from_raw_parts( | |
redis_reply_str(self.0) as *const u8, | |
redis_reply_len(self.0) | |
); | |
Reply::String(slice) | |
}, | |
5 => { | |
let slice = slice::from_raw_parts( | |
redis_reply_element(self.0) as *const ReplyRef, | |
redis_reply_elements(self.0) | |
); | |
Reply::Array(slice) | |
}, | |
_ => Reply::Unknown, | |
} | |
} | |
} | |
} | |
pub struct ReplyBox(ReplyRef); | |
impl ReplyBox { | |
pub fn deref<'a>(&'a self) -> Reply<'a> { | |
self.0.deref() | |
} | |
} | |
impl Drop for ReplyBox { | |
fn drop(&mut self) { | |
unsafe { freeReplyObject((self.0).0); } | |
} | |
} | |
pub enum Reply<'a> { | |
Status(&'a [u8]), | |
Error(&'a [u8]), | |
Integer(i64), | |
Nil, | |
String(&'a [u8]), | |
Array(&'a [ReplyRef]), | |
Unknown, | |
} | |
extern { | |
fn redis_connect(ip: *const c_char, port: c_int) -> *mut c_void; | |
fn redisFree(redis: *mut c_void); | |
fn redisCommandArgv( | |
redis: *mut c_void, | |
argc: c_int, | |
argv: *const *const c_char, | |
argvlen: *const size_t | |
) -> *mut c_void; | |
fn redis_reply_type(reply: *mut c_void) -> c_int; | |
fn redis_reply_integer(reply: *mut c_void) -> c_longlong; | |
fn redis_reply_len(reply: *mut c_void) -> size_t; | |
fn redis_reply_str(reply: *mut c_void) -> *const c_char; | |
fn redis_reply_elements(reply: *mut c_void) -> size_t; | |
fn redis_reply_element(reply: *mut c_void) -> *const *mut c_void; | |
fn freeReplyObject(reply: *mut c_void); | |
} | |
#[cfg(test)] | |
mod test { | |
use super::*; | |
#[test] | |
fn test_connect() { | |
Redis::connect("127.0.0.1", 6379).unwrap(); | |
} | |
#[test] | |
fn test_command() { | |
let mut redis = Redis::connect("127.0.0.1", 6379).unwrap(); | |
let reply = redis.command(&[b"EVAL", b"return 42", b"0"]).unwrap(); | |
assert!(match reply.deref() { | |
Reply::Integer(42) => true, | |
_ => false, | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment