Skip to main content

rust_api/
app.rs

1//! Application builder for rust-api framework
2//!
3//! Provides an ergonomic API for constructing and configuring REST applications.
4
5use crate::di::Container;
6use crate::error::Result;
7use axum::Router;
8use std::net::SocketAddr;
9
10/// Application builder for rust-api framework
11///
12/// Provides a fluent API for:
13/// - Registering services in the DI container
14/// - Adding routes to the application
15/// - Configuring middleware
16/// - Starting the HTTP server
17///
18/// # Example
19///
20/// ```ignore
21/// let app = App::new()
22///     .service::<DatabaseService>()
23///     .service::<UserService>()
24///     .build();
25/// ```
26pub struct App {
27    container: Container,
28    router: Router,
29}
30
31impl App {
32    /// Create a new application builder
33    pub fn new() -> Self {
34        Self {
35            container: Container::new(),
36            router: Router::new(),
37        }
38    }
39
40    /// Get a reference to the DI container
41    pub fn container(&self) -> &Container {
42        &self.container
43    }
44
45    /// Get a mutable reference to the DI container
46    pub fn container_mut(&mut self) -> &mut Container {
47        &mut self.container
48    }
49
50    /// Get a reference to the router
51    pub fn router(&self) -> &Router {
52        &self.router
53    }
54
55    /// Build and return the configured router
56    pub fn build(self) -> Router {
57        self.router
58    }
59
60    /// Start the HTTP server on the given address
61    ///
62    /// # Example
63    ///
64    /// ```ignore
65    /// app.serve("0.0.0.0:3000").await?;
66    /// ```
67    pub async fn serve(self, addr: impl Into<SocketAddr>) -> Result<()> {
68        let addr = addr.into();
69        let listener = self.create_listener_at(addr).await?;
70        let router = self.router;
71        Self::run_server_on(listener, router).await
72    }
73
74    //create a TCP listener on the given address
75    async fn create_listener_at(&self, addr: SocketAddr) -> Result<tokio::net::TcpListener> {
76        tokio::net::TcpListener::bind(addr)
77            .await
78            .map_err(|e| crate::error::Error::server_error(format!("Failed to bind to {}: {}", addr, e)))
79    }
80
81    //run the axum server with the given listener and router
82    async fn run_server_on(listener: tokio::net::TcpListener, router: Router) -> Result<()> {
83        let addr = listener.local_addr().unwrap();
84        tracing::info!("Server running on http://{}", addr);
85
86        axum::serve(listener, router)
87            .await
88            .map_err(|e| crate::error::Error::server_error(format!("Server error: {}", e)))
89    }
90}
91
92impl Default for App {
93    fn default() -> Self {
94        Self::new()
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_app_creation() {
104        let app = App::new();
105        assert!(app.container().is_empty());
106    }
107
108    #[test]
109    fn test_app_default() {
110        let app = App::default();
111        assert!(app.container().is_empty());
112    }
113}