use async_trait::async_trait;
use serde_json::json;
#[async_trait]
pub trait ApqStorage: Send + Sync {
async fn get(&self, hash: &str) -> Result<Option<String>, ApqError>;
async fn set(&self, hash: String, query: String) -> Result<(), ApqError>;
async fn exists(&self, hash: &str) -> Result<bool, ApqError>;
async fn remove(&self, hash: &str) -> Result<(), ApqError>;
async fn stats(&self) -> Result<ApqStats, ApqError>;
async fn clear(&self) -> Result<(), ApqError>;
}
#[derive(Debug, Clone)]
pub struct ApqStats {
pub total_queries: usize,
pub backend: String,
pub extra: serde_json::Value,
}
impl ApqStats {
#[must_use]
pub fn new(total_queries: usize, backend: String) -> Self {
Self {
total_queries,
backend,
extra: json!({}),
}
}
#[must_use]
pub const fn with_extra(
total_queries: usize,
backend: String,
extra: serde_json::Value,
) -> Self {
Self {
total_queries,
backend,
extra,
}
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ApqError {
#[error("Query not found")]
NotFound,
#[error("Query size exceeds maximum limit (100KB)")]
QueryTooLarge,
#[error("Storage error: {0}")]
StorageError(String),
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("Database error: {0}")]
DatabaseError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
}
pub type ArcApqStorage = std::sync::Arc<dyn ApqStorage>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_apq_stats_creation() {
let stats = ApqStats::new(100, "memory".to_string());
assert_eq!(stats.total_queries, 100);
assert_eq!(stats.backend, "memory");
assert_eq!(stats.extra, json!({}));
}
#[test]
fn test_apq_stats_with_extra() {
let extra = json!({
"hits": 500,
"misses": 50,
"hit_rate": 0.909
});
let stats = ApqStats::with_extra(100, "postgresql".to_string(), extra.clone());
assert_eq!(stats.total_queries, 100);
assert_eq!(stats.backend, "postgresql");
assert_eq!(stats.extra, extra);
}
#[test]
fn test_apq_error_display() {
let err = ApqError::QueryTooLarge;
assert_eq!(err.to_string(), "Query size exceeds maximum limit (100KB)");
let err = ApqError::StorageError("connection failed".to_string());
assert!(err.to_string().contains("connection failed"));
}
}