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;