#[cfg(feature = "aur")]
mod config;
#[cfg(feature = "cache-disk")]
#[cfg(feature = "aur")]
mod disk;
#[cfg(feature = "aur")]
mod memory;
#[cfg(feature = "aur")]
pub use config::{CacheConfig, CacheConfigBuilder};
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[cfg(feature = "aur")]
pub trait Cache<K, V>
where
K: AsRef<str>,
V: Clone + Serialize + for<'de> Deserialize<'de>,
{
fn get(&self, key: &K) -> Option<V>;
fn set(&self, key: &K, value: &V, ttl: Duration) -> Result<(), CacheError>;
fn invalidate(&self, key: &K) -> Result<(), CacheError>;
fn clear(&self) -> Result<(), CacheError>;
}
#[cfg(feature = "aur")]
#[derive(Debug, thiserror::Error)]
pub enum CacheError {
#[error("Cache serialization error: {0}")]
Serialization(String),
#[error("Cache deserialization error: {0}")]
Deserialization(String),
#[error("Cache I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Cache error: {0}")]
Other(String),
}
#[cfg(feature = "aur")]
#[must_use]
pub fn cache_key_search(query: &str) -> String {
let trimmed = query.trim();
format!("search:{trimmed}")
}
#[cfg(feature = "aur")]
#[must_use]
pub fn cache_key_info(names: &[&str]) -> String {
let mut sorted = names.to_vec();
sorted.sort_unstable();
format!("info:{}", sorted.join(","))
}
#[cfg(feature = "aur")]
#[must_use]
pub fn cache_key_comments(pkgname: &str) -> String {
format!("comments:{pkgname}")
}
#[cfg(feature = "aur")]
#[must_use]
pub fn cache_key_pkgbuild(package: &str) -> String {
format!("pkgbuild:{package}")
}
#[cfg(feature = "aur")]
use memory::MemoryCache;
#[cfg(feature = "cache-disk")]
#[cfg(feature = "aur")]
use disk::DiskCache;
#[cfg(feature = "aur")]
#[derive(Debug)]
pub struct CacheWrapper {
memory: MemoryCache,
#[cfg(feature = "cache-disk")]
disk: Option<DiskCache>,
}
#[cfg(feature = "aur")]
impl CacheWrapper {
pub fn new(config: &CacheConfig) -> Result<Self, CacheError> {
let memory = MemoryCache::new(config.memory_cache_size);
#[cfg(feature = "cache-disk")]
{
let disk = if config.enable_disk_cache {
Some(DiskCache::new().map_err(CacheError::Io)?)
} else {
None
};
Ok(Self { memory, disk })
}
#[cfg(not(feature = "cache-disk"))]
{
Ok(Self { memory })
}
}
#[must_use]
pub fn get<V>(&self, key: &str) -> Option<V>
where
V: Clone + Serialize + for<'de> Deserialize<'de>,
{
let key_str = key.to_string();
if let Some(value) = <MemoryCache as Cache<String, V>>::get(&self.memory, &key_str) {
return Some(value);
}
#[cfg(feature = "cache-disk")]
if let Some(ref disk) = self.disk
&& let Some(value) = <DiskCache as Cache<String, V>>::get(disk, &key_str)
{
let _ = <MemoryCache as Cache<String, V>>::set(
&self.memory,
&key_str,
&value,
Duration::from_secs(300),
);
return Some(value);
}
None
}
pub fn set<V>(&self, key: &str, value: &V, ttl: Duration) -> Result<(), CacheError>
where
V: Clone + Serialize + for<'de> Deserialize<'de>,
{
let key_str = key.to_string();
<MemoryCache as Cache<String, V>>::set(&self.memory, &key_str, value, ttl)?;
#[cfg(feature = "cache-disk")]
if let Some(ref disk) = self.disk {
let _ = <DiskCache as Cache<String, V>>::set(disk, &key_str, value, ttl);
}
Ok(())
}
pub fn invalidate(&self, key: &str) -> Result<(), CacheError> {
let key_str = key.to_string();
<MemoryCache as Cache<String, ()>>::invalidate(&self.memory, &key_str)?;
#[cfg(feature = "cache-disk")]
if let Some(ref disk) = self.disk {
let _ = <DiskCache as Cache<String, ()>>::invalidate(disk, &key_str);
}
Ok(())
}
pub fn clear(&self) -> Result<(), CacheError> {
<MemoryCache as Cache<String, ()>>::clear(&self.memory)?;
#[cfg(feature = "cache-disk")]
if let Some(ref disk) = self.disk {
let _ = <DiskCache as Cache<String, ()>>::clear(disk);
}
Ok(())
}
}