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
117
118
119
120
121
122
123
124
125
126
127
128
129
use std::any::Any;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::hash::Hash;
use std::sync::Arc;

use async_std::sync::RwLock;

use arc_swap::ArcSwap;

type AnyObject = Box<dyn Any + Send + Sync>;
type CacheObject = Option<ArcSwap<AnyObject>>;

pub trait CacheItem: Send + Sync {}

pub enum CacheResult {
    Ok,
    Error,
}

#[derive(Clone)]
pub struct Cache<K> {
    items: Arc<RwLock<HashMap<K, CacheObject>>>,
}

impl<K> Cache<K>
where
    K: Eq + Hash,
{
    pub fn new() -> Self {
        Self {
            items: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    pub async fn get<T: 'static + CacheItem, Q: ?Sized>(&self, key: &Q) -> Option<Option<Arc<T>>>
    where
        K: Borrow<Q>,
        Q: Hash + Eq,
    {
        match self.items.read().await.get(key) {
            Some(object) => match object {
                Some(object) => Some(match object.load().downcast_ref::<Arc<T>>() {
                    Some(value) => Some(value.to_owned()),
                    None => None,
                }),
                None => Some(None),
            },
            None => None,
        }
    }

    pub async fn insert<T: 'static + CacheItem>(&self, key: K, value: Option<T>) -> CacheResult {
        match self.items.write().await.insert(
            key,
            match value {
                Some(value) => Some(ArcSwap::new(Arc::new(
                    Box::new(Arc::new(value)) as AnyObject
                ))),
                None => None,
            },
        ) {
            Some(_) => CacheResult::Ok,
            None => CacheResult::Error,
        }
    }

    pub async fn remove<Q: ?Sized>(&self, key: &Q) -> CacheResult
    where
        K: Borrow<Q>,
        Q: Hash + Eq,
    {
        match self.items.write().await.remove(key) {
            Some(_) => CacheResult::Ok,
            None => CacheResult::Error,
        }
    }
}

impl<K> Default for Cache<K>
where
    K: Eq + Hash,
{
    fn default() -> Self {
        Cache::new()
    }
}

#[cfg(test)]
mod tests {
    use crate::{Cache, CacheItem};

    struct Object {
        value: i32,
        string: String,
    }

    impl CacheItem for Object {}

    #[tokio::test]
    async fn insert_and_get() {
        let cache = Cache::new();
        let object = Object {
            value: 1,
            string: String::from("test!"),
        };

        cache.insert("test", Some(object)).await;

        let cached_object = cache.get::<Object, _>("test").await.unwrap().unwrap();

        assert_eq!(cached_object.value, 1);
        assert_eq!(cached_object.string, "test!");
    }

    #[tokio::test]
    async fn remove() {
        let cache = Cache::new();
        let object = Object {
            value: 1,
            string: String::from("test!"),
        };

        cache.insert("test", Some(object)).await;
        cache.remove("test").await;

        assert_eq!(cache.get::<Object, _>("test").await.is_none(), true);
    }
}