use crate::error::ValidationErrorType;
use crate::validator::OpenApiPayloadValidator;
use dashmap::{DashMap, VacantEntry};
use serde_json::Value;
use std::fmt::{Display, Formatter};
use std::sync::{Arc, OnceLock};
static GLOBAL_CACHE: OnceLock<ValidatorCache> = OnceLock::new();
pub fn global_validator_cache() -> &'static ValidatorCache {
GLOBAL_CACHE.get_or_init(ValidatorCache::new)
}
#[derive(Debug)]
pub enum CacheError {
ValidatorNotFound,
ValidatorAlreadyExists,
FailedToCreateValidator(ValidationErrorType),
}
impl Display for CacheError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
CacheError::ValidatorNotFound => write!(f, "Validator not found in cache"),
CacheError::ValidatorAlreadyExists => write!(f, "Validator already exists in cache"),
CacheError::FailedToCreateValidator(err) => {
write!(f, "Failed to create new validator: {}", err)
}
}
}
}
impl std::error::Error for CacheError {}
#[derive(Default)]
pub struct ValidatorCache {
cache: DashMap<String, Arc<OpenApiPayloadValidator>>,
}
impl ValidatorCache {
pub fn new() -> Self {
ValidatorCache {
cache: DashMap::new(),
}
}
pub fn insert(
&self,
id: String,
spec: Value,
) -> Result<Arc<OpenApiPayloadValidator>, CacheError> {
match self.cache.entry(id) {
dashmap::mapref::entry::Entry::Occupied(_) => Err(CacheError::ValidatorAlreadyExists),
dashmap::mapref::entry::Entry::Vacant(entry) => Self::create_validator(entry, spec),
}
}
fn create_validator(
entry: VacantEntry<String, Arc<OpenApiPayloadValidator>>,
spec: Value,
) -> Result<Arc<OpenApiPayloadValidator>, CacheError> {
match OpenApiPayloadValidator::new(spec) {
Ok(validator) => {
log::debug!("Added validator to cache with ID: {}", entry.key());
let validator = Arc::new(validator);
entry.insert(validator.clone());
Ok(validator)
}
Err(e) => {
log::error!("Failed to create validator for ID {}: {}", entry.key(), e);
Err(CacheError::FailedToCreateValidator(e))
}
}
}
pub fn insert_or_replace(&self, id: String, validator: OpenApiPayloadValidator) {
self.cache.insert(id, Arc::new(validator));
}
pub fn get(&self, id: &str) -> Result<Arc<OpenApiPayloadValidator>, CacheError> {
match self.cache.get(id) {
Some(validator) => Ok(Arc::clone(validator.value())),
None => Err(CacheError::ValidatorNotFound),
}
}
pub fn remove(&self, id: &str) -> Result<(), CacheError> {
if self.cache.remove(id).is_none() {
return Err(CacheError::ValidatorNotFound);
}
Ok(())
}
pub fn get_or_insert(
&self,
id: String,
spec: Value,
) -> Result<Arc<OpenApiPayloadValidator>, CacheError> {
match self.cache.entry(id) {
dashmap::mapref::entry::Entry::Occupied(entry) => Ok(Arc::clone(entry.get())),
dashmap::mapref::entry::Entry::Vacant(entry) => Self::create_validator(entry, spec),
}
}
pub fn contains(&self, id: &str) -> bool {
self.cache.contains_key(id)
}
pub fn len(&self) -> usize {
self.cache.len()
}
pub fn is_empty(&self) -> bool {
self.cache.is_empty()
}
pub fn clear(&self) {
self.cache.clear();
log::debug!("Cleared validator cache");
}
}
#[cfg(test)]
mod tests {
}