1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use casbin::Cache;
use redis::{Client, Connection, IntoConnectionInfo, RedisResult};
use serde::{de::DeserializeOwned, Serialize};
use std::{borrow::Cow, hash::Hash, marker::PhantomData};
const CACHE_PREFIX: &str = "casbin";
pub struct RedisCache<K, V>
where
K: Eq + Hash + Send + Sync + Serialize + 'static,
V: Send + Sync + Clone + Serialize + DeserializeOwned + 'static,
{
conn: Connection,
_marker: PhantomData<(K, V)>,
}
impl<K, V> RedisCache<K, V>
where
K: Eq + Hash + Send + Sync + Serialize + 'static,
V: Send + Sync + Clone + Serialize + DeserializeOwned + 'static,
{
pub fn new<U: IntoConnectionInfo>(url: U) -> RedisResult<RedisCache<K, V>> {
let client = Client::open(url)?;
let conn = client.get_connection()?;
Ok(RedisCache {
conn,
_marker: PhantomData,
})
}
}
impl<K, V> Cache<K, V> for RedisCache<K, V>
where
K: Eq + Hash + Send + Sync + Serialize + 'static,
V: Send + Sync + Clone + Serialize + DeserializeOwned + 'static,
{
fn set_capacity(&mut self, _c: usize) {}
fn get(&mut self, k: &K) -> Option<Cow<'_, V>> {
if let Ok(ser_key) = serde_json::to_string(&k) {
let cache_key = format!("{}::{}", CACHE_PREFIX, ser_key);
if let Ok(res) = redis::cmd("GET")
.arg(cache_key)
.query::<String>(&mut self.conn)
{
return serde_json::from_str(&res).ok().map(|x| Cow::Owned(x));
}
}
None
}
fn has(&mut self, k: &K) -> bool {
self.get(k).is_some()
}
fn set(&mut self, k: K, v: V) {
if let (Ok(ser_key), Ok(ser_val)) = (serde_json::to_string(&k), serde_json::to_string(&v)) {
let cache_key = format!("{}::{}", CACHE_PREFIX, ser_key);
let _ = redis::cmd("SET")
.arg(cache_key)
.arg(ser_val)
.query::<String>(&mut self.conn);
}
}
fn clear(&mut self) {
let cache_key = format!("{}::*", CACHE_PREFIX);
let script = format!(
r#"
EVAL "for i, name in ipairs(redis.call('KEYS', '{}')) do redis.call('del', name, 0); end" 0
"#,
cache_key
);
let script = redis::Script::new(&script);
let _ = script
.arg(1)
.arg(2)
.invoke::<Option<String>>(&mut self.conn);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_set_and_get() {
let mut cache = RedisCache::new("redis://localhost:6379").unwrap();
cache.set(vec!["alice", "/data1", "read"], false);
assert!(cache.has(&vec!["alice", "/data1", "read"]));
assert!(cache.get(&vec!["alice", "/data1", "read"]) == Some(Cow::Borrowed(&false)));
}
#[test]
fn test_clear() {
let mut cache = RedisCache::new("redis://localhost:6379").unwrap();
cache.set(vec!["alice", "/data1", "read"], false);
cache.clear();
assert!(cache.get(&vec!["alice", "/data1", "read"]) == None);
}
}