use std::net::SocketAddr;
use axum::Router;
use crate::{di::Container, error::Result};
pub struct App {
container: Container,
router: Router,
}
impl App {
pub fn new() -> Self {
Self {
container: Container::new(),
router: Router::new(),
}
}
pub fn container(&self) -> &Container {
&self.container
}
pub fn container_mut(&mut self) -> &mut Container {
&mut self.container
}
pub fn router(&self) -> &Router {
&self.router
}
pub fn build(self) -> Router {
self.router
}
pub async fn serve(self, addr: impl Into<SocketAddr>) -> Result<()> {
let addr = addr.into();
let listener = self.create_listener_at(addr).await?;
let router = self.router;
Self::run_server_on(listener, router).await
}
async fn create_listener_at(&self, addr: SocketAddr) -> Result<tokio::net::TcpListener> {
tokio::net::TcpListener::bind(addr).await.map_err(|e| {
crate::error::Error::server_error(format!("Failed to bind to {}: {}", addr, e))
})
}
async fn run_server_on(listener: tokio::net::TcpListener, router: Router) -> Result<()> {
let addr = listener.local_addr().unwrap();
tracing::info!("Server running on http://{}", addr);
axum::serve(listener, router)
.await
.map_err(|e| crate::error::Error::server_error(format!("Server error: {}", e)))
}
}
impl Default for App {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_app_creation() {
let app = App::new();
assert!(app.container().is_empty());
}
#[test]
fn test_app_default() {
let app = App::default();
assert!(app.container().is_empty());
}
}