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