use sha1::Sha1;
use cmd::cmd;
use connection::ConnectionLike;
use types::{ErrorKind, FromRedisValue, RedisResult, ToRedisArgs};
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<'b, T: ToRedisArgs>(&'b mut self, arg: T) -> &'b mut ScriptInvocation<'a>
where
'a: 'b,
{
arg.write_redis_args(&mut self.args);
self
}
#[inline]
pub fn key<'b, T: ToRedisArgs>(&'b mut self, key: T) -> &'b mut ScriptInvocation<'a>
where
'a: 'b,
{
key.write_redis_args(&mut self.keys);
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 _: () = cmd("SCRIPT")
.arg("LOAD")
.arg(self.script.code.as_bytes())
.query(con)?;
} else {
fail!(err);
}
}
}
}
}
}