#![warn(missing_docs)]
use backends::CacheBackend;
use std::sync::{Mutex, OnceLock};
pub mod backends;
pub mod error;
pub mod eviction;
pub mod invalidation;
pub mod key_derivation;
pub mod metrics;
pub mod serialization;
pub mod warming;
#[cfg(test)]
mod invalidation_tests;
#[cfg(test)]
mod eviction_tests;
#[cfg(test)]
mod key_derivation_tests;
#[cfg(test)]
mod metrics_tests;
pub use error::Error as FncacheError;
#[cfg(feature = "wasm")]
pub use backends::wasm::WasmStorageBackend;
#[derive(Debug)]
pub struct GlobalCache(Box<dyn CacheBackend + Send + Sync>);
#[cfg(not(any(debug_assertions, feature = "test-utils")))]
static GLOBAL_CACHE: OnceLock<Mutex<GlobalCache>> = OnceLock::new();
#[cfg(any(debug_assertions, feature = "test-utils"))]
static GLOBAL_CACHE: OnceLock<Mutex<GlobalCache>> = OnceLock::new();
pub use backends::memory::MemoryBackend;
#[doc(inline)]
pub use fncache_macros::fncache;
pub type Result<T> = std::result::Result<T, error::Error>;
#[cfg(not(any(debug_assertions, feature = "test-utils")))]
pub fn init_global_cache<B>(backend: B) -> Result<()>
where
B: CacheBackend + Send + Sync + 'static,
{
let global_cache = GlobalCache(Box::new(backend));
GLOBAL_CACHE
.set(Mutex::new(global_cache))
.map_err(|_| error::Error::AlreadyInitialized)?;
Ok(())
}
#[cfg(any(debug_assertions, feature = "test-utils"))]
pub fn init_global_cache<B>(backend: B) -> Result<()>
where
B: CacheBackend + Send + Sync + 'static,
{
let global_cache = GlobalCache(Box::new(backend));
GLOBAL_CACHE
.set(Mutex::new(global_cache))
.map_err(|_| error::Error::AlreadyInitialized)?;
Ok(())
}
#[cfg(not(any(debug_assertions, feature = "test-utils")))]
pub fn global_cache() -> &'static Mutex<GlobalCache> {
GLOBAL_CACHE
.get()
.expect("Global cache not initialized. Call init_global_cache first.")
}
#[cfg(any(debug_assertions, feature = "test-utils"))]
pub fn global_cache() -> &'static Mutex<GlobalCache> {
GLOBAL_CACHE
.get()
.expect("Global cache not initialized. Call init_global_cache first.")
}
#[cfg(any(debug_assertions, feature = "test-utils"))]
pub fn reset_global_cache_for_testing() {
}
#[async_trait::async_trait]
impl CacheBackend for GlobalCache {
async fn get(&self, key: &String) -> Result<Option<Vec<u8>>> {
self.0.get(key).await
}
async fn set(
&self,
key: String,
value: Vec<u8>,
ttl: Option<std::time::Duration>,
) -> Result<()> {
self.0.set(key, value, ttl).await
}
async fn remove(&self, key: &String) -> Result<()> {
self.0.remove(key).await
}
async fn contains_key(&self, key: &String) -> Result<bool> {
self.0.contains_key(key).await
}
async fn clear(&self) -> Result<()> {
self.0.clear().await
}
}
pub mod prelude {
pub use crate::{
backends::{Backend, CacheBackend},
error::Error,
fncache, global_cache, init_global_cache,
metrics::Metrics,
Result,
};
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
#[serial]
fn test_global_cache_initialization() {
init_global_cache(MemoryBackend::new()).unwrap();
let _cache = global_cache();
}
#[test]
#[should_panic(expected = "Global cache not initialized")]
#[serial]
fn test_global_cache_uninitialized() {
static TEST_CACHE: OnceLock<Mutex<GlobalCache>> = OnceLock::new();
let _ = TEST_CACHE.get().expect("Global cache not initialized");
}
}