1use crate::di::Container;
6use crate::error::Result;
7use axum::Router;
8use std::net::SocketAddr;
9
10pub struct App {
27 container: Container,
28 router: Router,
29}
30
31impl App {
32 pub fn new() -> Self {
34 Self {
35 container: Container::new(),
36 router: Router::new(),
37 }
38 }
39
40 pub fn container(&self) -> &Container {
42 &self.container
43 }
44
45 pub fn container_mut(&mut self) -> &mut Container {
47 &mut self.container
48 }
49
50 pub fn router(&self) -> &Router {
52 &self.router
53 }
54
55 pub fn build(self) -> Router {
57 self.router
58 }
59
60 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 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 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}