kotoba_handler/
handler.rs

1//! Unified Handler implementation
2
3use crate::error::{HandlerError, Result};
4use crate::types::{HandlerContext, HandlerResult, HandlerConfig, ExecutionMode};
5use kotoba_core::prelude::*;
6use kotoba_storage::KeyValueStore;
7use std::collections::HashMap;
8use std::sync::Arc;
9use tokio::sync::RwLock;
10
11// Optional dependencies - will be enabled based on features
12#[cfg(feature = "kotobas")]
13use kotoba_kotobas::prelude::*;
14
15#[cfg(feature = "tsx")]
16use kotoba2tsx::prelude::*;
17
18/// Unified Handler for Kotoba ecosystem with KeyValueStore backend
19#[derive(Clone)]
20pub struct UnifiedHandler<T: KeyValueStore + 'static> {
21    config: Arc<RwLock<HandlerConfig>>,
22    storage: Arc<T>,
23    #[cfg(feature = "kotobas")]
24    kotobas_compiler: Arc<KotobasCompiler>,
25    #[cfg(feature = "tsx")]
26    tsx_converter: Arc<TsxConverter>,
27    cache: Arc<RwLock<HashMap<String, HandlerResult>>>,
28}
29
30impl<T: KeyValueStore + 'static> UnifiedHandler<T> {
31    /// Create new unified handler with KeyValueStore backend
32    pub fn new(storage: Arc<T>) -> Self {
33        Self {
34            config: Arc::new(RwLock::new(HandlerConfig {
35                timeout_ms: 30000,
36                max_memory_mb: 100,
37                enable_caching: true,
38                enable_logging: true,
39            })),
40            storage,
41            #[cfg(feature = "kotobas")]
42            kotobas_compiler: Arc::new(KotobasCompiler::new()),
43            #[cfg(feature = "tsx")]
44            tsx_converter: Arc::new(TsxConverter::new()),
45            cache: Arc::new(RwLock::new(HashMap::new())),
46        }
47    }
48
49    /// Execute handler with given content and context
50    pub async fn execute(&self, content: &str, context: HandlerContext) -> Result<String> {
51        // Check cache first
52        if self.config.read().await.enable_caching {
53            let cache_key = self.generate_cache_key(content, &context);
54            if let Some(cached_result) = self.cache.read().await.get(&cache_key) {
55                if self.config.read().await.enable_logging {
56                    println!("🔍 Cache hit for key: {}", cache_key);
57                }
58                return Ok(cached_result.body.clone());
59            }
60        }
61
62        // Parse and validate content
63        #[cfg(feature = "kotobas")]
64        let parsed = self.kotobas_compiler.compile(content)
65            .map_err(|e| HandlerError::Parse(format!("Failed to parse content: {}", e)))?;
66
67        #[cfg(not(feature = "kotobas"))]
68        let parsed = content.to_string(); // Fallback to raw content
69
70        // Convert to executable format (TSX/React)
71        #[cfg(feature = "tsx")]
72        let executable_code = self.tsx_converter.convert(&parsed)
73            .map_err(|e| HandlerError::Execution(format!("Failed to convert to TSX: {}", e)))?;
74
75        #[cfg(not(feature = "tsx"))]
76        let executable_code = parsed; // Use parsed content directly
77
78        // Store compiled result in KeyValueStore for caching
79        let compile_key = format!("compile:{}", self.generate_cache_key(content, &context));
80        self.storage.put(compile_key.as_bytes(), executable_code.as_bytes()).await
81            .map_err(|e| HandlerError::Storage(format!("Failed to store compiled result: {}", e)))?;
82
83        // Execute with context
84        let result = self.execute_with_context(&executable_code, context.clone()).await?;
85
86        // Cache result
87        if self.config.read().await.enable_caching {
88            let cache_key = self.generate_cache_key(content, &context);
89            let handler_result = HandlerResult {
90                status_code: 200,
91                headers: HashMap::new(),
92                body: result.clone(),
93                execution_time_ms: 0, // TODO: measure execution time
94                memory_used_mb: 0.0,  // TODO: measure memory usage
95            };
96            self.cache.write().await.insert(cache_key, handler_result);
97        }
98
99        Ok(result)
100    }
101
102    /// Execute handler with file input
103    pub async fn execute_file(&self, file_path: &str, context: HandlerContext) -> Result<String> {
104        let content = std::fs::read_to_string(file_path)
105            .map_err(|e| HandlerError::Io(e))?;
106
107        self.execute(&content, context).await
108    }
109
110    /// Update handler configuration
111    pub async fn update_config(&self, config: HandlerConfig) {
112        *self.config.write().await = config;
113    }
114
115    /// Get current handler configuration
116    pub async fn get_config(&self) -> HandlerConfig {
117        self.config.read().await.clone()
118    }
119
120    /// Clear cache
121    pub async fn clear_cache(&self) {
122        self.cache.write().await.clear();
123    }
124
125    /// Get cache size
126    pub async fn cache_size(&self) -> usize {
127        self.cache.read().await.len()
128    }
129
130    // Private methods
131
132    fn generate_cache_key(&self, content: &str, context: &HandlerContext) -> String {
133        use std::collections::hash_map::DefaultHasher;
134        use std::hash::{Hash, Hasher};
135
136        let mut hasher = DefaultHasher::new();
137        content.hash(&mut hasher);
138        context.method.hash(&mut hasher);
139        context.path.hash(&mut hasher);
140
141        format!("{:x}", hasher.finish())
142    }
143
144    async fn execute_with_context(&self, tsx_code: &str, context: HandlerContext) -> Result<String> {
145        // This is a simplified execution - in real implementation,
146        // this would use a JavaScript runtime or WASM execution
147
148        if self.config.read().await.enable_logging {
149            println!("🚀 Executing TSX code with context: {:?}", context);
150        }
151
152        // For now, return a placeholder response
153        // In real implementation, this would execute the TSX code
154        // with the provided context and return the rendered result
155        Ok(format!(
156            r#"<!DOCTYPE html>
157<html>
158<head>
159    <title>Kotoba Handler Result</title>
160    <meta charset="UTF-8">
161</head>
162<body>
163    <div id="kotoba-root">
164        <h1>Kotoba Handler Executed</h1>
165        <p>Method: {}</p>
166        <p>Path: {}</p>
167        <p>Content Length: {}</p>
168        <pre>{}</pre>
169    </div>
170</body>
171</html>"#,
172            context.method,
173            context.path,
174            tsx_code.len(),
175            tsx_code
176        ))
177    }
178}
179
180impl<T: KeyValueStore + 'static> Default for UnifiedHandler<T> {
181    fn default() -> Self {
182        Self::new(todo!("Default KeyValueStore implementation needed"))
183    }
184}