use sha1::Sha1;
use cmd::cmd;
use types::{ToRedisArgs, FromRedisValue, RedisResult, ErrorKind};
use connection::ConnectionLike;
pub struct Script {
code: String,
hash: String,
}
impl Script {
pub fn new(code: &str) -> Script {
let mut hash = Sha1::new();
hash.update(code.as_bytes());
Script {
code: code.to_string(),
hash: hash.digest().to_string(),
}
}
pub fn get_hash(&self) -> &str {
&self.hash
}
#[inline]
pub fn key<T: ToRedisArgs>(&self, key: T) -> ScriptInvocation {
ScriptInvocation {
script: self,
args: vec![],
keys: key.to_redis_args(),
}
}
#[inline]
pub fn arg<T: ToRedisArgs>(&self, arg: T) -> ScriptInvocation {
ScriptInvocation {
script: self,
args: arg.to_redis_args(),
keys: vec![],
}
}
#[inline]
pub fn prepare_invoke(&self) -> ScriptInvocation {
ScriptInvocation { script: self, args: vec![], keys: vec![] }
}
#[inline]
pub fn invoke<T: FromRedisValue>(&self, con: &ConnectionLike) -> RedisResult<T> {
ScriptInvocation {
script: self,
args: vec![],
keys: vec![],
}.invoke(con)
}
}
pub struct ScriptInvocation<'a> {
script: &'a Script,
args: Vec<Vec<u8>>,
keys: Vec<Vec<u8>>,
}
impl<'a> ScriptInvocation<'a> {
#[inline]
pub fn arg<T: ToRedisArgs>(&'a mut self, arg: T) -> &'a mut ScriptInvocation {
self.args.extend(arg.to_redis_args().into_iter());
self
}
#[inline]
pub fn key<T: ToRedisArgs>(&'a mut self, key: T) -> &'a mut ScriptInvocation {
self.keys.extend(key.to_redis_args().into_iter());
self
}
#[inline]
pub fn invoke<T: FromRedisValue>(&self, con: &ConnectionLike) -> RedisResult<T> {
loop {
match cmd("EVALSHA")
.arg(self.script.hash.as_bytes())
.arg(self.keys.len())
.arg(&*self.keys)
.arg(&*self.args).query(con) {
Ok(val) => { return Ok(val); }
Err(err) => {
if err.kind() == ErrorKind::NoScriptError {
let _ : () = try!(cmd("SCRIPT")
.arg("LOAD")
.arg(self.script.code.as_bytes())
.query(con));
} else {
fail!(err);
}
}
}
}
}
}