cdns_rs/a_sync/
caches.rs

1/*-
2 * cdns-rs - a simple sync/async DNS query library
3 * 
4 * Copyright (C) 2020  Aleksandr Morozov
5 * 
6 * Copyright 2025 Aleksandr Morozov
7 * 
8 * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
9 * the European Commission - subsequent versions of the EUPL (the "Licence").
10 * 
11 * You may not use this work except in compliance with the Licence.
12 * 
13 * You may obtain a copy of the Licence at:
14 * 
15 *    https://joinup.ec.europa.eu/software/page/eupl
16 * 
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
19 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
20 * Licence for the specific language governing permissions and limitations
21 * under the Licence.
22 */
23
24/// This file contains configuration caches.
25
26use std::path::Path;
27use std::sync::{Arc, LazyLock};
28use std::time::SystemTime;
29
30use tokio::fs;
31
32use tokio::sync::Mutex;
33
34
35use crate::cfg_host_parser::HostConfig;
36use crate::cfg_resolv_parser::ResolveConfig;
37use crate::{async_write_error, internal_error, internal_error_map, error::*};
38
39use super::cfg_parsers::*;
40
41pub static CACHE: LazyLock<CachesController> = LazyLock::new(|| { CachesController::new() });
42
43
44
45pub trait CacheOperations
46{
47    fn is_reload_allowed(&self) -> bool;
48}
49
50#[derive(Clone, Debug)]
51struct CacheTime<'cache>
52{
53    path: &'cache Path,
54    last_modif: SystemTime,
55}
56
57impl<'cache> CacheTime<'cache>
58{
59    async 
60    fn new(path: &'cache Path) -> CDnsResult<CacheTime<'cache>>
61    {
62        return Ok(CacheTime { path: path, last_modif: Self::get_last_modified(path).await? });
63    }
64
65    fn fake(path: &'cache Path) -> Self
66    {
67        return Self { path: path, last_modif: SystemTime::now() };
68    }
69
70    async 
71    fn get_last_modified(path: &'cache Path) -> CDnsResult<SystemTime>
72    {
73        let metadata = fs::metadata(path).await.map_err(|e| 
74            internal_error_map!(CDnsErrorType::InternalError, "fs::metadata::modified() not supported on this platform: '{}'", e)
75        )?;
76
77        let last_modif = 
78            metadata.modified().map_err(|e| 
79                internal_error_map!(CDnsErrorType::InternalError, "fs::metadata::modified() not supported on this platform: '{}'", e)
80            )?;
81
82        return Ok(last_modif);
83    }
84
85    async 
86    fn check_modified(&mut self) -> CDnsResult<bool>
87    {
88        // in case if get_last_modified() will return Err, return what was cached prevously
89        let last_modif = 
90            match Self::get_last_modified(self.path).await
91            {
92                Ok(t) => t,
93                Err(e) => internal_error!(CDnsErrorType::InternalError, "{}", e),
94            };
95
96        return Ok(self.last_modif != last_modif);
97    }
98}
99
100#[derive(Clone, Debug)]
101pub struct CacheInstance<T: Default + ConfigParser<T> + CacheOperations>//, P: ConfigParser<T>>
102{
103    last_modif: CacheTime<'static>,
104    cache: Arc<T>,
105   // f: PhantomData<P>,
106}
107
108impl<T: Default + ConfigParser<T> + CacheOperations> Default for CacheInstance<T>
109{
110    fn default() -> Self 
111    {
112        return 
113            Self 
114            {
115                last_modif: CacheTime::fake(T::get_file_path()), 
116                cache: Arc::new(T::default()),
117              //  f: PhantomData,
118            };
119    }
120}
121
122impl<T: Default + ConfigParser<T> + CacheOperations> CacheInstance<T>
123{
124    async  
125    fn new() -> CDnsResult<Self>
126    {
127        return Ok(
128            Self 
129            {
130                last_modif: CacheTime::new(T::get_file_path()).await?, 
131                cache: Arc::new(T::parse_config().await?),
132                //f: PhantomData,
133            }
134        );
135    }
136
137    async 
138    fn read_config() -> CDnsResult<Arc<T>>
139    {
140        return Ok(Arc::new(T::parse_config().await?));
141    }
142
143    async 
144    fn check_modified(&mut self) -> CDnsResult<()>
145    {
146        // in case if get_last_modified() will return Err, save what was cached prevously
147        if self.cache.is_reload_allowed() == false
148        {
149            // just return OK
150            return Ok(());
151        }
152
153        if self.last_modif.check_modified().await? == true
154        {
155            // reload
156            self.cache = Self::read_config().await?;
157        }
158        
159        return Ok(());
160    }
161
162    fn clone_cache(&self) -> Arc<T>
163    {
164        return self.cache.clone();
165    }
166}
167
168pub struct CachesController
169{
170    resolv_cache: Mutex<CacheInstance<ResolveConfig>>,
171    host_cache: Mutex<CacheInstance<HostConfig>>
172}
173
174unsafe impl Sync for CachesController{}
175unsafe impl Send for CachesController{}
176
177impl CachesController
178{
179    /// Just creates insrance, but does not read anything from disk. 
180    fn new() -> Self
181    {
182        /*let resolve_cache_res = CacheInstance::<ResolveConfig>::new().await;
183
184        let host_cache_res = CacheInstance::<HostConfig>::new().await;
185
186        let resolve_cache = 
187            match resolve_cache_res
188            {
189                Ok(r) => r,
190                Err(e) =>
191                {
192                    write_error!("{}", e);
193
194                    CacheInstance::default()
195                }
196            };
197        
198        let host_cache = 
199            match host_cache_res
200            {
201                Ok(r) => r,
202                Err(e) =>
203                {
204                    write_error!("{}", e);
205
206                    CacheInstance::default()
207                }
208            };
209    */
210
211        return 
212            CachesController
213            { 
214                resolv_cache: Mutex::new(CacheInstance::default()),//resolve_cache),
215                host_cache: Mutex::new(CacheInstance::default()),//host_cache),
216            };
217    }
218
219    pub async 
220    fn is_resolve_cached(&self) -> bool
221    {
222        return !self.resolv_cache.lock().await.cache.is_default();
223    }
224
225    pub async 
226    fn is_host_cached(&self) -> bool
227    {
228        return !self.host_cache.lock().await.cache.is_default();
229    }
230
231    async 
232    fn try_cache_resolve(&self) -> CDnsResult<Arc<ResolveConfig>>
233    {
234        let mut mutx_resolv = self.resolv_cache.lock().await;
235        mutx_resolv.check_modified().await?;
236
237        return Ok(mutx_resolv.clone_cache());
238    }
239
240    pub async 
241    fn clone_resolve_list(&self) -> CDnsResult<Arc<ResolveConfig>>
242    {
243        // clone from cache and/or reload cache
244        match self.try_cache_resolve().await
245        {
246            Ok(r) => return Ok(r),
247            Err(e) => 
248            {
249                async_write_error!("{}", e);
250
251                // try to read directly, this ignores all reload restrictions
252                return CacheInstance::<ResolveConfig>::read_config().await;
253            }
254        }
255    }
256
257    async 
258    fn try_cache_host(&self) -> CDnsResult<Arc<HostConfig>>
259    {
260        let mut mutx_host = self.host_cache.lock().await;
261        mutx_host.check_modified().await?;
262
263        return Ok(mutx_host.clone_cache());
264    }
265
266    pub async 
267    fn clone_host_list(&self) -> CDnsResult<Arc<HostConfig>>
268    {
269        match self.try_cache_host().await
270        {
271            Ok(r) => return Ok(r),
272            Err(e) =>
273            {
274                async_write_error!("{}", e);
275
276                // try to read directly, this ignores all reload restrictions
277                return CacheInstance::<HostConfig>::read_config().await;
278            }
279        }
280    }
281}
282
283
284#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
285async fn test_init()
286{
287    let res = CACHE.clone_resolve_list().await;
288    assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
289
290    let res = res.unwrap();
291
292    println!("{:?}", res);
293
294    let res = CACHE.clone_host_list().await;
295    assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
296
297    let res = res.unwrap();
298
299    println!("{:?}", res);
300}