1pub mod config;
7pub mod error;
8pub mod extract;
9pub mod logging;
10pub mod routing;
11
12use std::sync::Arc;
13
14use axum::extract::FromRef;
15use axum::{Router, routing::get};
16
17pub use config::{
18 AppConfig, AppSection, DatabaseSection, InertiaSection, PurwaConfigError, ServerSection,
19};
20pub use error::{PurwaError, ValidationErrorBody, flatten_validation_errors};
21pub use extract::{ValidatedForm, ValidatedJson};
22pub use logging::{init_tracing, init_tracing_with_filter};
23pub use routing::{
24 RegisteredRoute, RouteDescriptor, format_route_table, route_descriptors, router_from_inventory,
25};
26pub use sqlx::PgPool;
27
28#[derive(Clone)]
30pub struct AppState {
31 pub config: Arc<AppConfig>,
32 pub db: Arc<PgPool>,
33}
34
35impl AppState {
36 pub fn new(config: Arc<AppConfig>, db: Arc<PgPool>) -> Self {
37 Self { config, db }
38 }
39}
40
41impl FromRef<AppState> for Arc<AppConfig> {
42 fn from_ref(state: &AppState) -> Self {
43 state.config.clone()
44 }
45}
46
47impl FromRef<AppState> for PgPool {
48 fn from_ref(state: &AppState) -> Self {
49 state.db.as_ref().clone()
50 }
51}
52
53pub type AxumRouter = Router;
55
56pub fn app_router() -> AxumRouter {
59 Router::new()
60 .route("/", get(|| async { "Hello, Purwa" }))
61 .merge(router_from_inventory())
62}
63
64#[cfg(test)]
65mod tests {
66 use super::app_router;
67 use axum::body::Body;
68 use axum::http::{Request, StatusCode};
69 use tower::ServiceExt;
70
71 #[tokio::test]
72 async fn root_returns_200() {
73 let app = app_router();
74 let response = app
75 .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
76 .await
77 .unwrap();
78 assert_eq!(response.status(), StatusCode::OK);
79 }
80}