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
112
113
114
115
116
// Copyright 2018 Stefan Kroboth
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Implementation of the macros `cache!` and `cache_func!`.
use errors::*;
use PersistentCache;
use PREFIX;

/// Cache an entire function.
#[macro_export]
macro_rules! cache_func {
    // Create `RedisStorage` with default prefix
    (Redis, $host:expr, fn $f:ident($($x:ident : $t:ty),*) -> $r:ty $b:block) => {
        cache_func!(Redis, $host, "DEF", fn $f($($x : $t),*) -> $r $b);
    };
    // Create `FileStorage` with default prefix
    (File, $dir:expr, fn $f:ident($($x:ident : $t:ty),*) -> $r:ty $b:block) => {
        cache_func!(File, $dir, "DEF", fn $f($($x : $t),*) -> $r $b);
    };
    // Create `RedisStorage` with provided prefix
    (Redis, $host:expr, $prefix:expr, fn $f:ident($($x:ident : $t:ty),*) -> $r:ty $b:block) => {
        fn $f($($x: $t),*) -> $r {
            lazy_static!{
                // Unfortunately, the `redis` crate requires Mutex to work.
                // May need to look into this in more detail.
                static ref S: ::std::sync::Mutex<::storage::redis::RedisStorage> = ::std::sync::Mutex::new(::storage::redis::RedisStorage::new($host).unwrap());
            };
            cache_func!($f($($x),*), $b, $prefix);
        }
    };
    // Create `FileStorage` with provided prefix
    (File, $dir:expr, $prefix:expr, fn $f:ident($($x:ident : $t:ty),*) -> $r:ty $b:block) => {
        fn $f($($x: $t),*) -> $r {
            lazy_static!{
                // In order to be consistent with `RedisStorage`, `FileStorage` also uses a Mutex.
                // However, it would not be necessary.
                static ref S: ::std::sync::Mutex<::storage::file::FileStorage> = ::std::sync::Mutex::new(::storage::file::FileStorage::new($dir).unwrap());
            };
            cache_func!($f($($x),*), $b, $prefix);
        }
    };
    // internal
    ($f:ident($($x:ident),*), $b:block, $prefix:expr) => {
        extern crate bincode as pers_f_bincode;
        // use bincode as pers_f_bincode;
        // use bincode;
        use ::std::hash::{Hash, Hasher};

        let mut s = ::std::collections::hash_map::DefaultHasher::new();
        for item in &[&($($x),*)] {
            item.hash(&mut s);
        }
        let var_name = format!("{}_{}_{}_{:?}", PREFIX, $prefix, stringify!($f), s.finish());
        let result: Vec<u8> = S.lock().unwrap().get(&var_name).unwrap();

        match result.len() {
            0 => {
                let res = {$b};
                S.lock().unwrap().set(&var_name, &pers_f_bincode::serialize(&res).unwrap()).unwrap();
                return res;
            },
            _ => return pers_f_bincode::deserialize(&result).unwrap(),
        }
    }
}

/// Cache a single function call.
#[macro_export]
macro_rules! cache {
    // no prefix provided
    ($storage:ident, $func:ident($($x:expr),*)) => {
        cache!($storage, $func($($x),*), "DEF")
    };
    // prefix provided
    ($storage:ident, $func:ident($($x:expr),*), $prefix:expr) => {
        (||{
            extern crate bincode as pers_bincode;
            use ::std::hash::{Hash, Hasher};

            let mut s = ::std::collections::hash_map::DefaultHasher::new();
            for item in &[&($($x),*)] {
                item.hash(&mut s);
            }
            let var_name = format!("{}_{}_{}_{:?}", PREFIX, $prefix, stringify!($func), s.finish());

            let result: Vec<u8> = $storage.get(&var_name).unwrap();
            let res;
            match result.len() {
                0 => {
                    res = $func($($x),*);
                    $storage.set(&var_name, &pers_bincode::serialize(&res).unwrap()).unwrap();
                    res
                    // match $func($($x),*) {
                    //     Ok(res) => {
                    //         $storage.set(&var_name, &pers_bincode::serialize(&res)?)?;
                    //         Ok(res)
                    //     }
                    //     Err(e) => Err(e)
                    // }
                },
                // _ => match pers_bincode::deserialize(&result) {
                //     Ok(res) => res,
                //     Err(e) => panic!(e.into()), // I have no idea what I am doing.
                // }
                _ => {
                    res = pers_bincode::deserialize(&result).unwrap();
                    res
                }
            }
       })()
    }
}