rupring/lib.rs
1// #![allow(clippy::single_match)]
2// #![allow(clippy::to_string_trait_impl)]
3// #![allow(clippy::map_entry)]
4// #![allow(clippy::map_flatten)]
5
6/*! # Get Started
7There is only one dependency.
8```bash
9cargo add rupring
10```
11
12And you can write your server like this:
13```rust,ignore
14#[derive(Debug, Clone, Copy)]
15#[rupring::Module(controllers=[HomeController{}], modules=[])]
16pub struct RootModule {}
17
18#[derive(Debug, Clone)]
19#[rupring::Controller(prefix=/, routes=[hello, echo])]
20pub struct HomeController {}
21
22#[rupring::Get(path = /)]
23pub fn hello(_request: rupring::Request) -> rupring::Response {
24 rupring::Response::new().text("Hello, World!".to_string())
25}
26
27#[rupring::Get(path = /echo)]
28pub fn echo(request: rupring::Request) -> rupring::Response {
29 rupring::Response::new().text(request.body)
30}
31
32fn main() {
33 rupring::run(RootModule {})
34}
35```
36
37# Request
38- rupring defines HTTP Request through [crate::request::Request] type and provides convenient request processing using macros.
39```rust
40#[rupring::Get(path = /:id)]
41pub fn hello(request: rupring::Request) -> rupring::Response {
42 let method = request.method;
43 assert_eq!(method, rupring::Method::GET);
44
45 let path = request.path;
46 assert_eq!(path, "/");
47
48 let body = request.body;
49 assert_eq!(body, "");
50
51 let headers = request.headers;
52 let content_type = headers.get("content-type").unwrap();
53 assert_eq!(content_type, "text/plain");
54
55 let id = request.path_parameters["id"].clone();
56 assert_eq!(id, "123");
57
58 let query = request.query_parameters["query"].clone();
59 assert_eq!(query, vec!["asdf".to_string()]);
60
61 //...
62
63 response
64}
65```
66- Please refer to the corresponding [document](crate::request) for more details.
67
68# Response
69- rupring defines HTTP Response through [crate::response::Response] type and provides convenient response processing using macros.
70```rust
71#[rupring::Get(path = /)]
72pub fn hello(_request: rupring::Request) -> rupring::Response {
73 rupring::Response::new().text("Hello, World!".to_string())
74}
75```
76- Please refer to the corresponding [document](crate::response) for more details.
77
78# Server Sent Event (SSE)
79- rupring provides Server Sent Event (SSE) features.
80- You can use SSE streaming directly through [crate::response::Response::sse_stream] method.
81
82# Middleware
83rupring provides middleware features for common logic processing.
84
85If you want to log requests for all APIs that exist in a module, you can apply middleware in the form below.
86
87First, define a middleware function.
88```rust
89pub fn logger_middleware(
90 request: rupring::Request,
91 response: rupring::Response,
92 next: rupring::NextFunction,
93) -> rupring::Response {
94 println!(
95 "Request: {} {}",
96 request.method.to_string(),
97 request.path.to_string()
98 );
99
100 next(request, response)
101}
102```
103The above function only records logs and forwards them to the next middleware or route function.
104If you want to return a response immediately without forwarding, just return the response without calling the next function.
105
106
107And you can register the middleware function just defined in the module or controller unit.
108```rust
109pub fn logger_middleware(
110 request: rupring::Request,
111 response: rupring::Response,
112 next: rupring::NextFunction,
113) -> rupring::Response {
114 println!(
115 "Request: {} {}",
116 request.method.to_string(),
117 request.path.to_string()
118 );
119
120 next(request, response)
121}
122
123#[derive(Debug, Clone, Copy)]
124#[rupring::Module(
125 controllers=[RootController{}],
126 modules=[UserModule{}],
127 providers=[],
128 middlewares=[logger_middleware]
129)]
130pub struct RootModule {}
131
132#[derive(Debug, Clone)]
133#[rupring::Controller(prefix=/, routes=[])]
134pub struct RootController {}
135
136#[derive(Debug, Clone, Copy)]
137#[rupring::Module(
138 controllers=[UserController{}],
139 providers=[],
140 middlewares=[]
141)]
142pub struct UserModule {}
143
144// or Controller
145#[derive(Debug, Clone)]
146#[rupring::Controller(prefix=/, routes=[], middlewares=[logger_middleware])]
147pub struct UserController {}
148```
149Middleware registered in a module is recursively applied to the routes of controllers registered in that module and to child modules.
150On the other hand, middleware registered in a controller applies only to the routes of that controller.
151
152The priorities in which middleware is applied are as follows:
153
1541. Middleware of the same unit is executed in the order defined in the array.
1552. If module middleware and controller middleware exist at the same time, module middleware is executed first.
1563. If the parent module's middleware and the child module's middleware exist at the same time, the parent module middleware is executed first.
157
158
159# Dependency Injection
160- Rupring provides powerful DI features through macro and runtime support.
161```rust
162#[derive(Debug, Clone, Default)]
163pub struct HomeService {}
164
165impl HomeService {
166 pub fn hello(&self) -> String {
167 "hello!!".to_string()
168 }
169}
170
171impl rupring::IProvider for HomeService {
172 fn provide(&self, di_context: &rupring::DIContext) -> Box<dyn std::any::Any> {
173 Box::new(HomeService {})
174 }
175}
176```
177- Please refer to the corresponding [document](crate::di) for more details.
178
179# Swagger
180- When rupring starts the server, it automatically serves swagger documents to the `/docs` path.
181- Please refer to the corresponding [document](crate::swagger) for more details.
182
183# Application Properties
184- rupring provides various execution options through a special configuration file called application.properties.
185- Please refer to the corresponding [document](crate::application_properties) for more details.
186
187# AWS Lambda
188- rupring provides the option to run on AWS Lambda.
189- Supported Lambda Runtimes
190 1. Amazon Linux 2
191 2. Amazon Linux 2023
192
193## How to use
1941. Enable the "aws-lambda" feature flag.
195```ignore
196rupring={ version = "0.12.0", features=["aws-lambda"] }
197```
198
1992. Use the `rupring::run_on_aws_lambda` function instead of `rupring::run`.
200```rust,ignore
201fn main() {
202 rupring::run_on_aws_lambda(RootModule {})
203}
204```
205
2063. Compile and create an executable file. (x86_64-unknown-linux-musl)
207```bash
208rustup target add x86_64-unknown-linux-musl
209cargo build --release --target x86_64-unknown-linux-musl
210```
211
2123. Zip the executable file and upload it to the AWS console.
213- The name of the executable file must be `bootstrap`.
214```bash
215zip -j bootstrap.zip ./target/x86_64-unknown-linux-musl/release/bootstrap
216```
217
2184. ...and upload it as a file to the AWS console
219*/
220
221pub(crate) mod core;
222pub(crate) mod utils;
223pub use core::boot::run;
224
225#[cfg(feature = "aws-lambda")]
226pub use core::boot::run_on_aws_lambda;
227
228pub mod di;
229
230/// http protocol related module
231pub mod http;
232
233pub use http::header;
234
235mod logger;
236/// HTTP request module
237pub mod request;
238/// HTTP response module
239pub mod response;
240/// swagger module
241pub mod swagger;
242
243use std::panic::UnwindSafe;
244
245use application_properties::load_application_properties_from_all;
246use application_properties::ApplicationProperties;
247/** Controller Annotation
248```rust
249#[rupring::Get(path = /)]
250pub fn hello(request: rupring::Request) -> rupring::Response {
251 // ...
252 rupring::Response::new().text("Hello, World!".to_string())
253}
254
255#[rupring::Get(path = /echo)]
256pub fn echo(request: rupring::Request) -> rupring::Response {
257 // ...
258 rupring::Response::new().text(request.body)
259}
260
261#[derive(Debug, Clone)]
262#[rupring::Controller(prefix=/, routes=[hello, echo])]
263pub struct HomeController {}
264```
265*/
266pub use rupring_macro::Controller;
267
268/** Module Annotation
269```rust
270#[derive(Debug, Clone)]
271#[rupring::Module(
272 controllers=[/*HomeController{}*/],
273 modules=[],
274 providers=[/*HomeService::default(), HomeRepository::default(), UserService::default(), CounterService::default()*/]
275)]
276pub struct RootModule {}
277```
278 */
279pub use rupring_macro::Module;
280
281/** This is a shortcut annotation for creating an IProvider object.
282
283```rust
284use std::sync::{Arc, Mutex};
285
286#[derive(Debug, Clone, Default)]
287pub struct CounterService {
288 counter: Arc<Mutex<i32>>,
289}
290
291impl CounterService {
292 pub fn new() -> Self {
293 CounterService {
294 counter: Arc::new(Mutex::new(0)),
295 }
296 }
297}
298
299#[rupring_macro::Injectable(CounterServiceFactory)]
300fn inject_counter_service() -> CounterService {
301 CounterService::new()
302}
303
304#[derive(Debug, Clone, Copy)]
305#[rupring::Module(
306 controllers=[/*...*/],
307 modules=[/*...*/],
308 providers=[CounterServiceFactory{}],
309 middlewares=[]
310)]
311pub struct RootModule {}
312```
313*/
314pub use rupring_macro::Injectable;
315
316/// This is an alias for [Injectable].
317pub use rupring_macro::Bean;
318
319/// This is an alias for [Injectable].
320pub use rupring_macro::Component;
321
322/// This is an alias for [Injectable].
323pub use rupring_macro::Service;
324
325/// This is an alias for [Injectable].
326pub use rupring_macro::Repository;
327
328/** Get Route Annotation
329```rust
330#[rupring::Get(path = /)]
331pub fn hello(request: rupring::Request) -> rupring::Response {
332 // ...
333 rupring::Response::new().text("Hello, World!".to_string())
334}
335*/
336pub use rupring_macro::Get;
337
338/// This is an alias for [Get].
339pub use rupring_macro::GetMapping;
340
341/** Post Route Annotation
342```rust
343#[rupring::Post(path = /)]
344pub fn hello(request: rupring::Request) -> rupring::Response {
345 // ...
346 rupring::Response::new().text("Hello, World!".to_string())
347}
348```
349*/
350pub use rupring_macro::Post;
351
352/// This is an alias for [Post].
353pub use rupring_macro::PostMapping;
354
355/** Patch Route Annotation
356```rust
357#[rupring::Patch(path = /)]
358pub fn hello(request: rupring::Request) -> rupring::Response {
359 // ...
360 rupring::Response::new().text("Hello, World!".to_string())
361}
362```
363*/
364pub use rupring_macro::Patch;
365
366/// This is an alias for [Patch].
367pub use rupring_macro::PatchMapping;
368
369/** Put Route Annotation
370```rust
371#[rupring::Put(path = /)]
372pub fn hello(request: rupring::Request) -> rupring::Response {
373 // ...
374 rupring::Response::new().text("Hello, World!".to_string())
375}
376```
377*/
378pub use rupring_macro::Put;
379
380/// This is an alias for [Put].
381pub use rupring_macro::PutMapping;
382
383/** Delete Route Annotation
384```rust
385#[rupring::Delete(path = /)]
386pub fn hello(request: rupring::Request) -> rupring::Response {
387 // ...
388 rupring::Response::new().text("Hello, World!".to_string())
389}
390```
391*/
392pub use rupring_macro::Delete;
393
394/// This is an alias for [Delete].
395pub use rupring_macro::DeleteMapping;
396
397/// HTTP method (from hyper crate)
398pub type Method = hyper::Method;
399
400/// HTTP Header Name (from hyper crate)
401pub type HeaderName = hyper::header::HeaderName;
402
403/// Dependency Injection Context for entire life cycle
404pub use di::DIContext;
405/// Dependency Injection Provider
406pub use di::IProvider;
407/// String wrapper type for ParamStringDeserializer.
408pub use request::ParamString;
409/// ParamStringDeserializer trait
410pub use request::ParamStringDeserializer;
411/// HTTP Request
412pub use request::Request;
413/// HTTP Response
414pub use response::Response;
415use swagger::json::SwaggerOperation;
416use swagger::macros::SwaggerRequestBody;
417use swagger::SwaggerSecurity;
418
419/// Application Properties
420pub mod application_properties;
421
422/// Module interface
423pub trait IModule {
424 fn child_modules(&self) -> Vec<Box<dyn IModule>>;
425 fn controllers(&self) -> Vec<Box<dyn IController>>;
426 fn providers(&self) -> Vec<Box<dyn IProvider>>;
427 fn middlewares(&self) -> Vec<MiddlewareFunction>;
428}
429
430/// Middleware function type
431pub type MiddlewareFunction =
432 Box<dyn Fn(Request, Response, NextFunction) -> Response + Send + Sync + UnwindSafe + 'static>;
433
434/// Controller interface
435pub trait IController {
436 fn prefix(&self) -> String;
437 fn routes(&self) -> Vec<Box<dyn IRoute + Send + 'static>>;
438 fn middlewares(&self) -> Vec<MiddlewareFunction>;
439}
440
441/// Route interface
442pub trait IRoute {
443 fn method(&self) -> Method;
444 fn path(&self) -> String;
445 fn handler(&self) -> Box<dyn IHandler + Send + 'static>;
446
447 fn swagger(&self) -> SwaggerOperation {
448 Default::default()
449 }
450
451 fn swagger_request_info(&self) -> Option<SwaggerRequestBody> {
452 None
453 }
454
455 fn swagger_response_info(&self) -> Option<SwaggerRequestBody> {
456 None
457 }
458
459 fn swagger_security_info(&self) -> Vec<SwaggerSecurity> {
460 vec![]
461 }
462}
463
464/// Handler interface
465pub trait IHandler: UnwindSafe {
466 fn handle(&self, request: Request, response: Response) -> Response;
467}
468
469/// Next function type for middleware
470pub type NextFunction = fn(Request, Response) -> Response;
471
472/// Rupring Factory for creating server
473#[derive(Debug, Clone)]
474pub struct RupringFactory<T: IModule> {
475 root_module: T,
476 pub application_properties: ApplicationProperties,
477}
478
479impl<T: IModule + Clone + Copy + Sync + Send + 'static> RupringFactory<T> {
480 /// It receives the root module object and creates a factory to run the server.
481 pub fn create(module: T) -> Self {
482 RupringFactory {
483 root_module: module,
484 application_properties: load_application_properties_from_all(),
485 }
486 }
487
488 /// It receives the port number and runs the server.
489 pub fn listen(self) -> anyhow::Result<()> {
490 use tokio::runtime::Builder;
491
492 let mut runtime_builder = Builder::new_multi_thread();
493
494 runtime_builder.enable_all();
495
496 if let Some(thread_limit) = self.application_properties.server.thread_limit {
497 runtime_builder.worker_threads(thread_limit);
498 }
499
500 let runtime = runtime_builder.build()?;
501
502 runtime.block_on(async {
503 core::run_server(self.application_properties, self.root_module).await
504 })
505 }
506
507 #[cfg(feature = "aws-lambda")]
508 pub fn listen_on_aws_lambda(self) -> anyhow::Result<()> {
509 use tokio::runtime::Builder;
510
511 let mut runtime_builder = Builder::new_multi_thread();
512
513 runtime_builder.enable_all();
514
515 if let Some(thread_limit) = self.application_properties.server.thread_limit {
516 runtime_builder.worker_threads(thread_limit);
517 }
518
519 let runtime = runtime_builder.build()?;
520
521 runtime.block_on(async {
522 core::run_server_on_aws_lambda(self.application_properties, self.root_module).await
523 })
524 }
525}
526
527/// RupringDto derive macro
528pub use rupring_macro::RupringDto;
529
530#[cfg(test)]
531mod test_proc_macro;
532
533pub use anyhow;
534pub use anyhow::anyhow as error;
535pub use anyhow::Result;
536
537//pub use serde;
538//pub use serde::{Deserialize, Serialize};
539pub use serde_json;
540
541pub use tokio;