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 (C) 2025 Aleksandr Morozov
7 * 
8 * The syslog-rs crate can be redistributed and/or modified
9 * under the terms of either of the following licenses:
10 *
11 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
12 *                     
13 *   2. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
14 */
15
16use std::marker::PhantomData;
17/// This file contains configuration caches.
18
19use std::path::Path;
20use std::sync::Arc;
21use std::time::SystemTime;
22
23
24use crate::a_sync::interface::{AsyncMutex, AsyncMutexGuard, MutexedCaches, UnifiedFs};
25#[cfg(feature = "built_in_async")]
26use crate::a_sync::{IoInterf};
27use crate::cfg_host_parser::HostConfig;
28use crate::cfg_resolv_parser::ResolveConfig;
29use crate::{error::*, internal_error, internal_error_map, write_error};
30
31use super::cfg_parsers::*;
32
33//pub static CACHE: LazyLock<CachesController> = LazyLock::new(|| { CachesController::new() });
34
35
36pub trait CacheOperations
37{
38    fn is_reload_allowed(&self) -> bool;
39}
40
41#[derive(Clone, Debug)]
42struct CacheTime<'cache, FS: UnifiedFs>
43{
44    path: &'cache Path,
45    last_modif: SystemTime,
46    _fs: PhantomData<FS>
47}
48
49impl<'cache, FS: UnifiedFs> CacheTime<'cache, FS>
50{
51    async 
52    fn new(path: &'cache Path) -> CDnsResult<CacheTime<'cache, FS>>
53    {
54        return Ok(
55            CacheTime 
56            { 
57                path: path, 
58                last_modif: Self::get_last_modified(path).await?,
59                _fs: PhantomData
60            }
61        );
62    }
63
64    fn fake(path: &'cache Path) -> Self
65    {
66        return 
67            Self 
68            { 
69                path: path, 
70                last_modif: SystemTime::now(),
71                _fs: PhantomData
72            };
73    }
74
75    async 
76    fn get_last_modified(path: &'cache Path) -> CDnsResult<SystemTime>
77    {
78        let metadata = 
79            FS::metadata(path)
80                .await
81                .map_err(|e| 
82                    internal_error_map!(CDnsErrorType::InternalError, 
83                        "fs::metadata::modified() not supported on this platform: '{}'", e)
84                )?;
85
86        let last_modif = 
87            metadata
88                .modified()
89                .map_err(|e| 
90                    internal_error_map!(CDnsErrorType::InternalError, 
91                        "fs::metadata::modified() not supported on this platform: '{}'", e)
92                )?;
93
94        return Ok(last_modif);
95    }
96
97    async 
98    fn check_modified(&mut self) -> CDnsResult<bool>
99    {
100        // in case if get_last_modified() will return Err, return what was cached prevously
101        let last_modif = 
102            match Self::get_last_modified(self.path).await
103            {
104                Ok(t) => t,
105                Err(e) => internal_error!(CDnsErrorType::InternalError, "{}", e),
106            };
107
108        return Ok(self.last_modif != last_modif);
109    }
110}
111
112#[derive(Clone, Debug)]
113pub struct CacheInstance<T: Default + ConfigParser<T> + CacheOperations, FS: UnifiedFs>
114{
115    last_modif: CacheTime<'static, FS>,
116    cache: Arc<T>,
117    _fs: PhantomData<FS>
118}
119
120impl<T: Default + ConfigParser<T> + CacheOperations, FS: UnifiedFs> Default for CacheInstance<T, FS>
121{
122    fn default() -> Self 
123    {
124        return 
125            Self 
126            {
127                last_modif: CacheTime::fake(T::get_file_path()), 
128                cache: Arc::new(T::default()),
129                _fs: PhantomData
130              //  f: PhantomData,
131            };
132    }
133}
134
135impl<T: Default + ConfigParser<T> + CacheOperations, FS: UnifiedFs> CacheInstance<T, FS>
136{
137    async  
138    fn new() -> CDnsResult<Self>
139    {
140        return Ok(
141            Self 
142            {
143                last_modif: CacheTime::new(T::get_file_path()).await?, 
144                cache: Arc::new(T::parse_config::<FS>().await?),
145                _fs: PhantomData
146                //f: PhantomData,
147            }
148        );
149    }
150
151    async 
152    fn read_config() -> CDnsResult<Arc<T>>
153    {
154        return Ok(Arc::new(T::parse_config::<FS>().await?));
155    }
156
157    async 
158    fn check_modified(&mut self) -> CDnsResult<()>
159    {
160        // in case if get_last_modified() will return Err, save what was cached prevously
161        if self.cache.is_reload_allowed() == false
162        {
163            // just return OK
164            return Ok(());
165        }
166
167        if self.last_modif.check_modified().await? == true
168        {
169            // reload
170            self.cache = Self::read_config().await?;
171        }
172        
173        return Ok(());
174    }
175
176    fn clone_cache(&self) -> Arc<T>
177    {
178        return self.cache.clone();
179    }
180}
181
182/*
183pub struct CachesController
184{
185    resolv_cache: Mutex<CacheInstance<ResolveConfig>>,
186    host_cache: Mutex<CacheInstance<HostConfig>>
187}
188    */
189
190#[cfg(feature = "built_in_async")]
191#[derive(Debug)]
192pub struct CachesController<MC: MutexedCaches = IoInterf>
193{
194    resolv_cache: MC::ResolveCache,
195    host_cache: MC::HostCahae,
196    _p: PhantomData<MC>,
197}
198
199#[cfg(feature = "built_in_async")]
200impl CachesController<IoInterf>
201{
202    pub async 
203    fn new() -> CDnsResult<Self>
204    {
205         return Ok(
206            CachesController
207            { 
208                resolv_cache: 
209                    <IoInterf as MutexedCaches>::ResolveCache::a_new(CacheInstance::new().await?),//resolve_cache),
210                host_cache: 
211                    <IoInterf as MutexedCaches>::HostCahae::a_new(CacheInstance::new().await?),//host_cache),
212                _p: 
213                    PhantomData
214            }
215        );
216    }
217}
218
219#[cfg(not(feature = "built_in_async"))]
220#[derive(Debug)]
221pub struct CachesController<MC: MutexedCaches>
222{
223    resolv_cache: MC::ResolveCache,
224    host_cache: MC::HostCahae,
225    _p: PhantomData<MC>,
226}
227
228unsafe impl<MC: MutexedCaches> Sync for CachesController<MC>{}
229unsafe impl<MC: MutexedCaches> Send for CachesController<MC>{}
230
231impl<MC: MutexedCaches> CachesController<MC>
232{
233    /// Just creates insrance, but does not read anything from disk. 
234    pub async 
235    fn new_custom() -> CDnsResult<Self>
236    {
237        /*let resolve_cache_res = CacheInstance::<ResolveConfig>::new().await;
238
239        let host_cache_res = CacheInstance::<HostConfig>::new().await;
240
241        let resolve_cache = 
242            match resolve_cache_res
243            {
244                Ok(r) => r,
245                Err(e) =>
246                {
247                    write_error!("{}", e);
248
249                    CacheInstance::default()
250                }
251            };
252        
253        let host_cache = 
254            match host_cache_res
255            {
256                Ok(r) => r,
257                Err(e) =>
258                {
259                    write_error!("{}", e);
260
261                    CacheInstance::default()
262                }
263            };
264    */
265
266        return Ok(
267            CachesController
268            { 
269                resolv_cache: MC::ResolveCache::a_new(CacheInstance::new().await?),//resolve_cache),
270                host_cache: MC::HostCahae::a_new(CacheInstance::new().await?),//host_cache),
271                _p: PhantomData
272            }
273        );
274    }
275
276    pub async 
277    fn is_resolve_cached(&self) -> bool
278    {
279        return !self.resolv_cache.a_lock().await.guard().cache.is_default();
280    }
281
282    pub async 
283    fn is_host_cached(&self) -> bool
284    {
285        return !self.host_cache.a_lock().await.guard().cache.is_default();
286    }
287
288    async 
289    fn try_cache_resolve(&self) -> CDnsResult<Arc<ResolveConfig>>
290    {
291        let mut mutx_resolv = self.resolv_cache.a_lock().await;
292        mutx_resolv.guard_mut().check_modified().await?;
293
294        return Ok(mutx_resolv.guard().clone_cache());
295    }
296
297    pub async 
298    fn clone_resolve_list(&self) -> CDnsResult<Arc<ResolveConfig>>
299    {
300        // clone from cache and/or reload cache
301        match self.try_cache_resolve().await
302        {
303            Ok(r) => 
304                return Ok(r),
305            Err(e) => 
306            {
307                write_error!(e);
308
309                // try to read directly, this ignores all reload restrictions
310                return CacheInstance::<ResolveConfig, MC::MetadataFs>::read_config().await;
311            }
312        }
313    }
314
315    async 
316    fn try_cache_host(&self) -> CDnsResult<Arc<HostConfig>>
317    {
318        let mut mutx_host = self.host_cache.a_lock().await;
319        mutx_host.guard_mut().check_modified().await?;
320
321        return Ok(mutx_host.guard().clone_cache());
322    }
323
324    pub async 
325    fn clone_host_list(&self) -> CDnsResult<Arc<HostConfig>>
326    {
327        match self.try_cache_host().await
328        {
329            Ok(r) => return Ok(r),
330            Err(e) =>
331            {
332                write_error!(e);
333
334                // try to read directly, this ignores all reload restrictions
335                return CacheInstance::<HostConfig, MC::MetadataFs>::read_config().await;
336            }
337        }
338    }
339}
340
341
342#[cfg(feature = "use_async_tokio_tls")]
343#[cfg(test)]
344mod tests
345{
346    use std::sync::Arc;
347
348    use crate::a_sync::CachesController;
349
350
351    #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
352    async fn test_init()
353    {
354        let cache = Arc::new(CachesController::new().await.unwrap());
355
356        let res = cache.clone_resolve_list().await;
357        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
358
359        let res = res.unwrap();
360
361        println!("{:?}", res);
362
363        let res = cache.clone_host_list().await;
364        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
365
366        let res = res.unwrap();
367
368        println!("{:?}", res);
369    }
370}