rohas_runtime/
executor.rs

1use crate::error::{Result, RuntimeError};
2use crate::handler::{Handler, HandlerContext, HandlerResult};
3use crate::node_runtime::NodeRuntime;
4use crate::python_runtime::PythonRuntime;
5use crate::rust_runtime::RustRuntime;
6use crate::{Language, RuntimeConfig};
7use rohas_codegen::templates;
8use std::collections::HashMap;
9use std::path::PathBuf;
10use std::sync::Arc;
11use tokio::sync::RwLock;
12use tracing::{debug, info};
13
14pub struct Executor {
15    config: RuntimeConfig,
16    handlers: Arc<RwLock<HashMap<String, Arc<dyn Handler>>>>,
17    python_runtime: Arc<PythonRuntime>,
18    node_runtime: Arc<NodeRuntime>,
19    rust_runtime: Arc<RustRuntime>,
20}
21
22impl Executor {
23    pub fn new(config: RuntimeConfig) -> Self {
24        let mut python_runtime = PythonRuntime::new().expect("Failed to initialize Python runtime");
25        python_runtime.set_project_root(config.project_root.clone());
26        let python_runtime = Arc::new(python_runtime);
27
28        let mut node_runtime = NodeRuntime::new().expect("Failed to initialize Node.js runtime");
29        node_runtime.set_project_root(config.project_root.clone());
30        let node_runtime = Arc::new(node_runtime);
31
32        let mut rust_runtime = RustRuntime::new().expect("Failed to initialize Rust runtime");
33        rust_runtime.set_project_root(config.project_root.clone());
34        let rust_runtime = Arc::new(rust_runtime);
35
36        info!("Executor initialized with Python, Node.js, and Rust runtimes");
37
38        let executor = Self {
39            config: config.clone(),
40            handlers: Arc::new(RwLock::new(HashMap::new())),
41            python_runtime,
42            node_runtime,
43            rust_runtime: rust_runtime.clone(),
44        };
45
46        executor
47    }
48
49
50    pub async fn register_handler(&self, handler: Arc<dyn Handler>) {
51        let name = handler.name().to_string();
52        let mut handlers = self.handlers.write().await;
53        handlers.insert(name.clone(), handler);
54        info!("Registered handler: {}", name);
55    }
56
57    pub async fn execute(
58        &self,
59        handler_name: &str,
60        payload: serde_json::Value,
61    ) -> Result<HandlerResult> {
62        self.execute_with_params(handler_name, payload, HashMap::new())
63            .await
64    }
65
66    pub async fn execute_with_params(
67        &self,
68        handler_name: &str,
69        payload: serde_json::Value,
70        query_params: HashMap<String, String>,
71    ) -> Result<HandlerResult> {
72        debug!("Executing handler: {}", handler_name);
73
74        let mut context = HandlerContext::new(handler_name, payload);
75        context.query_params = query_params;
76
77        {
78            let handlers = self.handlers.read().await;
79            if let Some(handler) = handlers.get(handler_name) {
80                return handler.execute(context.clone()).await;
81            }
82        }
83
84        self.execute_external_handler(context).await
85    }
86
87    pub async fn execute_with_context(&self, context: HandlerContext) -> Result<HandlerResult> {
88        debug!("Executing handler: {}", context.handler_name);
89
90        {
91            let handlers = self.handlers.read().await;
92            if let Some(handler) = handlers.get(&context.handler_name) {
93                return handler.execute(context.clone()).await;
94            }
95        }
96
97        self.execute_external_handler(context).await
98    }
99
100    async fn execute_external_handler(&self, context: HandlerContext) -> Result<HandlerResult> {
101        let start = std::time::Instant::now();
102
103        let handler_path = self.resolve_handler_path(&context.handler_name)?;
104
105        let result = match self.config.language {
106            Language::TypeScript => self.execute_typescript(&handler_path, &context).await,
107            Language::Python => self.execute_python(&handler_path, &context).await,
108            Language::Rust => self.execute_rust(&handler_path, &context).await,
109        };
110
111        let execution_time_ms = start.elapsed().as_millis() as u64;
112
113        match result {
114            Ok(mut res) => {
115                res.execution_time_ms = execution_time_ms;
116                Ok(res)
117            }
118            Err(e) => Ok(HandlerResult::error(e.to_string(), execution_time_ms)),
119        }
120    }
121
122    fn resolve_handler_path(&self, handler_name: &str) -> Result<PathBuf> {
123        let handlers_dir = self.config.project_root.join("src/handlers");
124        let middlewares_dir = self.config.project_root.join("src/middlewares");
125
126        let snake_case_name = templates::to_snake_case(handler_name);
127
128        let possible_paths = [
129            middlewares_dir.join(format!(
130                "{}.{}",
131                snake_case_name,
132                self.config.language.file_extension()
133            )),
134            middlewares_dir.join(format!(
135                "{}.{}",
136                handler_name,
137                self.config.language.file_extension()
138            )),
139            handlers_dir.join(format!(
140                "api/{}.{}",
141                handler_name,
142                self.config.language.file_extension()
143            )),
144            handlers_dir.join(format!(
145                "events/{}.{}",
146                handler_name,
147                self.config.language.file_extension()
148            )),
149            handlers_dir.join(format!(
150                "websockets/{}.{}",
151                handler_name,
152                self.config.language.file_extension()
153            )),
154            handlers_dir.join(format!(
155                "cron/{}.{}",
156                snake_case_name,
157                self.config.language.file_extension()
158            )),
159            handlers_dir.join(format!(
160                "cron/{}.{}",
161                handler_name,
162                self.config.language.file_extension()
163            )),
164            handlers_dir.join(format!(
165                "{}.{}",
166                handler_name,
167                self.config.language.file_extension()
168            )),
169        ];
170
171        for path in &possible_paths {
172            if path.exists() {
173                return Ok(path.clone());
174            }
175        }
176
177        Err(RuntimeError::HandlerNotFound(format!(
178            "Handler '{}' not found in any handlers directory",
179            handler_name
180        )))
181    }
182
183    async fn execute_typescript(
184        &self,
185        handler_path: &PathBuf,
186        context: &HandlerContext,
187    ) -> Result<HandlerResult> {
188        debug!(
189            "Executing TypeScript handler via Node.js runtime: {:?}",
190            handler_path
191        );
192        self.node_runtime
193            .execute_handler(handler_path, context.clone())
194            .await
195    }
196
197    async fn execute_python(
198        &self,
199        handler_path: &PathBuf,
200        context: &HandlerContext,
201    ) -> Result<HandlerResult> {
202        debug!("Executing Python handler via pyo3: {:?}", handler_path);
203        self.python_runtime
204            .execute_handler(handler_path, context.clone())
205            .await
206    }
207
208    async fn execute_rust(
209        &self,
210        handler_path: &PathBuf,
211        context: &HandlerContext,
212    ) -> Result<HandlerResult> {
213        debug!("Executing Rust handler (high-performance): {:?}", handler_path);
214        self.rust_runtime
215            .execute_handler(handler_path, context.clone())
216            .await
217    }
218
219    pub async fn list_handlers(&self) -> Vec<String> {
220        let handlers = self.handlers.read().await;
221        handlers.keys().cloned().collect()
222    }
223
224    pub async fn reload_python_module(&self, module_name: &str) -> Result<()> {
225        self.python_runtime.reload_module(module_name).await
226    }
227
228    pub async fn reload_node_module(&self, module_name: &str) -> Result<()> {
229        self.node_runtime.reload_module(module_name).await
230    }
231
232    pub async fn clear_handler_cache(&self) -> Result<()> {
233        match self.config.language {
234            Language::TypeScript => {
235                self.node_runtime.clear_cache().await?;
236            }
237            Language::Python => {
238                // @TODO Python runtime doesn't cache modules the same way
239                // Module reloading is handled differently in pyo3
240            }
241            Language::Rust => {
242                self.rust_runtime.clear_handlers().await;
243            }
244        }
245        Ok(())
246    }
247
248    /// Get a reference to the Rust runtime for direct handler registration.
249    /// 
250    /// This allows static handler registration:
251    /// ```rust
252    /// use rohas_runtime::{Executor, RuntimeConfig, HandlerContext, HandlerResult};
253    ///
254    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
255    /// let executor = Executor::new(RuntimeConfig::default());
256    ///
257    /// async fn my_handler_fn(ctx: HandlerContext) -> rohas_runtime::error::Result<HandlerResult> {
258    ///     Ok(HandlerResult::success(serde_json::json!({}), 0))
259    /// }
260    ///
261    /// executor.rust_runtime().register_handler("my_handler".to_string(), my_handler_fn).await;
262    /// # Ok(())
263    /// # }
264    /// ```
265    pub fn rust_runtime(&self) -> &Arc<RustRuntime> {
266        &self.rust_runtime
267    }
268}
269
270#[cfg(test)]
271mod tests {
272    use super::*;
273
274    struct TestHandler {
275        name: String,
276    }
277
278    #[async_trait::async_trait]
279    impl Handler for TestHandler {
280        async fn execute(&self, _context: HandlerContext) -> Result<HandlerResult> {
281            Ok(HandlerResult::success(
282                serde_json::json!({"message": "test"}),
283                0,
284            ))
285        }
286
287        fn name(&self) -> &str {
288            &self.name
289        }
290    }
291
292    #[tokio::test]
293    async fn test_register_and_execute_handler() {
294        let config = RuntimeConfig::default();
295        let executor = Executor::new(config);
296
297        let handler = Arc::new(TestHandler {
298            name: "test_handler".to_string(),
299        });
300
301        executor.register_handler(handler).await;
302
303        let handlers = executor.list_handlers().await;
304        assert_eq!(handlers.len(), 1);
305        assert_eq!(handlers[0], "test_handler");
306
307        let result = executor
308            .execute("test_handler", serde_json::json!({}))
309            .await
310            .unwrap();
311
312        assert!(result.success);
313    }
314}