use crate::error::{Result, SecurityError};
use dashmap::DashMap;
use std::sync::Arc;
type QuotaEntry = (u64, Arc<parking_lot::RwLock<u64>>);
type QuotaKey = (String, String);
pub struct QuotaManager {
quotas: Arc<DashMap<QuotaKey, QuotaEntry>>,
}
impl QuotaManager {
pub fn new() -> Self {
Self {
quotas: Arc::new(DashMap::new()),
}
}
pub fn set_quota(&self, tenant_id: String, resource_type: String, limit: u64) -> Result<()> {
self.quotas.insert(
(tenant_id, resource_type),
(limit, Arc::new(parking_lot::RwLock::new(0))),
);
Ok(())
}
pub fn check_quota(&self, tenant_id: &str, resource_type: &str, amount: u64) -> Result<bool> {
let key = (tenant_id.to_string(), resource_type.to_string());
if let Some(entry) = self.quotas.get(&key) {
let (limit, used) = entry.value();
let current = *used.read();
Ok(current + amount <= *limit)
} else {
Ok(true) }
}
pub fn use_quota(&self, tenant_id: &str, resource_type: &str, amount: u64) -> Result<()> {
let key = (tenant_id.to_string(), resource_type.to_string());
if let Some(entry) = self.quotas.get(&key) {
let (limit, used) = entry.value();
let mut current = used.write();
if *current + amount > *limit {
return Err(SecurityError::quota_exceeded(format!(
"Quota exceeded for {} ({})",
tenant_id, resource_type
)));
}
*current += amount;
}
Ok(())
}
pub fn release_quota(&self, tenant_id: &str, resource_type: &str, amount: u64) -> Result<()> {
let key = (tenant_id.to_string(), resource_type.to_string());
if let Some(entry) = self.quotas.get(&key) {
let (_, used) = entry.value();
let mut current = used.write();
*current = current.saturating_sub(amount);
}
Ok(())
}
}
impl Default for QuotaManager {
fn default() -> Self {
Self::new()
}
}