1use std::net::SocketAddr;
4
5use axum::Router as AxumRouter;
6use tower_http::trace::TraceLayer;
7
8use crate::container::{Container, ContainerBuilder};
9use crate::middleware::{install_defaults, MiddlewareRegistry};
10use crate::route::{RouteInfo, Router};
11use crate::server_config::ServerConfig;
12use crate::shutdown::ShutdownHandle;
13
14pub struct Application {
15 pub container: Container,
16 pub registry: MiddlewareRegistry,
17 pub web: AxumRouter<Container>,
18 pub api: AxumRouter<Container>,
19 pub shutdown: ShutdownHandle,
20 pub server_config: ServerConfig,
21 routes: Vec<RouteInfo>,
22}
23
24pub struct ApplicationBuilder {
25 container_builder: ContainerBuilder,
26 registry: MiddlewareRegistry,
27 web_routes: Option<Box<dyn FnOnce(Router) -> Router>>,
28 api_routes: Option<Box<dyn FnOnce(Router) -> Router>>,
29 server_config: ServerConfig,
30}
31
32impl Default for ApplicationBuilder {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38impl ApplicationBuilder {
39 pub fn new() -> Self {
40 let registry = MiddlewareRegistry::new();
41 install_defaults(®istry);
42 Self {
43 container_builder: ContainerBuilder::from_env(),
44 registry,
45 web_routes: None,
46 api_routes: None,
47 server_config: ServerConfig::default().apply_env_overrides(),
48 }
49 }
50
51 pub fn container<F>(mut self, configure: F) -> Self
52 where
53 F: FnOnce(ContainerBuilder) -> ContainerBuilder,
54 {
55 self.container_builder = configure(self.container_builder);
56 self
57 }
58
59 pub fn middleware<F>(self, configure: F) -> Self
60 where
61 F: FnOnce(&MiddlewareRegistry),
62 {
63 configure(&self.registry);
64 self
65 }
66
67 pub fn web<F>(mut self, build: F) -> Self
68 where
69 F: FnOnce(Router) -> Router + 'static,
70 {
71 self.web_routes = Some(Box::new(build));
72 self
73 }
74
75 pub fn api<F>(mut self, build: F) -> Self
76 where
77 F: FnOnce(Router) -> Router + 'static,
78 {
79 self.api_routes = Some(Box::new(build));
80 self
81 }
82
83 pub fn server_config(mut self, cfg: ServerConfig) -> Self {
86 self.server_config = cfg;
87 self
88 }
89
90 pub fn server_config_file(mut self, path: impl AsRef<std::path::Path>) -> Self {
93 self.server_config = ServerConfig::from_file_or_default(path);
94 self
95 }
96
97 pub fn build(self) -> Application {
98 let container = self.container_builder.build();
99 let registry = self.registry;
100 let server_config = self.server_config;
101
102 let mut all_routes: Vec<RouteInfo> = Vec::new();
103
104 let web_router = self.web_routes.map(|f| {
105 let router = Router::new(registry.clone());
106 let built = f(router);
107 let (axum_router, routes) = built.finish();
108 all_routes.extend(routes);
109 axum_router
110 });
111
112 let api_router = self.api_routes.map(|f| {
113 let router = Router::new(registry.clone()).prefix("/api");
114 let built = f(router);
115 let (axum_router, routes) = built.finish();
116 all_routes.extend(routes);
117 axum_router
118 });
119
120 Application {
121 container,
122 registry,
123 web: web_router.unwrap_or_default(),
124 api: api_router.unwrap_or_default(),
125 shutdown: ShutdownHandle::new(),
126 server_config,
127 routes: all_routes,
128 }
129 }
130}
131
132impl Application {
133 pub fn builder() -> ApplicationBuilder {
134 ApplicationBuilder::new()
135 }
136
137 pub fn routes(&self) -> &[RouteInfo] {
140 &self.routes
141 }
142
143 pub fn into_router(self) -> AxumRouter {
147 let cfg = self.server_config.clone();
148 let container_for_mw = self.container.clone();
149 let combined = self.web.merge(self.api);
150 let combined = crate::server::apply_layers(combined, &cfg);
151 combined
152 .layer(axum::middleware::from_fn(
156 move |req: axum::http::Request<axum::body::Body>, next: axum::middleware::Next| {
157 let c = container_for_mw.clone();
158 async move { crate::middleware::inject_container_mw(c, req, next).await }
159 },
160 ))
161 .layer(TraceLayer::new_for_http())
162 .with_state(self.container.clone())
163 }
164
165 pub async fn run(self) -> Result<(), crate::Error> {
171 let shutdown_handle = self.shutdown.clone().install();
172 let cfg = self.server_config.clone();
173 let container = self.container.clone();
174 let container_for_mw = container.clone();
175 let combined = self.web.merge(self.api);
176 let layered = crate::server::apply_layers(combined, &cfg)
177 .layer(axum::middleware::from_fn(
180 move |req: axum::http::Request<axum::body::Body>, next: axum::middleware::Next| {
181 let c = container_for_mw.clone();
182 async move { crate::middleware::inject_container_mw(c, req, next).await }
183 },
184 ))
185 .layer(TraceLayer::new_for_http())
186 .with_state(container);
187
188 let (tx, rx) = tokio::sync::oneshot::channel::<()>();
189 tokio::spawn(async move {
190 shutdown_handle.wait().await;
191 let _ = tx.send(());
192 });
193
194 crate::server::serve(layered, &cfg, rx).await
195 }
196
197 pub async fn serve(self, addr: SocketAddr) -> Result<(), crate::Error> {
200 let mut cfg = self.server_config.clone();
201 cfg.bind = addr.to_string();
202 cfg.tls = None;
203 let app_with_cfg = Application {
204 server_config: cfg,
205 ..self
206 };
207 app_with_cfg.run().await
208 }
209}