fact_tools/
engine.rs

1//! Core processing engine for FACT
2
3use crate::{FactError, Result, Template, TemplateRegistry};
4use serde::{Deserialize, Serialize};
5use std::sync::Arc;
6use std::time::Duration;
7use tokio::time::timeout;
8
9/// Configuration for the FACT engine
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct EngineConfig {
12    /// Maximum processing timeout
13    pub timeout: Duration,
14    
15    /// Enable parallel processing
16    pub parallel: bool,
17    
18    /// Maximum concurrent tasks
19    pub max_concurrent: usize,
20    
21    /// Enable performance monitoring
22    pub monitoring: bool,
23}
24
25impl Default for EngineConfig {
26    fn default() -> Self {
27        Self {
28            timeout: Duration::from_secs(30),
29            parallel: true,
30            max_concurrent: num_cpus::get(),
31            monitoring: true,
32        }
33    }
34}
35
36/// Processing options for individual requests
37#[derive(Debug, Clone, Default)]
38pub struct ProcessingOptions {
39    /// Override default timeout
40    pub timeout: Option<Duration>,
41    
42    /// Disable caching for this request
43    pub no_cache: bool,
44    
45    /// Processing priority
46    pub priority: Priority,
47}
48
49/// Processing priority levels
50#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
51pub enum Priority {
52    Low,
53    Normal,
54    High,
55    Critical,
56}
57
58impl Default for Priority {
59    fn default() -> Self {
60        Self::Normal
61    }
62}
63
64/// The main FACT processing engine
65pub struct FactEngine {
66    config: EngineConfig,
67    registry: Arc<TemplateRegistry>,
68}
69
70impl FactEngine {
71    /// Create a new engine with default configuration
72    pub fn new() -> Self {
73        Self::with_config(EngineConfig::default())
74    }
75    
76    /// Create a new engine with custom configuration
77    pub fn with_config(config: EngineConfig) -> Self {
78        Self {
79            config,
80            registry: Arc::new(TemplateRegistry::new()),
81        }
82    }
83    
84    /// Process a context using a cognitive template
85    pub async fn process(
86        &self,
87        template_id: &str,
88        context: serde_json::Value,
89    ) -> Result<serde_json::Value> {
90        self.process_with_options(template_id, context, ProcessingOptions::default())
91            .await
92    }
93    
94    /// Process with custom options
95    pub async fn process_with_options(
96        &self,
97        template_id: &str,
98        context: serde_json::Value,
99        options: ProcessingOptions,
100    ) -> Result<serde_json::Value> {
101        let template = self
102            .registry
103            .get(template_id)
104            .ok_or_else(|| FactError::TemplateNotFound(template_id.to_string()))?;
105        
106        let timeout_duration = options.timeout.unwrap_or(self.config.timeout);
107        
108        match timeout(
109            timeout_duration,
110            self.execute_template(&template, context, &options),
111        )
112        .await
113        {
114            Ok(result) => result,
115            Err(_) => Err(FactError::Timeout(timeout_duration)),
116        }
117    }
118    
119    /// Execute a template
120    async fn execute_template(
121        &self,
122        template: &Template,
123        mut context: serde_json::Value,
124        options: &ProcessingOptions,
125    ) -> Result<serde_json::Value> {
126        // Execute each step in the template
127        for step in &template.steps {
128            context = self.execute_step(&step, context, options).await?;
129        }
130        
131        Ok(serde_json::json!({
132            "template_id": template.id,
133            "template_name": template.name,
134            "result": context,
135            "metadata": {
136                "processed_at": chrono::Utc::now().to_rfc3339(),
137                "priority": format!("{:?}", options.priority),
138            }
139        }))
140    }
141    
142    /// Execute a single processing step
143    async fn execute_step(
144        &self,
145        step: &ProcessingStep,
146        context: serde_json::Value,
147        _options: &ProcessingOptions,
148    ) -> Result<serde_json::Value> {
149        match &step.operation {
150            Operation::Transform(transform) => self.apply_transform(transform, context),
151            Operation::Analyze(analysis) => self.apply_analysis(analysis, context),
152            Operation::Filter(filter) => self.apply_filter(filter, context),
153            Operation::Aggregate(aggregation) => self.apply_aggregation(aggregation, context),
154        }
155    }
156    
157    fn apply_transform(
158        &self,
159        transform: &Transform,
160        mut context: serde_json::Value,
161    ) -> Result<serde_json::Value> {
162        match transform {
163            Transform::Expand => {
164                if let Some(obj) = context.as_object_mut() {
165                    obj.insert(
166                        "_expanded".to_string(),
167                        serde_json::Value::Bool(true),
168                    );
169                    obj.insert(
170                        "_timestamp".to_string(),
171                        serde_json::Value::String(chrono::Utc::now().to_rfc3339()),
172                    );
173                }
174            }
175            Transform::Compress => {
176                if let Some(obj) = context.as_object_mut() {
177                    obj.retain(|k, _| !k.starts_with('_'));
178                }
179            }
180            Transform::Normalize => {
181                // Normalize the data structure
182                context = normalize_json(context);
183            }
184        }
185        
186        Ok(context)
187    }
188    
189    fn apply_analysis(
190        &self,
191        analysis: &Analysis,
192        context: serde_json::Value,
193    ) -> Result<serde_json::Value> {
194        let result = match analysis {
195            Analysis::Statistical => {
196                serde_json::json!({
197                    "original": context,
198                    "analysis": {
199                        "type": "statistical",
200                        "metrics": compute_statistics(&context),
201                    }
202                })
203            }
204            Analysis::Pattern => {
205                serde_json::json!({
206                    "original": context,
207                    "analysis": {
208                        "type": "pattern",
209                        "patterns": detect_patterns(&context),
210                    }
211                })
212            }
213            Analysis::Semantic => {
214                serde_json::json!({
215                    "original": context,
216                    "analysis": {
217                        "type": "semantic",
218                        "entities": extract_entities(&context),
219                        "concepts": extract_concepts(&context),
220                    }
221                })
222            }
223        };
224        
225        Ok(result)
226    }
227    
228    fn apply_filter(
229        &self,
230        filter: &Filter,
231        context: serde_json::Value,
232    ) -> Result<serde_json::Value> {
233        match filter {
234            Filter::Type(type_name) => {
235                // Filter by type
236                if context.get("type").and_then(|v| v.as_str()) == Some(type_name) {
237                    Ok(context)
238                } else {
239                    Ok(serde_json::Value::Null)
240                }
241            }
242            Filter::Range { min, max } => {
243                // Filter by numeric range
244                if let Some(value) = context.as_f64() {
245                    if value >= *min && value <= *max {
246                        Ok(context)
247                    } else {
248                        Ok(serde_json::Value::Null)
249                    }
250                } else {
251                    Ok(context)
252                }
253            }
254            Filter::Custom(expr) => {
255                // Apply custom filter expression
256                // This is a simplified implementation
257                if expr.contains("true") {
258                    Ok(context)
259                } else {
260                    Ok(serde_json::Value::Null)
261                }
262            }
263        }
264    }
265    
266    fn apply_aggregation(
267        &self,
268        aggregation: &Aggregation,
269        context: serde_json::Value,
270    ) -> Result<serde_json::Value> {
271        match aggregation {
272            Aggregation::Sum => {
273                let sum = sum_numeric_values(&context);
274                Ok(serde_json::json!({ "sum": sum }))
275            }
276            Aggregation::Average => {
277                let (sum, count) = sum_and_count_numeric_values(&context);
278                let avg = if count > 0 { sum / count as f64 } else { 0.0 };
279                Ok(serde_json::json!({ "average": avg }))
280            }
281            Aggregation::Count => {
282                let count = count_values(&context);
283                Ok(serde_json::json!({ "count": count }))
284            }
285        }
286    }
287}
288
289impl Default for FactEngine {
290    fn default() -> Self {
291        Self::new()
292    }
293}
294
295/// Processing step in a template
296#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct ProcessingStep {
298    pub name: String,
299    pub operation: Operation,
300}
301
302/// Available operations
303#[derive(Debug, Clone, Serialize, Deserialize)]
304#[serde(tag = "type", rename_all = "lowercase")]
305pub enum Operation {
306    Transform(Transform),
307    Analyze(Analysis),
308    Filter(Filter),
309    Aggregate(Aggregation),
310}
311
312/// Transform operations
313#[derive(Debug, Clone, Serialize, Deserialize)]
314#[serde(rename_all = "lowercase")]
315pub enum Transform {
316    Expand,
317    Compress,
318    Normalize,
319}
320
321/// Analysis operations
322#[derive(Debug, Clone, Serialize, Deserialize)]
323#[serde(rename_all = "lowercase")]
324pub enum Analysis {
325    Statistical,
326    Pattern,
327    Semantic,
328}
329
330/// Filter operations
331#[derive(Debug, Clone, Serialize, Deserialize)]
332#[serde(tag = "type", rename_all = "lowercase")]
333pub enum Filter {
334    Type(String),
335    Range { min: f64, max: f64 },
336    Custom(String),
337}
338
339/// Aggregation operations
340#[derive(Debug, Clone, Serialize, Deserialize)]
341#[serde(rename_all = "lowercase")]
342pub enum Aggregation {
343    Sum,
344    Average,
345    Count,
346}
347
348// Helper functions
349
350fn normalize_json(value: serde_json::Value) -> serde_json::Value {
351    match value {
352        serde_json::Value::Object(map) => {
353            let normalized: serde_json::Map<String, serde_json::Value> = map
354                .into_iter()
355                .map(|(k, v)| (k.to_lowercase(), normalize_json(v)))
356                .collect();
357            serde_json::Value::Object(normalized)
358        }
359        serde_json::Value::Array(arr) => {
360            serde_json::Value::Array(arr.into_iter().map(normalize_json).collect())
361        }
362        other => other,
363    }
364}
365
366fn compute_statistics(value: &serde_json::Value) -> serde_json::Value {
367    let numbers = extract_numbers(value);
368    
369    if numbers.is_empty() {
370        return serde_json::json!({});
371    }
372    
373    let sum: f64 = numbers.iter().sum();
374    let count = numbers.len() as f64;
375    let mean = sum / count;
376    
377    let variance = numbers.iter().map(|n| (n - mean).powi(2)).sum::<f64>() / count;
378    let std_dev = variance.sqrt();
379    
380    serde_json::json!({
381        "count": count,
382        "sum": sum,
383        "mean": mean,
384        "std_dev": std_dev,
385        "min": numbers.iter().cloned().fold(f64::INFINITY, f64::min),
386        "max": numbers.iter().cloned().fold(f64::NEG_INFINITY, f64::max),
387    })
388}
389
390fn extract_numbers(value: &serde_json::Value) -> Vec<f64> {
391    let mut numbers = Vec::new();
392    
393    match value {
394        serde_json::Value::Number(n) => {
395            if let Some(f) = n.as_f64() {
396                numbers.push(f);
397            }
398        }
399        serde_json::Value::Array(arr) => {
400            for v in arr {
401                numbers.extend(extract_numbers(v));
402            }
403        }
404        serde_json::Value::Object(map) => {
405            for v in map.values() {
406                numbers.extend(extract_numbers(v));
407            }
408        }
409        _ => {}
410    }
411    
412    numbers
413}
414
415fn detect_patterns(value: &serde_json::Value) -> Vec<String> {
416    let mut patterns = Vec::new();
417    
418    if let Some(obj) = value.as_object() {
419        if obj.contains_key("query") || obj.contains_key("question") {
420            patterns.push("inquiry".to_string());
421        }
422        if obj.contains_key("data") || obj.contains_key("dataset") {
423            patterns.push("data-driven".to_string());
424        }
425        if obj.contains_key("rules") || obj.contains_key("constraints") {
426            patterns.push("rule-based".to_string());
427        }
428    }
429    
430    patterns
431}
432
433fn extract_entities(value: &serde_json::Value) -> Vec<String> {
434    // Simplified entity extraction
435    let text = serde_json::to_string(value).unwrap_or_default();
436    
437    // Extract capitalized words as potential entities
438    text.split_whitespace()
439        .filter(|word| word.chars().next().map_or(false, |c| c.is_uppercase()))
440        .take(10)
441        .map(|s| s.to_string())
442        .collect()
443}
444
445fn extract_concepts(_value: &serde_json::Value) -> Vec<String> {
446    // Simplified concept extraction
447    vec![
448        "processing".to_string(),
449        "analysis".to_string(),
450        "transformation".to_string(),
451    ]
452}
453
454fn sum_numeric_values(value: &serde_json::Value) -> f64 {
455    extract_numbers(value).iter().sum()
456}
457
458fn sum_and_count_numeric_values(value: &serde_json::Value) -> (f64, usize) {
459    let numbers = extract_numbers(value);
460    (numbers.iter().sum(), numbers.len())
461}
462
463fn count_values(value: &serde_json::Value) -> usize {
464    match value {
465        serde_json::Value::Array(arr) => arr.len(),
466        serde_json::Value::Object(map) => map.len(),
467        _ => 1,
468    }
469}
470