use std::time::Duration;
use log::error;
use moka::future::Cache;
use serde::Deserialize;
use crate::{errors::{FileError, VetisError, VirtualHostError}, fs::FileSource};
const MAX_FILE_SIZE: usize = 10 * 1024 * 1024; const DEFAULT_TTL: Duration = Duration::from_secs(60);
const DEFAULT_TTI: Duration = Duration::from_secs(10);
const DEFAULT_CAPACITY: u64 = 1000;
#[derive(Debug, Clone)]
pub struct CacheConfigBuilder {
max_file_size: usize,
ttl: Duration,
tti: Duration,
capacity: u64,
}
impl CacheConfigBuilder {
pub fn max_file_size(mut self, max_file_size: usize) -> Self {
self.max_file_size = max_file_size;
self
}
pub fn ttl(mut self, ttl: Duration) -> Self {
self.ttl = ttl;
self
}
pub fn tti(mut self, tti: Duration) -> Self {
self.tti = tti;
self
}
pub fn capacity(mut self, capacity: u64) -> Self {
self.capacity = capacity;
self
}
pub fn build(self) -> CacheConfig {
CacheConfig {
max_file_size: self.max_file_size,
ttl: self.ttl,
tti: self.tti,
capacity: self.capacity,
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct CacheConfig {
max_file_size: usize,
#[serde(deserialize_with = "crate::utils::serde::deserialize_duration")]
ttl: Duration,
#[serde(deserialize_with = "crate::utils::serde::deserialize_duration")]
tti: Duration,
capacity: u64,
}
impl Default for CacheConfig {
fn default() -> Self {
Self {
max_file_size: MAX_FILE_SIZE,
ttl: DEFAULT_TTL,
tti: DEFAULT_TTI,
capacity: DEFAULT_CAPACITY,
}
}
}
impl CacheConfig {
pub fn builder() -> CacheConfigBuilder {
CacheConfigBuilder {
max_file_size: MAX_FILE_SIZE,
ttl: DEFAULT_TTL,
tti: DEFAULT_TTI,
capacity: DEFAULT_CAPACITY,
}
}
pub fn max_file_size(&self) -> usize {
self.max_file_size
}
pub fn ttl(&self) -> Duration {
self.ttl
}
pub fn tti(&self) -> Duration {
self.tti
}
pub fn capacity(&self) -> u64 {
self.capacity
}
}
#[derive(Clone)]
pub struct FileCache {
cache: Cache<String, FileSource>,
}
impl FileCache {
pub fn new(cache: Cache<String, FileSource>) -> Self {
Self { cache }
}
pub async fn cache_file(&self, file_path: &std::path::Path) -> Result<FileSource, VetisError> {
let path = file_path
.display()
.to_string();
let file = if let Some(file) = self
.cache
.get(&path)
.await
{
Ok(file)
} else {
let file = self
.resolver
.load_file(file_path)
.await;
match file {
Ok(file) => {
self.cache
.insert(path.clone(), file)
.await;
let file = self
.cache
.get(&path)
.await
.unwrap();
Ok(file)
}
Err(e) => {
error!("Error resolving file {}: {}", path, e);
Err(VetisError::VirtualHost(VirtualHostError::File(FileError::NotFound)))
}
}
};
file
}
}