ngyn_shared/core/
engine.rs1use bytes::Bytes;
2use http::Request;
3use matchit::{Match, Router};
4use std::{mem::ManuallyDrop, sync::Arc};
5
6use super::handler::{handler, RouteHandler};
7use crate::{
8 server::{context::AppState, Method, NgynContext, NgynResponse, ToBytes},
9 Middleware, NgynMiddleware,
10};
11
12pub struct GroupRouter<'b> {
13 base_path: &'b str,
14 router: Router<RouteHandler>,
15}
16
17impl RouteInstance for GroupRouter<'_> {
18 fn router_mut(&mut self) -> &mut Router<RouteHandler> {
19 &mut self.router
20 }
21
22 fn mount(&self) -> &str {
23 self.base_path
24 }
25}
26
27#[derive(Default)]
28pub struct PlatformData {
29 router: Router<RouteHandler>,
30 middlewares: Vec<Box<dyn crate::Middleware>>,
31 state: Option<Arc<Box<dyn AppState>>>,
32}
33
34impl PlatformData {
36 pub async fn respond(&self, req: Request<Vec<u8>>) -> NgynResponse {
46 let path = req.method().to_string() + req.uri().path();
47 let mut cx = NgynContext::from_request(req);
48
49 if let Some(state) = &self.state {
50 cx.state = Some(ManuallyDrop::new(state.into()));
51 }
52
53 let mut route_handler = None;
54 let route_info = self.router.at(&path);
55
56 if let Ok(Match { params, value, .. }) = route_info {
57 cx.params = Some(params);
58 route_handler = Some(value);
59 } else {
60 *cx.response_mut().status_mut() = http::StatusCode::NOT_FOUND;
62 }
63
64 for middleware in &self.middlewares {
66 middleware.run(&mut cx).await;
67 }
68
69 if let Some(route_handler) = route_handler {
71 *cx.response_mut().body_mut() = match route_handler {
72 RouteHandler::Sync(handler) => handler(&mut cx),
73 RouteHandler::Async(async_handler) => async_handler(&mut cx).await,
74 }
75 .to_bytes()
76 .into();
77 if cx.request().method() == Method::HEAD {
80 *cx.response_mut().body_mut() = Bytes::default().into();
81 }
82 }
83
84 cx.response
85 }
86
87 pub(self) fn add_middleware(&mut self, middleware: Box<dyn Middleware>) {
93 self.middlewares.push(middleware);
94 }
95}
96
97pub trait NgynPlatform: Default {
98 fn data_mut(&mut self) -> &mut PlatformData;
99}
100
101pub trait RouteInstance {
102 fn router_mut(&mut self) -> &mut Router<RouteHandler>;
103
104 fn mount(&self) -> &str {
106 "/"
107 }
108
109 fn add_route(&mut self, path: &str, method: Option<Method>, handler: RouteHandler) {
117 let method = method
118 .map(|method| method.to_string())
119 .unwrap_or_else(|| "{METHOD}".to_string());
120
121 let route = if path.starts_with('/') {
122 method + path
123 } else {
124 method + self.mount() + path
125 };
126
127 self.router_mut().insert(route, handler).unwrap();
128 }
129}
130
131pub trait NgynHttpPlatform: Default {
132 fn data_mut(&mut self) -> &mut PlatformData;
133}
134
135pub trait NgynHttpEngine: NgynPlatform {
136 fn route(&mut self, path: &str, method: Method, handler: impl Into<RouteHandler>) {
155 self.add_route(path, Some(method), handler.into());
156 }
157
158 fn get(&mut self, path: &str, handler: impl Into<RouteHandler>) {
160 self.route(path, Method::GET, handler.into())
161 }
162
163 fn post(&mut self, path: &str, handler: impl Into<RouteHandler>) {
165 self.route(path, Method::POST, handler.into())
166 }
167
168 fn put(&mut self, path: &str, handler: impl Into<RouteHandler>) {
170 self.route(path, Method::PUT, handler.into())
171 }
172
173 fn delete(&mut self, path: &str, handler: impl Into<RouteHandler>) {
175 self.route(path, Method::DELETE, handler.into())
176 }
177
178 fn patch(&mut self, path: &str, handler: impl Into<RouteHandler>) {
180 self.route(path, Method::PATCH, handler.into())
181 }
182
183 fn head(&mut self, path: &str, handler: impl Into<RouteHandler>) {
185 self.route(path, Method::HEAD, handler.into())
186 }
187
188 fn use_static(&mut self, path_buf: std::path::PathBuf) -> std::io::Result<()> {
202 let assets = include!("statics.rs");
203
204 for (file_path, content) in assets {
205 self.get(&file_path, handler(move |_| Bytes::from(content)));
206 }
207
208 Ok(())
209 }
210}
211
212pub trait NgynEngine: NgynPlatform {
213 fn any(&mut self, path: &str, handler: impl Into<RouteHandler>) {
214 self.add_route(path, None, handler.into());
215 }
216
217 fn group(&mut self, base_path: &str, registry: impl Fn(&mut GroupRouter)) {
219 let mut group = GroupRouter {
220 base_path,
221 router: Router::<RouteHandler>::new(),
222 };
223 registry(&mut group);
224 self.data_mut().router.merge(group.router).unwrap();
225 }
226
227 fn use_middleware(&mut self, middleware: impl NgynMiddleware + 'static) {
233 self.data_mut().add_middleware(Box::new(middleware));
234 }
235
236 fn set_state(&mut self, state: impl AppState + 'static) {
242 self.data_mut().state = Some(Arc::new(Box::new(state)));
243 }
244}
245
246impl<T: NgynHttpPlatform> NgynPlatform for T {
247 fn data_mut(&mut self) -> &mut PlatformData {
248 self.data_mut()
249 }
250}
251
252impl<T: NgynPlatform> NgynEngine for T {}
253impl<T: NgynPlatform> RouteInstance for T {
254 fn router_mut(&mut self) -> &mut Router<RouteHandler> {
255 &mut self.data_mut().router
256 }
257}
258impl<T: NgynHttpPlatform> NgynHttpEngine for T {}
259
260#[cfg(test)]
261mod tests {
262 use http::StatusCode;
263
264 use crate::core::handler::Handler;
265 use std::any::Any;
266
267 use super::*;
268
269 struct MockAppState;
270
271 impl AppState for MockAppState {
272 fn as_any(&self) -> &dyn Any {
273 self
274 }
275
276 fn as_any_mut(&mut self) -> &mut dyn Any {
277 self
278 }
279 }
280
281 struct MockMiddleware;
282
283 impl NgynMiddleware for MockMiddleware {
284 async fn handle(cx: &mut NgynContext<'_>) {
285 *cx.response_mut().status_mut() = StatusCode::OK;
286 }
287 }
288
289 #[derive(Default)]
290 struct MockEngine {
291 data: PlatformData,
292 }
293
294 impl NgynPlatform for MockEngine {
295 fn data_mut(&mut self) -> &mut PlatformData {
296 &mut self.data
297 }
298 }
299
300 #[tokio::test]
301 async fn test_respond_with_state() {
302 let mut engine = MockEngine::default();
303 let app_state = MockAppState;
304 engine.data_mut().state = Some(Arc::new(Box::new(app_state)));
305
306 let req = Request::builder()
307 .method(Method::GET)
308 .uri("/test")
309 .body(Vec::new())
310 .unwrap();
311
312 let res = engine.data.respond(req).await;
313
314 assert_eq!(res.status(), http::StatusCode::NOT_FOUND);
315 }
316
317 #[tokio::test]
318 async fn test_respond_without_state() {
319 let engine = MockEngine::default();
320
321 let req = Request::builder()
322 .method(Method::GET)
323 .uri("/test")
324 .body(Vec::new())
325 .unwrap();
326
327 let res = engine.data.respond(req).await;
328
329 assert_eq!(res.status(), http::StatusCode::NOT_FOUND);
330 }
331
332 #[tokio::test]
333 async fn test_respond_with_middleware() {
334 let mut engine = MockEngine::default();
335 let middleware = MockMiddleware;
336 engine.data_mut().add_middleware(Box::new(middleware));
337
338 let req = Request::builder()
339 .method(Method::GET)
340 .uri("/test")
341 .body(Vec::new())
342 .unwrap();
343
344 let res = engine.data.respond(req).await;
345
346 assert_eq!(res.status(), http::StatusCode::OK);
347 }
348
349 #[tokio::test]
350 async fn test_respond_with_route_handler() {
351 let mut engine = MockEngine::default();
352 let handler: Box<Handler> = Box::new(|_| Box::new(()) as Box<dyn ToBytes>);
353 engine.add_route("/test", Some(Method::GET), RouteHandler::Sync(handler));
354
355 let req = Request::builder()
356 .method(Method::GET)
357 .uri("/test")
358 .body(Vec::new())
359 .unwrap();
360
361 let res = engine.data.respond(req).await;
362
363 assert_eq!(res.status(), http::StatusCode::OK);
364 }
365
366 #[tokio::test]
367 async fn test_respond_with_route_handler_not_found() {
368 let engine = MockEngine::default();
369
370 let req = Request::builder()
371 .method(Method::GET)
372 .uri("/test")
373 .body(Vec::new())
374 .unwrap();
375
376 let res = engine.data.respond(req).await;
377
378 assert_eq!(res.status(), http::StatusCode::NOT_FOUND);
379 }
380
381 #[tokio::test]
401 async fn test_add_route() {
402 let mut engine = MockEngine::default();
403 let handler: Box<Handler> = Box::new(|_| Box::new(()) as Box<dyn ToBytes>);
404 engine.add_route("/test", Some(Method::GET), RouteHandler::Sync(handler));
405
406 assert!(engine.data.router.at("GET/test").is_ok());
407 }
408
409 #[tokio::test]
410 async fn test_add_middleware() {
411 let mut engine = MockEngine::default();
412 let middleware = MockMiddleware;
413 engine.data_mut().add_middleware(Box::new(middleware));
414
415 assert_eq!(engine.data.middlewares.len(), 1);
416 }
417
418 #[tokio::test]
419 async fn test_use_middleware() {
420 let mut engine = MockEngine::default();
421 let middleware = MockMiddleware;
422 engine.use_middleware(middleware);
423
424 assert_eq!(engine.data.middlewares.len(), 1);
425 }
426
427 #[tokio::test]
428 async fn test_set_state() {
429 let mut engine = MockEngine::default();
430 let app_state = MockAppState;
431 engine.set_state(app_state);
432
433 assert!(engine.data.state.is_some());
434 }
435}