cache_service/
kv_cache.rs

1use redis::{Client, Commands, Connection, RedisError};
2
3use crate::SetPayload;
4
5pub struct KvCache {
6    con: Connection,
7}
8
9#[derive(Debug)]
10pub enum KvError {
11    CommandFailed(RedisError),
12    ConnectionNotEstablished,
13}
14
15impl From<RedisError> for KvError {
16    fn from(err: RedisError) -> Self {
17        KvError::CommandFailed(err)
18    }
19}
20
21impl KvCache {
22    pub fn new(url: &str) -> Result<KvCache, KvError> {
23        let client = Client::open(url).map_err(|_| KvError::ConnectionNotEstablished)?;
24        let con = client
25            .get_connection()
26            .map_err(|_| KvError::ConnectionNotEstablished)?;
27        Ok(KvCache { con })
28    }
29
30    pub fn set(&mut self, payload: SetPayload) -> Result<String, KvError> {
31        let res: String = self.con.get(payload.key).unwrap_or_else(|_| "".to_string());
32        if res.is_empty() {
33            self.con
34                .set_ex(payload.key, payload.value, payload.ttl)
35                .map_err(KvError::CommandFailed)?;
36            return Ok(payload.value.to_string());
37        }
38        Ok(res)
39    }
40
41    pub fn get(&mut self, key: &str) -> Option<String> {
42        let res: String = self.con.get(key).unwrap_or_else(|_| "".to_string());
43        if res.is_empty() {
44            return None;
45        }
46        Some(res)
47    }
48
49    pub fn unset(&mut self, key: &str) -> Result<(), KvError> {
50        self.con.del(key).map_err(KvError::CommandFailed)?;
51        Ok(())
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    impl KvCache {
60        fn set_raw(&mut self, key: &str, value: &str) -> Result<(), KvError> {
61            self.con.set(key, value).map_err(KvError::CommandFailed)?;
62            Ok(())
63        }
64    }
65
66    fn teardown(key: &str) {
67        let mut cache = KvCache::new("redis://127.0.0.1:6379")
68            .expect("Should establish connection with no problem");
69        cache.unset(key).expect("Should not fail");
70    }
71
72    #[test]
73    fn it_should_return_empty_value() {
74        let key = "foo1";
75        let mut cache = KvCache::new("redis://127.0.0.1:6379")
76            .expect("Should establish connection with no problem");
77        let res = cache
78            .set(SetPayload {
79                key,
80                value: "",
81                ttl: 1,
82            })
83            .expect("Should not fail");
84        teardown(key);
85        assert_eq!(res, "");
86    }
87
88    #[test]
89    fn it_should_return_value() {
90        let key = "foo2";
91        let mut cache = KvCache::new("redis://127.0.0.1:6379")
92            .expect("Should establish connection with no problem");
93        cache.set_raw(key, "42").expect("Should not fail");
94        let res = cache
95            .set(SetPayload {
96                key,
97                value: "42",
98                ttl: 1,
99            })
100            .expect("Should not fail");
101        teardown(key);
102        assert_eq!(res, "42");
103    }
104
105    #[test]
106    fn it_should_return_error_for_wrong_connection() {
107        let cache = KvCache::new("");
108        assert!(matches!(cache, Err(KvError::ConnectionNotEstablished)));
109    }
110
111    #[test]
112    fn it_should_cache_value_for_ttl() {
113        let key = "foo3";
114        let mut cache = KvCache::new("redis://127.0.0.1:6379")
115            .expect("Should establish connection with no problem");
116
117        cache
118            .set(SetPayload {
119                key,
120                value: "42",
121                ttl: 1,
122            })
123            .expect("Should not fail");
124        let res = cache.get(key).unwrap();
125        assert_eq!(res, "42");
126        std::thread::sleep(std::time::Duration::from_secs(2));
127        let res = cache.get(key);
128        teardown(key);
129        assert!(res.is_none());
130    }
131}