1use parking_lot::RwLock;
37use serde::{Deserialize, Serialize};
38use std::sync::Arc;
39use std::time::Duration;
40use thiserror::Error;
41
42pub mod cache;
43pub mod engine;
44pub mod processor;
45pub mod templates;
46
47pub use cache::{Cache, CacheStats};
48pub use engine::{FactEngine, ProcessingOptions};
49pub use processor::QueryProcessor;
50pub use templates::{Template, TemplateRegistry};
51
52pub type Result<T> = std::result::Result<T, FactError>;
54
55#[derive(Error, Debug)]
57pub enum FactError {
58 #[error("Template not found: {0}")]
59 TemplateNotFound(String),
60
61 #[error("Processing error: {0}")]
62 ProcessingError(String),
63
64 #[error("Serialization error: {0}")]
65 SerializationError(#[from] serde_json::Error),
66
67 #[error("IO error: {0}")]
68 IoError(#[from] std::io::Error),
69
70 #[error("Cache error: {0}")]
71 CacheError(String),
72
73 #[error("Timeout exceeded: {0:?}")]
74 Timeout(Duration),
75}
76
77pub struct Fact {
79 engine: FactEngine,
80 cache: Arc<RwLock<Cache>>,
81}
82
83impl Fact {
84 pub fn new() -> Self {
86 Self {
87 engine: FactEngine::new(),
88 cache: Arc::new(RwLock::new(Cache::new())),
89 }
90 }
91
92 pub fn with_config(config: FactConfig) -> Self {
94 Self {
95 engine: FactEngine::with_config(config.engine_config),
96 cache: Arc::new(RwLock::new(Cache::with_capacity(config.cache_size))),
97 }
98 }
99
100 pub async fn process(
102 &self,
103 template_id: &str,
104 context: serde_json::Value,
105 ) -> Result<serde_json::Value> {
106 let cache_key = self.generate_cache_key(template_id, &context);
108
109 if let Some(cached) = self.cache.write().get(&cache_key) {
111 return Ok(cached.clone());
112 }
113
114 let result = self.engine.process(template_id, context).await?;
116
117 self.cache.write().put(cache_key, result.clone());
119
120 Ok(result)
121 }
122
123 pub fn cache_stats(&self) -> CacheStats {
125 self.cache.read().stats()
126 }
127
128 pub fn clear_cache(&self) {
130 self.cache.write().clear();
131 }
132
133 fn generate_cache_key(&self, template_id: &str, context: &serde_json::Value) -> String {
134 use std::collections::hash_map::DefaultHasher;
135 use std::hash::{Hash, Hasher};
136
137 let mut hasher = DefaultHasher::new();
138 template_id.hash(&mut hasher);
139 context.to_string().hash(&mut hasher);
140
141 format!("fact:{}:{:x}", template_id, hasher.finish())
142 }
143}
144
145impl Default for Fact {
146 fn default() -> Self {
147 Self::new()
148 }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct FactConfig {
154 pub engine_config: engine::EngineConfig,
156
157 pub cache_size: usize,
159
160 pub enable_monitoring: bool,
162
163 pub timeout: Option<Duration>,
165}
166
167impl Default for FactConfig {
168 fn default() -> Self {
169 Self {
170 engine_config: Default::default(),
171 cache_size: 100 * 1024 * 1024, enable_monitoring: true,
173 timeout: Some(Duration::from_secs(30)),
174 }
175 }
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct Metrics {
181 pub total_requests: u64,
182 pub cache_hits: u64,
183 pub cache_misses: u64,
184 pub avg_processing_time_ms: f64,
185 pub error_count: u64,
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[tokio::test]
193 async fn test_fact_creation() {
194 let fact = Fact::new();
195 let stats = fact.cache_stats();
196 assert_eq!(stats.entries, 0);
197 }
198
199 #[tokio::test]
200 async fn test_basic_processing() {
201 let fact = Fact::new();
202 let context = serde_json::json!({
203 "data": [1, 2, 3, 4, 5],
204 "operation": "sum"
205 });
206
207 match fact.process("test-template", context).await {
209 Ok(_) => {
210 }
212 Err(FactError::TemplateNotFound(_)) => {
213 }
215 Err(e) => panic!("Unexpected error: {}", e),
216 }
217 }
218}