kotoba_handler/
handler.rs1use 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#[cfg(feature = "kotobas")]
13use kotoba_kotobas::prelude::*;
14
15#[cfg(feature = "tsx")]
16use kotoba2tsx::prelude::*;
17
18#[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 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 pub async fn execute(&self, content: &str, context: HandlerContext) -> Result<String> {
51 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 #[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(); #[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; 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 let result = self.execute_with_context(&executable_code, context.clone()).await?;
85
86 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, memory_used_mb: 0.0, };
96 self.cache.write().await.insert(cache_key, handler_result);
97 }
98
99 Ok(result)
100 }
101
102 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 pub async fn update_config(&self, config: HandlerConfig) {
112 *self.config.write().await = config;
113 }
114
115 pub async fn get_config(&self) -> HandlerConfig {
117 self.config.read().await.clone()
118 }
119
120 pub async fn clear_cache(&self) {
122 self.cache.write().await.clear();
123 }
124
125 pub async fn cache_size(&self) -> usize {
127 self.cache.read().await.len()
128 }
129
130 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 if self.config.read().await.enable_logging {
149 println!("🚀 Executing TSX code with context: {:?}", context);
150 }
151
152 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}