use std::path::Path;
use std::sync::Arc;
use std::time::SystemTime;
use tokio::fs;
use tokio::sync::Mutex;
use crate::cfg_host_parser::HostConfig;
use crate::cfg_resolv_parser::ResolveConfig;
use crate::{async_write_error, internal_error, internal_error_map, error::*};
use super::cfg_parsers::*;
lazy_static!{
pub static ref CACHE: CachesController = CachesController::new();
}
pub trait CacheOperations
{
fn is_reload_allowed(&self) -> bool;
}
#[derive(Clone, Debug)]
struct CacheTime<'cache>
{
path: &'cache Path,
last_modif: SystemTime,
}
impl<'cache> CacheTime<'cache>
{
async
fn new(path: &'cache Path) -> CDnsResult<CacheTime<'cache>>
{
return Ok(CacheTime { path: path, last_modif: Self::get_last_modified(path).await? });
}
fn fake(path: &'cache Path) -> Self
{
return Self { path: path, last_modif: SystemTime::now() };
}
async
fn get_last_modified(path: &'cache Path) -> CDnsResult<SystemTime>
{
let metadata = fs::metadata(path).await.map_err(|e|
internal_error_map!(CDnsErrorType::InternalError, "fs::metadata::modified() not supported on this platform: '{}'", e)
)?;
let last_modif =
metadata.modified().map_err(|e|
internal_error_map!(CDnsErrorType::InternalError, "fs::metadata::modified() not supported on this platform: '{}'", e)
)?;
return Ok(last_modif);
}
async
fn check_modified(&mut self) -> CDnsResult<bool>
{
let last_modif =
match Self::get_last_modified(self.path).await
{
Ok(t) => t,
Err(e) => internal_error!(CDnsErrorType::InternalError, "{}", e),
};
return Ok(self.last_modif != last_modif);
}
}
#[derive(Clone, Debug)]
pub struct CacheInstance<T: Default + ConfigParser<T> + CacheOperations>{
last_modif: CacheTime<'static>,
cache: Arc<T>,
}
impl<T: Default + ConfigParser<T> + CacheOperations> Default for CacheInstance<T>
{
fn default() -> Self
{
return
Self
{
last_modif: CacheTime::fake(T::get_file_path()),
cache: Arc::new(T::default()),
};
}
}
impl<T: Default + ConfigParser<T> + CacheOperations> CacheInstance<T>
{
async
fn new() -> CDnsResult<Self>
{
return Ok(
Self
{
last_modif: CacheTime::new(T::get_file_path()).await?,
cache: Arc::new(T::parse_config().await?),
}
);
}
async
fn read_config() -> CDnsResult<Arc<T>>
{
return Ok(Arc::new(T::parse_config().await?));
}
async
fn check_modified(&mut self) -> CDnsResult<()>
{
if self.cache.is_reload_allowed() == false
{
return Ok(());
}
if self.last_modif.check_modified().await? == true
{
self.cache = Self::read_config().await?;
}
return Ok(());
}
fn clone_cache(&self) -> Arc<T>
{
return self.cache.clone();
}
}
pub struct CachesController
{
resolv_cache: Mutex<CacheInstance<ResolveConfig>>,
host_cache: Mutex<CacheInstance<HostConfig>>
}
unsafe impl Sync for CachesController{}
unsafe impl Send for CachesController{}
impl CachesController
{
fn new() -> Self
{
return
CachesController
{
resolv_cache: Mutex::new(CacheInstance::default()),host_cache: Mutex::new(CacheInstance::default()),};
}
pub async
fn is_resolve_cached(&self) -> bool
{
return !self.resolv_cache.lock().await.cache.is_default();
}
pub async
fn is_host_cached(&self) -> bool
{
return !self.host_cache.lock().await.cache.is_default();
}
async
fn try_cache_resolve(&self) -> CDnsResult<Arc<ResolveConfig>>
{
let mut mutx_resolv = self.resolv_cache.lock().await;
mutx_resolv.check_modified().await?;
return Ok(mutx_resolv.clone_cache());
}
pub async
fn clone_resolve_list(&self) -> CDnsResult<Arc<ResolveConfig>>
{
match self.try_cache_resolve().await
{
Ok(r) => return Ok(r),
Err(e) =>
{
async_write_error!("{}", e);
return CacheInstance::<ResolveConfig>::read_config().await;
}
}
}
async
fn try_cache_host(&self) -> CDnsResult<Arc<HostConfig>>
{
let mut mutx_host = self.host_cache.lock().await;
mutx_host.check_modified().await?;
return Ok(mutx_host.clone_cache());
}
pub async
fn clone_host_list(&self) -> CDnsResult<Arc<HostConfig>>
{
match self.try_cache_host().await
{
Ok(r) => return Ok(r),
Err(e) =>
{
async_write_error!("{}", e);
return CacheInstance::<HostConfig>::read_config().await;
}
}
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_init()
{
let res = CACHE.clone_resolve_list().await;
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
println!("{:?}", res);
let res = CACHE.clone_host_list().await;
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
println!("{:?}", res);
}