Skip to main content

serverless_fn/
server.rs

1//! Server module for running serverless functions as HTTP services.
2
3use std::future::Future;
4use std::sync::Arc;
5
6use futures::future::BoxFuture;
7
8use crate::error::ServerlessError;
9
10/// Function handler type.
11pub type FunctionHandler =
12    Arc<dyn Fn(Vec<u8>) -> BoxFuture<'static, Result<Vec<u8>, ServerlessError>> + Send + Sync>;
13
14/// Unified trait for registering serverless functions.
15///
16/// Implementations should register themselves with the appropriate server type.
17pub trait FunctionRegistry: Send + Sync + 'static {
18    /// Returns the function name.
19    fn function_name(&self) -> &'static str;
20
21    /// Returns the function path (for HTTP routing).
22    fn function_path(&self) -> &'static str {
23        self.function_name()
24    }
25
26    /// Registers the function handler with the server.
27    fn register(&self, server: &mut FunctionServer);
28}
29
30// Collect all FunctionRegistry implementations
31inventory::collect!(&'static dyn FunctionRegistry);
32
33/// Server configuration.
34#[derive(Debug, Clone)]
35pub struct ServerConfig {
36    /// Host address to bind to.
37    pub host: String,
38    /// Port to listen on.
39    pub port: u16,
40}
41
42impl Default for ServerConfig {
43    fn default() -> Self {
44        Self {
45            host: "127.0.0.1".to_string(),
46            port: 3000,
47        }
48    }
49}
50
51impl ServerConfig {
52    /// Creates a new server configuration with default values.
53    #[must_use]
54    pub fn new() -> Self {
55        Self::default()
56    }
57
58    /// Sets the host address.
59    #[must_use]
60    pub fn host(mut self, host: &str) -> Self {
61        self.host = host.to_string();
62        self
63    }
64
65    /// Sets the port.
66    #[must_use]
67    pub fn port(mut self, port: u16) -> Self {
68        self.port = port;
69        self
70    }
71}
72
73/// Unified server for hosting serverless functions.
74///
75/// # Examples
76///
77/// ```rust,no_run
78/// use serverless_fn::server::{FunctionServer, ServerConfig};
79///
80/// #[tokio::main]
81/// async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
82///     // Method 1: Using builder pattern
83///     FunctionServer::new()
84///         .host("0.0.0.0")
85///         .port(3000)
86///         .start()
87///         .await?;
88///
89///     // Method 2: Using custom config
90///     let config = ServerConfig::new().host("0.0.0.0").port(3000);
91///     FunctionServer::with_config(config)
92///         .start()
93///         .await?;
94///
95///     Ok(())
96/// }
97/// ```
98pub struct FunctionServer {
99    config: ServerConfig,
100    router: Option<axum::Router>,
101}
102
103impl Default for FunctionServer {
104    fn default() -> Self {
105        Self::new()
106    }
107}
108
109impl FunctionServer {
110    /// Creates a new server with default configuration.
111    #[must_use]
112    pub fn new() -> Self {
113        Self {
114            config: ServerConfig::default(),
115            router: Some(axum::Router::new()),
116        }
117    }
118
119    /// Creates a new server with custom configuration.
120    #[must_use]
121    pub fn with_config(config: ServerConfig) -> Self {
122        Self {
123            config,
124            router: Some(axum::Router::new()),
125        }
126    }
127
128    /// Sets the host address to bind to.
129    #[must_use]
130    pub fn host(mut self, host: &str) -> Self {
131        self.config.host = host.to_string();
132        self
133    }
134
135    /// Sets the port to listen on.
136    #[must_use]
137    pub fn port(mut self, port: u16) -> Self {
138        self.config.port = port;
139        self
140    }
141
142    /// Returns a reference to the server configuration.
143    #[must_use]
144    pub fn config(&self) -> &ServerConfig {
145        &self.config
146    }
147
148    /// Registers a function for HTTP transport.
149    pub fn register_http_route<F, T>(&mut self, path: &str, handler: F)
150    where
151        F: axum::handler::Handler<T, ()> + Send + Sync + 'static,
152        T: 'static,
153    {
154        if let Some(ref mut router) = self.router {
155            let old_router = std::mem::replace(router, axum::Router::new());
156            *router = old_router.route(path, axum::routing::post(handler));
157        }
158    }
159
160    /// Starts the server and listens for incoming requests.
161    ///
162    /// Automatically discovers and registers all serverless functions
163    /// using the inventory crate.
164    ///
165    /// # Errors
166    ///
167    /// Returns an error if the server fails to start.
168    pub async fn start(mut self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
169        // Register all functions from inventory
170        for registry in inventory::iter::<&'static dyn FunctionRegistry> {
171            registry.register(&mut self);
172        }
173
174        self.serve().await
175    }
176
177    /// Starts the server with graceful shutdown support.
178    ///
179    /// # Errors
180    ///
181    /// Returns an error if the server fails to start.
182    pub async fn start_with_graceful_shutdown<F>(
183        mut self,
184        shutdown_signal: F,
185    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
186    where
187        F: Future<Output = ()> + Send + 'static + Clone,
188    {
189        // Register all functions from inventory
190        for registry in inventory::iter::<&'static dyn FunctionRegistry> {
191            registry.register(&mut self);
192        }
193
194        self.serve_with_shutdown(shutdown_signal).await
195    }
196
197    /// Internal method to serve requests based on enabled features.
198    async fn serve(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
199        if let Some(ref router) = self.router {
200            let listener =
201                tokio::net::TcpListener::bind((self.config.host.as_str(), self.config.port))
202                    .await?;
203            return axum::serve(listener, router.clone())
204                .await
205                .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { Box::new(e) });
206        }
207
208        Ok(())
209    }
210
211    /// Internal method to serve requests with graceful shutdown.
212    async fn serve_with_shutdown<F>(
213        &self,
214        shutdown_signal: F,
215    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
216    where
217        F: Future<Output = ()> + Send + 'static,
218    {
219        if let Some(ref router) = self.router {
220            let listener =
221                tokio::net::TcpListener::bind((self.config.host.as_str(), self.config.port))
222                    .await?;
223            return axum::serve(listener, router.clone())
224                .with_graceful_shutdown(shutdown_signal)
225                .await
226                .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { Box::new(e) });
227        }
228
229        Ok(())
230    }
231}
232
233// Re-export for backward compatibility
234pub use FunctionServer as Server;