use std::{collections::HashMap, time::Duration}; use crate::{cluster_wrapper::Cluster, internal}; use proxy_wasm::{hostcalls::RedisCallbackFn, types::Status}; use redis::{Cmd, ToRedisArgs, Value}; pub type RedisValueCallbackFn = dyn FnOnce(&Result, usize, u32); fn gen_callback(call_fn: Box) -> Box { Box::new(move |token_id, status, response_size| { let res = match internal::get_redis_call_response(0, response_size) { Some(data) => match redis::parse_redis_value(&data) { Ok(v) => Ok(v), Err(e) => Err(e.to_string()), }, None => Err("response data not found".to_string()), }; call_fn(&res, status, token_id); }) } pub struct RedisClientBuilder { upstream: String, username: Option, password: Option, timeout: Duration, database: Option, } impl RedisClientBuilder { pub fn new(cluster: &dyn Cluster, timeout: Duration) -> Self { RedisClientBuilder { upstream: cluster.cluster_name(), username: None, password: None, timeout, database: None, } } pub fn username>(mut self, username: Option) -> Self { self.username = username.map(|u| u.as_ref().to_string()); self } pub fn password>(mut self, password: Option) -> Self { self.password = password.map(|p| p.as_ref().to_string()); self } pub fn database(mut self, database: Option) -> Self { self.database = database; self } pub fn build(self) -> RedisClient { let upstream = if let Some(db) = self.database { if db != 0 { format!("{}?db={}", self.upstream, db) } else { self.upstream } } else { self.upstream }; RedisClient { upstream, username: self.username, password: self.password, timeout: self.timeout, } } } pub struct RedisClientConfig { upstream: String, username: Option, password: Option, timeout: Duration, database: Option, } impl RedisClientConfig { pub fn new(cluster: &dyn Cluster, timeout: Duration) -> Self { RedisClientConfig { upstream: cluster.cluster_name(), username: None, password: None, timeout, database: None, } } pub fn username>(&mut self, username: Option) -> &Self { self.username = username.map(|u| u.as_ref().to_string()); self } pub fn password>(&mut self, password: Option) -> &Self { self.password = password.map(|p| p.as_ref().to_string()); self } pub fn database(&mut self, database: Option) -> &Self { self.database = database; self } } #[derive(Debug, Clone)] pub struct RedisClient { upstream: String, username: Option, password: Option, timeout: Duration, } impl RedisClient { pub fn new(config: &RedisClientConfig) -> Self { let upstream = if let Some(db) = config.database { if db != 0 { format!("{}?db={}", config.upstream, db) } else { config.upstream.clone() } } else { config.upstream.clone() }; RedisClient { upstream, username: config.username.clone(), password: config.password.clone(), timeout: config.timeout, } } pub fn init(&self) -> Result<(), Status> { internal::redis_init( &self.upstream, self.username.as_ref().map(|u| u.as_bytes()), self.password.as_ref().map(|p| p.as_bytes()), self.timeout, ) } fn call(&self, query: &[u8], call_fn: Box) -> Result { internal::dispatch_redis_call(&self.upstream, query, gen_callback(call_fn)) } pub fn command(&self, cmd: &Cmd, call_fn: Box) -> Result { self.call(&cmd.get_packed_command(), call_fn) } pub fn eval( &self, script: &str, numkeys: i32, keys: Vec<&str>, args: Vec, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("eval"); cmd.arg(script).arg(numkeys); for key in keys { cmd.arg(key); } for arg in args { cmd.arg(arg); } self.command(&cmd, call_fn) } // Key pub fn del(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("del"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn exists(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("exists"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn expire( &self, key: &str, ttl: i32, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("expire"); cmd.arg(key).arg(ttl); self.command(&cmd, call_fn) } pub fn persist(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("persist"); cmd.arg(key); self.command(&cmd, call_fn) } // String pub fn get(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("get"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn set( &self, key: &str, value: T, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("set"); cmd.arg(key).arg(value); self.command(&cmd, call_fn) } pub fn setex( &self, key: &str, value: T, ttl: i32, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("setex"); cmd.arg(key).arg(ttl).arg(value); self.command(&cmd, call_fn) } pub fn mget(&self, keys: Vec<&str>, call_fn: Box) -> Result { let mut cmd = redis::cmd("mget"); for key in keys { cmd.arg(key); } self.command(&cmd, call_fn) } pub fn mset( &self, kv_map: HashMap<&str, T>, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("mset"); for (k, v) in kv_map { cmd.arg(k).arg(v); } self.command(&cmd, call_fn) } pub fn incr(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("incr"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn decr(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("decr"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn incrby( &self, key: &str, delta: i32, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("incrby"); cmd.arg(key).arg(delta); self.command(&cmd, call_fn) } pub fn decrby( &self, key: &str, delta: i32, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("decrby"); cmd.arg(key).arg(delta); self.command(&cmd, call_fn) } // List pub fn llen(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("llen"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn rpush( &self, key: &str, vals: Vec, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("rpush"); cmd.arg(key); for val in vals { cmd.arg(val); } self.command(&cmd, call_fn) } pub fn rpop(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("rpop"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn lpush( &self, key: &str, vals: Vec, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("lpush"); cmd.arg(key); for val in vals { cmd.arg(val); } self.command(&cmd, call_fn) } pub fn lpop(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("lpop"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn lindex( &self, key: &str, index: i32, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("lindex"); cmd.arg(key).arg(index); self.command(&cmd, call_fn) } pub fn lrange( &self, key: &str, start: i32, stop: i32, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("lrange"); cmd.arg(key).arg(start).arg(stop); self.command(&cmd, call_fn) } pub fn lrem( &self, key: &str, count: i32, value: T, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("lrem"); cmd.arg(key).arg(count).arg(value); self.command(&cmd, call_fn) } pub fn linsert_before( &self, key: &str, pivot: T, value: T, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("linsert"); cmd.arg(key).arg("before").arg(pivot).arg(value); self.command(&cmd, call_fn) } pub fn linsert_after( &self, key: &str, pivot: T, value: T, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("linsert"); cmd.arg(key).arg("after").arg(pivot).arg(value); self.command(&cmd, call_fn) } // Hash pub fn hexists( &self, key: &str, field: &str, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("hexists"); cmd.arg(key).arg(field); self.command(&cmd, call_fn) } pub fn hdel( &self, key: &str, fields: Vec<&str>, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("hdel"); cmd.arg(key); for field in fields { cmd.arg(field); } self.command(&cmd, call_fn) } pub fn hlen(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("hlen"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn hget( &self, key: &str, field: &str, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("hget"); cmd.arg(key).arg(field); self.command(&cmd, call_fn) } pub fn hset( &self, key: &str, field: &str, value: T, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("hset"); cmd.arg(key).arg(field).arg(value); self.command(&cmd, call_fn) } pub fn hmget( &self, key: &str, fields: Vec<&str>, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("hmget"); cmd.arg(key); for field in fields { cmd.arg(field); } self.command(&cmd, call_fn) } pub fn hmset( &self, key: &str, kv_map: HashMap<&str, T>, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("hmset"); cmd.arg(key); for (k, v) in kv_map { cmd.arg(k).arg(v); } self.command(&cmd, call_fn) } pub fn hkeys(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("hkeys"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn hvals(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("hvals"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn hgetall(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("hgetall"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn hincrby( &self, key: &str, field: &str, delta: i32, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("hincrby"); cmd.arg(key).arg(field).arg(delta); self.command(&cmd, call_fn) } pub fn hincrbyfloat( &self, key: &str, field: &str, delta: f64, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("hincrbyfloat"); cmd.arg(key).arg(field).arg(delta); self.command(&cmd, call_fn) } // Set pub fn scard(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("scard"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn sadd( &self, key: &str, vals: Vec, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("sadd"); cmd.arg(key); for val in vals { cmd.arg(val); } self.command(&cmd, call_fn) } pub fn srem( &self, key: &str, vals: Vec, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("srem"); cmd.arg(key); for val in vals { cmd.arg(val); } self.command(&cmd, call_fn) } pub fn sismember( &self, key: &str, value: T, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("sismember"); cmd.arg(key).arg(value); self.command(&cmd, call_fn) } pub fn smembers(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("smembers"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn sdiff( &self, key1: &str, key2: &str, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("sdiff"); cmd.arg(key1).arg(key2); self.command(&cmd, call_fn) } pub fn sdiffstore( &self, destination: &str, key1: &str, key2: &str, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("sdiffstore"); cmd.arg(destination).arg(key1).arg(key2); self.command(&cmd, call_fn) } pub fn sinter( &self, key1: &str, key2: &str, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("sinter"); cmd.arg(key1).arg(key2); self.command(&cmd, call_fn) } pub fn sinterstore( &self, destination: &str, key1: &str, key2: &str, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("sinterstore"); cmd.arg(destination).arg(key1).arg(key2); self.command(&cmd, call_fn) } pub fn sunion( &self, key1: &str, key2: &str, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("sunion"); cmd.arg(key1).arg(key2); self.command(&cmd, call_fn) } pub fn sunion_store( &self, destination: &str, key1: &str, key2: &str, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("sunionstore"); cmd.arg(destination).arg(key1).arg(key2); self.command(&cmd, call_fn) } // Sorted Set pub fn zcard(&self, key: &str, call_fn: Box) -> Result { let mut cmd = redis::cmd("zcard"); cmd.arg(key); self.command(&cmd, call_fn) } pub fn zadd( &self, key: &str, ms_map: HashMap<&str, T>, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("zadd"); cmd.arg(key); for (m, s) in ms_map { cmd.arg(s).arg(m); } self.command(&cmd, call_fn) } pub fn zcount( &self, key: &str, min: T, max: T, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("zcount"); cmd.arg(key).arg(min).arg(max); self.command(&cmd, call_fn) } pub fn zincrby( &self, key: &str, member: &str, delta: T, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("zincrby"); cmd.arg(key).arg(delta).arg(member); self.command(&cmd, call_fn) } pub fn zscore( &self, key: &str, member: &str, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("zscore"); cmd.arg(key).arg(member); self.command(&cmd, call_fn) } pub fn zrank( &self, key: &str, member: &str, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("zrank"); cmd.arg(key).arg(member); self.command(&cmd, call_fn) } pub fn zrev_rank( &self, key: &str, member: &str, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("zrevrank"); cmd.arg(key).arg(member); self.command(&cmd, call_fn) } pub fn zrem( &self, key: &str, members: Vec<&str>, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("zrem"); cmd.arg(key); for member in members { cmd.arg(member); } self.command(&cmd, call_fn) } pub fn zrange( &self, key: &str, start: i32, stop: i32, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("zrange"); cmd.arg(key).arg(start).arg(stop); self.command(&cmd, call_fn) } pub fn zrevrange( &self, key: &str, start: i32, stop: i32, call_fn: Box, ) -> Result { let mut cmd = redis::cmd("zrevrange"); cmd.arg(key).arg(start).arg(stop); self.command(&cmd, call_fn) } }