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