use std::path::Path;
use std::sync::{Arc, LazyLock, Mutex};
use std::time::SystemTime;
use std::fs;
use crate::cfg_host_parser::HostConfig;
use crate::cfg_resolv_parser::ResolveConfig;
use crate::{write_error, internal_error, internal_error_map, error::*};
use super::cfg_parsers::*;
pub static CACHE: LazyLock<CachesController> = LazyLock::new(|| { 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>
{
fn new(path: &'cache Path) -> CDnsResult<Self>
{
return Ok(Self { path: path, last_modif: Self::get_last_modified(path)? });
}
fn fake(path: &'cache Path) -> Self
{
return Self { path: path, last_modif: SystemTime::now() };
}
fn get_last_modified(path: &'cache Path) -> CDnsResult<SystemTime>
{
let metadata = fs::metadata(path).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);
}
fn check_modified(&mut self) -> CDnsResult<bool>
{
let last_modif =
match Self::get_last_modified(self.path)
{
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>
{
fn new() -> CDnsResult<Self>
{
return Ok(
Self
{
last_modif: CacheTime::new(T::get_file_path())?,
cache: Arc::new(T::parse_config()?),
}
);
}
fn read_config() -> CDnsResult<Arc<T>>
{
return Ok(Arc::new(T::parse_config()?));
}
fn check_modified(&mut self) -> CDnsResult<()>
{
if self.cache.is_reload_allowed() == false
{
return Ok(());
}
if self.last_modif.check_modified()? == true
{
self.cache = Self::read_config()?;
}
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
{
let resolve_cache_res = CacheInstance::<ResolveConfig>::new();
let host_cache_res = CacheInstance::<HostConfig>::new();
let resolve_cache =
match resolve_cache_res
{
Ok(r) => r,
Err(e) =>
{
write_error!("{}", e);
CacheInstance::default()
}
};
let host_cache =
match host_cache_res
{
Ok(r) => r,
Err(e) =>
{
write_error!("{}", e);
CacheInstance::default()
}
};
return
CachesController
{
resolv_cache: Mutex::new(resolve_cache),
host_cache: Mutex::new(host_cache),
};
}
pub
fn is_resolve_cached(&self) -> bool
{
return !self.resolv_cache.lock().unwrap().cache.is_default();
}
pub
fn is_host_cached(&self) -> bool
{
return !self.host_cache.lock().unwrap().cache.is_default();
}
fn try_cache_resolve(&self) -> CDnsResult<Arc<ResolveConfig>>
{
let mut mutx_resolv = self.resolv_cache.lock().unwrap();
mutx_resolv.check_modified()?;
return Ok(mutx_resolv.clone_cache());
}
pub
fn clone_resolve_list(&self) -> CDnsResult<Arc<ResolveConfig>>
{
match self.try_cache_resolve()
{
Ok(r) => return Ok(r),
Err(e) =>
{
write_error!("{}", e);
return CacheInstance::<ResolveConfig>::read_config();
}
}
}
fn try_cache_host(&self) -> CDnsResult<Arc<HostConfig>>
{
let mut mutx_host = self.host_cache.lock().unwrap();
mutx_host.check_modified()?;
return Ok(mutx_host.clone_cache());
}
pub
fn clone_host_list(&self) -> CDnsResult<Arc<HostConfig>>
{
match self.try_cache_host()
{
Ok(r) => return Ok(r),
Err(e) =>
{
write_error!("{}", e);
return CacheInstance::<HostConfig>::read_config();
}
}
}
}
#[test]
fn test_init()
{
let res = CACHE.clone_resolve_list();
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
println!("{:?}", res);
let res = CACHE.clone_host_list();
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
println!("{:?}", res);
}