1
use std::{error::Error as StdError, num::NonZeroUsize, sync::Arc};
2

            
3
use async_trait::async_trait;
4
use lru::LruCache;
5
use tokio::sync::RwLock;
6

            
7
use super::super::{
8
    device::{
9
        DelCacheQueryCond, DeviceCache, DeviceCacheItem, GetCacheQueryCond, QueryCond, QueryOneCond,
10
    },
11
    Model,
12
};
13

            
14
pub struct Cache {
15
    model: Arc<dyn Model>,
16
    uldata: Arc<RwLock<LruCache<String, Option<DeviceCacheItem>>>>,
17
}
18

            
19
pub struct Options {
20
    pub uldata_size: usize,
21
}
22

            
23
const DEF_SIZE: usize = 10_000;
24

            
25
impl Cache {
26
20
    pub fn new(opts: &Options, model: Arc<dyn Model>) -> Self {
27
20
        let uldata = unsafe { NonZeroUsize::new_unchecked(opts.uldata_size) };
28
20
        Cache {
29
20
            model,
30
20
            uldata: Arc::new(RwLock::new(LruCache::new(uldata))),
31
20
        }
32
20
    }
33
}
34

            
35
#[async_trait]
36
impl DeviceCache for Cache {
37
92
    async fn clear(&self) -> Result<(), Box<dyn StdError>> {
38
        // To collect all locks before clearing cache.
39
92
        let mut lock = self.uldata.write().await;
40
92
        lock.clear();
41
92
        Ok(())
42
184
    }
43

            
44
    async fn get(
45
        &self,
46
        cond: &GetCacheQueryCond,
47
64
    ) -> Result<Option<DeviceCacheItem>, Box<dyn StdError>> {
48
        // Try to hit cache first, or returns a model query condition.
49
64
        let model_cond = match cond {
50
64
            GetCacheQueryCond::CodeAddr(cond) => {
51
64
                let key = match cond.unit_code {
52
20
                    None => format!(".{}.{}", cond.network_code, cond.network_addr),
53
44
                    Some(unit) => format!("{}.{}.{}", unit, cond.network_code, cond.network_addr),
54
                };
55
                {
56
64
                    let mut lock = self.uldata.write().await;
57
64
                    if let Some(value) = lock.get(&key) {
58
18
                        return Ok(value.clone());
59
46
                    }
60
46
                }
61
46
                QueryCond {
62
46
                    device: Some(QueryOneCond {
63
46
                        unit_code: cond.unit_code,
64
46
                        network_code: cond.network_code,
65
46
                        network_addr: cond.network_addr,
66
46
                    }),
67
46
                    ..Default::default()
68
46
                }
69
            }
70
        };
71

            
72
46
        let item = match self.model.device().get(&model_cond).await? {
73
6
            None => None,
74
40
            Some(device) => Some(DeviceCacheItem {
75
40
                device_id: device.device_id,
76
40
                profile: device.profile,
77
40
            }),
78
        };
79
46
        let _ = self.set(cond, item.as_ref()).await;
80
46
        Ok(item)
81
128
    }
82

            
83
    async fn set(
84
        &self,
85
        cond: &GetCacheQueryCond,
86
        value: Option<&DeviceCacheItem>,
87
46
    ) -> Result<(), Box<dyn StdError>> {
88
46
        match cond {
89
46
            GetCacheQueryCond::CodeAddr(cond) => {
90
46
                let key = match cond.unit_code {
91
16
                    None => format!(".{}.{}", cond.network_code, cond.network_addr),
92
30
                    Some(unit) => format!("{}.{}.{}", unit, cond.network_code, cond.network_addr),
93
                };
94
                {
95
46
                    let mut lock = self.uldata.write().await;
96
46
                    let _ = match value {
97
6
                        None => lock.push(key, None),
98
40
                        Some(value) => lock.push(key, Some(value.clone())),
99
                    };
100
                }
101
            }
102
        }
103
46
        Ok(())
104
92
    }
105

            
106
32936
    async fn del(&self, cond: &DelCacheQueryCond) -> Result<(), Box<dyn StdError>> {
107
32936
        let key = match cond.network_code {
108
            None => {
109
                // Disallow deleting all devices of public networks.
110
4
                if cond.unit_code.len() == 0 {
111
2
                    return Ok(());
112
2
                }
113
2

            
114
2
                // Remove all routes of the unit.
115
2
                cond.unit_code.to_string()
116
            }
117
32932
            Some(code) => match cond.network_addr {
118
                None => {
119
                    // Remove all routes of the network.
120
16
                    format!("{}.{}", cond.unit_code, code)
121
                }
122
32916
                Some(addr) => {
123
32916
                    let key = format!("{}.{}.{}", cond.unit_code, code, addr);
124
                    {
125
32916
                        let mut lock = self.uldata.write().await;
126
32916
                        lock.pop(&key);
127
32916
                    }
128
32916
                    return Ok(());
129
                }
130
            },
131
        };
132
        {
133
18
            let mut lock = self.uldata.write().await;
134
            loop {
135
24
                let mut rm_key = None;
136
24
                for (k, _) in lock.iter() {
137
22
                    if k.starts_with(key.as_str()) {
138
6
                        rm_key = Some(k.clone());
139
6
                        break;
140
16
                    }
141
                }
142
24
                match rm_key {
143
18
                    None => break,
144
6
                    Some(key) => {
145
6
                        let _ = lock.pop(&key);
146
6
                    }
147
                }
148
            }
149
        }
150
18
        Ok(())
151
65872
    }
152
}
153

            
154
impl Default for Options {
155
10
    fn default() -> Self {
156
10
        Options {
157
10
            uldata_size: DEF_SIZE,
158
10
        }
159
10
    }
160
}