1use crate::engine::{Operation, ProcessingStep, Transform, Analysis, Filter, Aggregation};
4use ahash::AHashMap;
5use serde::{Deserialize, Serialize};
6use parking_lot::RwLock;
7use std::sync::Arc;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Template {
12 pub id: String,
14
15 pub name: String,
17
18 pub description: String,
20
21 pub steps: Vec<ProcessingStep>,
23
24 pub metadata: TemplateMetadata,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct TemplateMetadata {
31 pub version: String,
33
34 pub author: String,
36
37 pub created_at: String,
39
40 pub updated_at: String,
42
43 pub tags: Vec<String>,
45
46 pub performance: PerformanceProfile,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct PerformanceProfile {
53 pub avg_execution_time_ms: f64,
55
56 pub memory_usage_bytes: usize,
58
59 pub complexity: u8,
61}
62
63pub struct TemplateRegistry {
65 templates: Arc<RwLock<AHashMap<String, Template>>>,
66}
67
68impl TemplateRegistry {
69 pub fn new() -> Self {
71 let registry = Self {
72 templates: Arc::new(RwLock::new(AHashMap::new())),
73 };
74
75 registry.load_default_templates();
77
78 registry
79 }
80
81 pub fn register(&self, template: Template) {
83 self.templates.write().insert(template.id.clone(), template);
84 }
85
86 pub fn get(&self, id: &str) -> Option<Template> {
88 self.templates.read().get(id).cloned()
89 }
90
91 pub fn list(&self) -> Vec<String> {
93 self.templates.read().keys().cloned().collect()
94 }
95
96 pub fn remove(&self, id: &str) -> Option<Template> {
98 self.templates.write().remove(id)
99 }
100
101 fn load_default_templates(&self) {
103 self.register(Template {
105 id: "analysis-basic".to_string(),
106 name: "Basic Analysis".to_string(),
107 description: "Performs basic statistical and pattern analysis".to_string(),
108 steps: vec![
109 ProcessingStep {
110 name: "normalize".to_string(),
111 operation: Operation::Transform(Transform::Normalize),
112 },
113 ProcessingStep {
114 name: "analyze".to_string(),
115 operation: Operation::Analyze(Analysis::Statistical),
116 },
117 ProcessingStep {
118 name: "expand".to_string(),
119 operation: Operation::Transform(Transform::Expand),
120 },
121 ],
122 metadata: TemplateMetadata {
123 version: "1.0.0".to_string(),
124 author: "FACT Team".to_string(),
125 created_at: chrono::Utc::now().to_rfc3339(),
126 updated_at: chrono::Utc::now().to_rfc3339(),
127 tags: vec!["analysis".to_string(), "statistics".to_string()],
128 performance: PerformanceProfile {
129 avg_execution_time_ms: 50.0,
130 memory_usage_bytes: 1024 * 1024, complexity: 3,
132 },
133 },
134 });
135
136 self.register(Template {
138 id: "pattern-detection".to_string(),
139 name: "Pattern Detection".to_string(),
140 description: "Detects patterns in structured data".to_string(),
141 steps: vec![
142 ProcessingStep {
143 name: "normalize".to_string(),
144 operation: Operation::Transform(Transform::Normalize),
145 },
146 ProcessingStep {
147 name: "pattern-analysis".to_string(),
148 operation: Operation::Analyze(Analysis::Pattern),
149 },
150 ProcessingStep {
151 name: "semantic-enrichment".to_string(),
152 operation: Operation::Analyze(Analysis::Semantic),
153 },
154 ],
155 metadata: TemplateMetadata {
156 version: "1.0.0".to_string(),
157 author: "FACT Team".to_string(),
158 created_at: chrono::Utc::now().to_rfc3339(),
159 updated_at: chrono::Utc::now().to_rfc3339(),
160 tags: vec!["pattern".to_string(), "detection".to_string(), "ai".to_string()],
161 performance: PerformanceProfile {
162 avg_execution_time_ms: 75.0,
163 memory_usage_bytes: 2 * 1024 * 1024, complexity: 5,
165 },
166 },
167 });
168
169 self.register(Template {
171 id: "data-aggregation".to_string(),
172 name: "Data Aggregation".to_string(),
173 description: "Aggregates numerical data with various operations".to_string(),
174 steps: vec![
175 ProcessingStep {
176 name: "filter-numbers".to_string(),
177 operation: Operation::Filter(Filter::Range { min: 0.0, max: 1000000.0 }),
178 },
179 ProcessingStep {
180 name: "sum".to_string(),
181 operation: Operation::Aggregate(Aggregation::Sum),
182 },
183 ProcessingStep {
184 name: "average".to_string(),
185 operation: Operation::Aggregate(Aggregation::Average),
186 },
187 ProcessingStep {
188 name: "count".to_string(),
189 operation: Operation::Aggregate(Aggregation::Count),
190 },
191 ],
192 metadata: TemplateMetadata {
193 version: "1.0.0".to_string(),
194 author: "FACT Team".to_string(),
195 created_at: chrono::Utc::now().to_rfc3339(),
196 updated_at: chrono::Utc::now().to_rfc3339(),
197 tags: vec!["aggregation".to_string(), "numerical".to_string(), "statistics".to_string()],
198 performance: PerformanceProfile {
199 avg_execution_time_ms: 25.0,
200 memory_usage_bytes: 512 * 1024, complexity: 2,
202 },
203 },
204 });
205
206 self.register(Template {
208 id: "quick-transform".to_string(),
209 name: "Quick Transform".to_string(),
210 description: "Fast data transformation for caching".to_string(),
211 steps: vec![
212 ProcessingStep {
213 name: "compress".to_string(),
214 operation: Operation::Transform(Transform::Compress),
215 },
216 ProcessingStep {
217 name: "normalize".to_string(),
218 operation: Operation::Transform(Transform::Normalize),
219 },
220 ],
221 metadata: TemplateMetadata {
222 version: "1.0.0".to_string(),
223 author: "FACT Team".to_string(),
224 created_at: chrono::Utc::now().to_rfc3339(),
225 updated_at: chrono::Utc::now().to_rfc3339(),
226 tags: vec!["transform".to_string(), "fast".to_string(), "cache".to_string()],
227 performance: PerformanceProfile {
228 avg_execution_time_ms: 10.0,
229 memory_usage_bytes: 256 * 1024, complexity: 1,
231 },
232 },
233 });
234 }
235
236 pub fn search_by_tags(&self, tags: &[String]) -> Vec<Template> {
238 self.templates
239 .read()
240 .values()
241 .filter(|template| {
242 tags.iter().any(|tag| template.metadata.tags.contains(tag))
243 })
244 .cloned()
245 .collect()
246 }
247
248 pub fn get_by_performance(&self, max_complexity: u8) -> Vec<Template> {
250 let mut templates: Vec<_> = self.templates
251 .read()
252 .values()
253 .filter(|t| t.metadata.performance.complexity <= max_complexity)
254 .cloned()
255 .collect();
256
257 templates.sort_by(|a, b| {
258 a.metadata.performance.avg_execution_time_ms
259 .partial_cmp(&b.metadata.performance.avg_execution_time_ms)
260 .unwrap()
261 });
262
263 templates
264 }
265}
266
267impl Default for TemplateRegistry {
268 fn default() -> Self {
269 Self::new()
270 }
271}
272
273pub struct TemplateBuilder {
275 id: String,
276 name: String,
277 description: String,
278 steps: Vec<ProcessingStep>,
279 tags: Vec<String>,
280}
281
282impl TemplateBuilder {
283 pub fn new(id: impl Into<String>) -> Self {
285 Self {
286 id: id.into(),
287 name: String::new(),
288 description: String::new(),
289 steps: Vec::new(),
290 tags: Vec::new(),
291 }
292 }
293
294 pub fn name(mut self, name: impl Into<String>) -> Self {
296 self.name = name.into();
297 self
298 }
299
300 pub fn description(mut self, description: impl Into<String>) -> Self {
302 self.description = description.into();
303 self
304 }
305
306 pub fn add_step(mut self, step: ProcessingStep) -> Self {
308 self.steps.push(step);
309 self
310 }
311
312 pub fn add_tag(mut self, tag: impl Into<String>) -> Self {
314 self.tags.push(tag.into());
315 self
316 }
317
318 pub fn build(self) -> Template {
320 Template {
321 id: self.id,
322 name: self.name,
323 description: self.description,
324 steps: self.steps,
325 metadata: TemplateMetadata {
326 version: "1.0.0".to_string(),
327 author: "Custom".to_string(),
328 created_at: chrono::Utc::now().to_rfc3339(),
329 updated_at: chrono::Utc::now().to_rfc3339(),
330 tags: self.tags,
331 performance: PerformanceProfile {
332 avg_execution_time_ms: 0.0,
333 memory_usage_bytes: 0,
334 complexity: 5,
335 },
336 },
337 }
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn test_template_registry() {
347 let registry = TemplateRegistry::new();
348
349 assert!(registry.get("analysis-basic").is_some());
351 assert!(registry.get("pattern-detection").is_some());
352 assert!(registry.get("data-aggregation").is_some());
353 assert!(registry.get("quick-transform").is_some());
354
355 let templates = registry.list();
357 assert!(templates.len() >= 4);
358 }
359
360 #[test]
361 fn test_template_builder() {
362 let template = TemplateBuilder::new("custom-template")
363 .name("Custom Template")
364 .description("A custom template for testing")
365 .add_tag("custom")
366 .add_tag("test")
367 .add_step(ProcessingStep {
368 name: "normalize".to_string(),
369 operation: Operation::Transform(Transform::Normalize),
370 })
371 .build();
372
373 assert_eq!(template.id, "custom-template");
374 assert_eq!(template.name, "Custom Template");
375 assert_eq!(template.steps.len(), 1);
376 assert_eq!(template.metadata.tags.len(), 2);
377 }
378
379 #[test]
380 fn test_search_by_tags() {
381 let registry = TemplateRegistry::new();
382
383 let analysis_templates = registry.search_by_tags(&[String::from("analysis")]);
384 assert!(!analysis_templates.is_empty());
385
386 let pattern_templates = registry.search_by_tags(&[String::from("pattern")]);
387 assert!(!pattern_templates.is_empty());
388 }
389}