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
//! Storage for persistently saving return values of functions in Redis.
use std::error::Error;
use redis::{self, Commands};
use errors::*;

#[allow(unused_imports)]
use PREFIX;
use PersistentCache;


/// `RedisStorage` struct holds a `redis::Connection` variable.
pub struct RedisStorage {
    con: redis::Connection,
}

impl RedisStorage {
    /// Connects to the Redis server listening at `host` and constructs a new `RedisStorage`
    /// struct.
    ///
    /// This will fail in case there is no redis server running.
    ///
    /// # Examples
    ///
    /// ```
    /// use persistentcache::storage::redis::RedisStorage;
    ///
    /// let s = RedisStorage::new("redis://127.0.0.1").unwrap();
    /// ```
    pub fn new(host: &str) -> Result<Self> {
        let client = redis::Client::open(host)?;
        let con = client.get_connection()?;
        Ok(RedisStorage { con: con })
    }
}

impl PersistentCache for RedisStorage {
    /// Returns the value within the Redis variable `name`.
    fn get(&self, name: &str) -> Result<Vec<u8>> {
        match self.con.get(name) {
            Ok(res) => Ok(res),
            Err(e) => Err(e.into()),
        }
    }

    /// Sets the Redis variable `name` to the array `val` of type `&[u8]`.
    fn set(&self, name: &str, val: &[u8]) -> Result<()> {
        // Yes, this is weird.
        let r: Result<()> = self.con.set(name, val).map_err(|e| e.into());
        r?;
        Ok(())
    }

    /// Delete all variables stored in the Redis database which start with `PREFIX_`.
    fn flush(&self) -> Result<()> {
        let iter: redis::Iter<String> = redis::cmd("KEYS").arg(format!("{}_*", PREFIX)).iter(
            &self.con,
        )?;
        let cmd: &mut redis::Cmd = &mut redis::cmd("DEL");
        // Not a very good looking hack, but I dont know how to figure out whether the iterator is
        // empty or not...
        let mut flushed_vars = 0;
        for bla in iter {
            flushed_vars += 1;
            cmd.arg(bla);
        }
        if flushed_vars > 0 {
            let r: Result<()> = cmd.query(&self.con).map_err(|e| e.into());
            // This is weird.
            r?;
        }
        Ok(())
    }
}