use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::time::Duration;
use thiserror::Error;
pub mod cache;
pub mod engine;
pub mod processor;
pub mod templates;
pub use cache::{Cache, CacheStats};
pub use engine::{FactEngine, ProcessingOptions};
pub use processor::QueryProcessor;
pub use templates::{Template, TemplateRegistry};
pub type Result<T> = std::result::Result<T, FactError>;
#[derive(Error, Debug)]
pub enum FactError {
#[error("Template not found: {0}")]
TemplateNotFound(String),
#[error("Processing error: {0}")]
ProcessingError(String),
#[error("Serialization error: {0}")]
SerializationError(#[from] serde_json::Error),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("Cache error: {0}")]
CacheError(String),
#[error("Timeout exceeded: {0:?}")]
Timeout(Duration),
}
pub struct Fact {
engine: FactEngine,
cache: Arc<RwLock<Cache>>,
}
impl Fact {
pub fn new() -> Self {
Self {
engine: FactEngine::new(),
cache: Arc::new(RwLock::new(Cache::new())),
}
}
pub fn with_config(config: FactConfig) -> Self {
Self {
engine: FactEngine::with_config(config.engine_config),
cache: Arc::new(RwLock::new(Cache::with_capacity(config.cache_size))),
}
}
pub async fn process(
&self,
template_id: &str,
context: serde_json::Value,
) -> Result<serde_json::Value> {
let cache_key = self.generate_cache_key(template_id, &context);
if let Some(cached) = self.cache.write().get(&cache_key) {
return Ok(cached.clone());
}
let result = self.engine.process(template_id, context).await?;
self.cache.write().put(cache_key, result.clone());
Ok(result)
}
pub fn cache_stats(&self) -> CacheStats {
self.cache.read().stats()
}
pub fn clear_cache(&self) {
self.cache.write().clear();
}
fn generate_cache_key(&self, template_id: &str, context: &serde_json::Value) -> String {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
template_id.hash(&mut hasher);
context.to_string().hash(&mut hasher);
format!("fact:{}:{:x}", template_id, hasher.finish())
}
}
impl Default for Fact {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FactConfig {
pub engine_config: engine::EngineConfig,
pub cache_size: usize,
pub enable_monitoring: bool,
pub timeout: Option<Duration>,
}
impl Default for FactConfig {
fn default() -> Self {
Self {
engine_config: Default::default(),
cache_size: 100 * 1024 * 1024, enable_monitoring: true,
timeout: Some(Duration::from_secs(30)),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Metrics {
pub total_requests: u64,
pub cache_hits: u64,
pub cache_misses: u64,
pub avg_processing_time_ms: f64,
pub error_count: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_fact_creation() {
let fact = Fact::new();
let stats = fact.cache_stats();
assert_eq!(stats.entries, 0);
}
#[tokio::test]
async fn test_basic_processing() {
let fact = Fact::new();
let context = serde_json::json!({
"data": [1, 2, 3, 4, 5],
"operation": "sum"
});
match fact.process("test-template", context).await {
Ok(_) => {
}
Err(FactError::TemplateNotFound(_)) => {
}
Err(e) => panic!("Unexpected error: {}", e),
}
}
}