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

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

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

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

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

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

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

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