nestforge_http/
factory.rs1use std::net::SocketAddr;
2use std::sync::Arc;
3
4use anyhow::Result;
5use axum::Router;
6use axum::middleware::from_fn;
7use nestforge_core::{
8 execute_pipeline, framework_log, initialize_module_graph, Container, Guard, Interceptor,
9 ModuleDefinition,
10};
11use tower_http::trace::TraceLayer;
12
13pub struct NestForgeFactory<M: ModuleDefinition> {
25 _marker: std::marker::PhantomData<M>,
26 container: Container,
27 controllers: Vec<Router<Container>>,
28 global_prefix: Option<String>,
29 version: Option<String>,
30 global_guards: Vec<Arc<dyn Guard>>,
31 global_interceptors: Vec<Arc<dyn Interceptor>>,
32}
33
34impl<M: ModuleDefinition> NestForgeFactory<M> {
35 pub fn create() -> Result<Self> {
36 let container = Container::new();
37 let controllers = initialize_module_graph::<M>(&container)?;
38
39 Ok(Self {
40 _marker: std::marker::PhantomData,
41 container,
42 controllers,
43 global_prefix: None,
44 version: None,
45 global_guards: Vec::new(),
46 global_interceptors: Vec::new(),
47 })
48 }
49
50 pub fn with_global_prefix(mut self, prefix: impl Into<String>) -> Self {
51 let prefix = prefix.into().trim().trim_matches('/').to_string();
52 if !prefix.is_empty() {
53 framework_log(format!("Using global route prefix '{}'.", prefix));
54 self.global_prefix = Some(prefix);
55 }
56 self
57 }
58
59 pub fn with_version(mut self, version: impl Into<String>) -> Self {
60 let version = version.into().trim().trim_matches('/').to_string();
61 if !version.is_empty() {
62 framework_log(format!("Using api version '{}'.", version));
63 self.version = Some(version);
64 }
65 self
66 }
67
68 pub fn use_guard<G>(mut self) -> Self
69 where
70 G: Guard + Default,
71 {
72 framework_log(format!(
73 "Registering guard {}.",
74 std::any::type_name::<G>()
75 ));
76 self.global_guards.push(Arc::new(G::default()));
77 self
78 }
79
80 pub fn use_interceptor<I>(mut self) -> Self
81 where
82 I: Interceptor + Default,
83 {
84 framework_log(format!(
85 "Registering interceptor {}.",
86 std::any::type_name::<I>()
87 ));
88 self.global_interceptors.push(Arc::new(I::default()));
89 self
90 }
91
92 pub async fn listen(self, port: u16) -> Result<()> {
93 let mut app: Router<Container> = Router::new();
98
99 for controller_router in self.controllers {
103 app = app.merge(controller_router);
104 }
105
106 if let Some(version) = &self.version {
107 app = Router::new().nest(&format!("/{}", version), app);
108 }
109
110 if let Some(prefix) = &self.global_prefix {
111 app = Router::new().nest(&format!("/{}", prefix), app);
112 }
113
114 let global_guards = Arc::new(self.global_guards);
115 let global_interceptors = Arc::new(self.global_interceptors);
116
117 let app = app.route_layer(from_fn(move |req, next| {
118 let guards = Arc::clone(&global_guards);
119 let interceptors = Arc::clone(&global_interceptors);
120 async move { execute_pipeline(req, next, guards, interceptors).await }
121 }));
122
123 let app = app
124 .with_state(self.container.clone())
125 .layer(TraceLayer::new_for_http());
126
127 let addr = SocketAddr::from(([127, 0, 0, 1], port));
128 let listener = tokio::net::TcpListener::bind(addr).await?;
129
130 framework_log(format!("Listening on {}.", addr));
131
132 axum::serve(listener, app).await?;
133 Ok(())
134 }
135}