modulink_rs/listeners/
http_listener.rs

1
2
3
4
5use axum::{Router, routing::post, extract::State, Json};
6use crate::context::Context;
7use crate::listeners::BaseListenerAsync;
8use std::sync::Arc;
9use std::net::SocketAddr;
10use async_trait::async_trait;
11
12
13
14/// Default ergonomic HTTP listener for modulink-rust using axum.
15/// Accepts a handler (chain) and address.
16pub struct HttpListener {
17    pub handler: Arc<dyn Fn(Context) -> std::pin::Pin<Box<dyn std::future::Future<Output = Context> + Send>> + Send + Sync>,
18    pub addr: String,
19}
20
21
22
23
24#[async_trait]
25impl BaseListenerAsync for HttpListener {
26    async fn start(&self) -> std::io::Result<()> {
27        let handler = self.handler.clone();
28        let addr: SocketAddr = self.addr.parse().expect("Invalid address");
29
30        // Axum handler closure
31        let handler_clone = handler.clone();
32        let app = Router::new()
33            .route("/run", post(
34                move |State(handler): State<Arc<dyn Fn(Context) -> std::pin::Pin<Box<dyn std::future::Future<Output = Context> + Send>> + Send + Sync>>, Json(body): Json<serde_json::Value>| {
35                    let handler = handler.clone();
36                    async move {
37                        let map = body.as_object().cloned().unwrap_or_default();
38                        let ctx = Context(map.into_iter().collect());
39                        let result = handler(ctx).await;
40                        // Convert HashMap to serde_json::Map for correct JSON response
41                        let map: serde_json::Map<String, serde_json::Value> = result.0.into_iter().collect();
42                        Json(serde_json::Value::Object(map))
43                    }
44                }
45            ))
46            .with_state(handler_clone);
47
48        // Use axum::serve (hyper::Server)
49        use axum::serve;
50        use tokio::net::TcpListener;
51        let listener = TcpListener::bind(addr).await.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
52        serve(listener, app.into_make_service()).await.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
53    }
54    fn name(&self) -> &'static str {
55        "http"
56    }
57}