From 8edd6733b4a105de5b406854e285a9d833618a96 Mon Sep 17 00:00:00 2001 From: myyrakle Date: Mon, 2 Sep 2024 00:37:35 +0900 Subject: [PATCH 1/8] =?UTF-8?q?[#108]=20ApplicationProperties=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EA=B5=AC=EC=A1=B0=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rupring/src/application_properties.rs | 63 +++++++++++++++++++++++++++ rupring/src/lib.rs | 2 + 2 files changed, 65 insertions(+) create mode 100644 rupring/src/application_properties.rs diff --git a/rupring/src/application_properties.rs b/rupring/src/application_properties.rs new file mode 100644 index 0000000..7054f10 --- /dev/null +++ b/rupring/src/application_properties.rs @@ -0,0 +1,63 @@ +use std::collections::HashMap; + +pub struct ApplicationProperties { + pub server: Server, + + pub etc: HashMap, +} + +// Reference: https://docs.spring.io/spring-boot/appendix/application-properties/index.html#appendix.application-properties.server +pub struct Server { + pub address: String, + pub port: u16, +} + +impl Default for Server { + fn default() -> Self { + Server { + address: "0.0.0.0".to_string(), + port: 3000, + } + } +} + +impl ApplicationProperties { + pub fn from_properties(text: String) -> ApplicationProperties { + let mut server = Server::default(); + let mut etc = HashMap::new(); + + for line in text.lines() { + let mut parts = line.split("="); + + let key = match parts.next() { + Some(key) => key.trim().to_owned(), + None => continue, + }; + let value = match parts.next() { + Some(value) => value.trim().to_owned(), + None => continue, + }; + + // value에 앞뒤로 ""가 있다면 제거 + let value = if value.starts_with('"') && value.ends_with('"') { + value[1..value.len() - 1].to_string() + } else { + value.to_string() + }; + + match key.as_str() { + "server.port" => { + server.port = value.parse().unwrap(); + } + "server.address" => { + server.address = value.to_string(); + } + _ => { + etc.insert(key, value); + } + } + } + + ApplicationProperties { server, etc } + } +} diff --git a/rupring/src/lib.rs b/rupring/src/lib.rs index 351b7ac..fc388d4 100644 --- a/rupring/src/lib.rs +++ b/rupring/src/lib.rs @@ -791,6 +791,8 @@ use swagger::json::SwaggerOperation; use swagger::macros::SwaggerRequestBody; use swagger::SwaggerSecurity; +pub mod application_properties; + /// Module interface pub trait IModule { fn child_modules(&self) -> Vec>; From cecebe7ec12924b02a4e369a86b1cb58f9c10cad Mon Sep 17 00:00:00 2001 From: myyrakle Date: Mon, 2 Sep 2024 00:43:20 +0900 Subject: [PATCH 2/8] =?UTF-8?q?[#108]=20from=5Fproperties=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rupring/src/application_properties.rs | 108 +++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/rupring/src/application_properties.rs b/rupring/src/application_properties.rs index 7054f10..99d838e 100644 --- a/rupring/src/application_properties.rs +++ b/rupring/src/application_properties.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +#[derive(Debug, PartialEq, Clone)] pub struct ApplicationProperties { pub server: Server, @@ -7,6 +8,7 @@ pub struct ApplicationProperties { } // Reference: https://docs.spring.io/spring-boot/appendix/application-properties/index.html#appendix.application-properties.server +#[derive(Debug, PartialEq, Clone)] pub struct Server { pub address: String, pub port: u16, @@ -47,7 +49,9 @@ impl ApplicationProperties { match key.as_str() { "server.port" => { - server.port = value.parse().unwrap(); + if let Ok(value) = value.parse::() { + server.port = value; + } } "server.address" => { server.address = value.to_string(); @@ -61,3 +65,105 @@ impl ApplicationProperties { ApplicationProperties { server, etc } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_from_properties() { + struct TestCase { + name: String, + input: String, + expected: ApplicationProperties, + } + + let test_cases = vec![ + TestCase { + name: "일반적인 기본 속성 바인딩".to_string(), + input: r#" + server.port=8080 + server.address=127.0.0.1 + "# + .to_string(), + expected: ApplicationProperties { + server: Server { + address: "127.0.0.1".to_string(), + port: 8080, + }, + etc: HashMap::new(), + }, + }, + TestCase { + name: "추가 속성 바인딩".to_string(), + input: r#" + server.port=8080 + server.address=127.0.0.1 + foo.bar=hello + "# + .to_string(), + expected: ApplicationProperties { + server: Server { + address: "127.0.0.1".to_string(), + port: 8080, + }, + etc: HashMap::from([("foo.bar".to_string(), "hello".to_string())]), + }, + }, + TestCase { + name: "따옴표로 감싸기".to_string(), + input: r#" + server.port=8080 + server.address="127.0.0.1" + "# + .to_string(), + expected: ApplicationProperties { + server: Server { + address: "127.0.0.1".to_string(), + port: 8080, + }, + etc: HashMap::new(), + }, + }, + TestCase { + name: "중간에 띄어쓰기".to_string(), + input: r#" + server.port=8080 + server.address= 127.0.0.1 + "# + .to_string(), + expected: ApplicationProperties { + server: Server { + address: "127.0.0.1".to_string(), + port: 8080, + }, + etc: HashMap::new(), + }, + }, + TestCase { + name: "포트 파싱 실패".to_string(), + input: r#" + server.port=80#@#@80 + server.address= 127.0.0.1 + "# + .to_string(), + expected: ApplicationProperties { + server: Server { + address: "127.0.0.1".to_string(), + port: 3000, + }, + etc: HashMap::new(), + }, + }, + ]; + + for tc in test_cases { + let got = ApplicationProperties::from_properties(tc.input.clone()); + assert_eq!( + got, tc.expected, + "{} - input: {:?}, actual: {:?}", + tc.name, tc.input, got + ); + } + } +} From 03f464e0082755227cfcc284989292e27e68603e Mon Sep 17 00:00:00 2001 From: myyrakle Date: Mon, 2 Sep 2024 22:43:52 +0900 Subject: [PATCH 3/8] =?UTF-8?q?[#108]=20environment=20=EC=86=8D=EC=84=B1?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rupring/src/application_properties.rs | 33 ++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/rupring/src/application_properties.rs b/rupring/src/application_properties.rs index 99d838e..db813c7 100644 --- a/rupring/src/application_properties.rs +++ b/rupring/src/application_properties.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; #[derive(Debug, PartialEq, Clone)] pub struct ApplicationProperties { pub server: Server, + pub environment: String, pub etc: HashMap, } @@ -26,6 +27,7 @@ impl Default for Server { impl ApplicationProperties { pub fn from_properties(text: String) -> ApplicationProperties { let mut server = Server::default(); + let mut environment = "dev".to_string(); let mut etc = HashMap::new(); for line in text.lines() { @@ -56,13 +58,20 @@ impl ApplicationProperties { "server.address" => { server.address = value.to_string(); } + "environment" => { + environment = value.to_string(); + } _ => { etc.insert(key, value); } } } - ApplicationProperties { server, etc } + ApplicationProperties { + server, + etc, + environment, + } } } @@ -92,6 +101,7 @@ mod tests { port: 8080, }, etc: HashMap::new(), + environment: "dev".to_string(), }, }, TestCase { @@ -107,6 +117,7 @@ mod tests { address: "127.0.0.1".to_string(), port: 8080, }, + environment: "dev".to_string(), etc: HashMap::from([("foo.bar".to_string(), "hello".to_string())]), }, }, @@ -122,6 +133,7 @@ mod tests { address: "127.0.0.1".to_string(), port: 8080, }, + environment: "dev".to_string(), etc: HashMap::new(), }, }, @@ -137,6 +149,7 @@ mod tests { address: "127.0.0.1".to_string(), port: 8080, }, + environment: "dev".to_string(), etc: HashMap::new(), }, }, @@ -152,6 +165,24 @@ mod tests { address: "127.0.0.1".to_string(), port: 3000, }, + environment: "dev".to_string(), + etc: HashMap::new(), + }, + }, + TestCase { + name: "environment 바인딩".to_string(), + input: r#" + server.port=80#@#@80 + server.address= 127.0.0.1 + environment=prod + "# + .to_string(), + expected: ApplicationProperties { + server: Server { + address: "127.0.0.1".to_string(), + port: 3000, + }, + environment: "prod".to_string(), etc: HashMap::new(), }, }, From 1a317913b54d476661c8695e916c1e8fd37904ad Mon Sep 17 00:00:00 2001 From: myyrakle Date: Mon, 2 Sep 2024 22:48:33 +0900 Subject: [PATCH 4/8] =?UTF-8?q?[#108]=20load=5Fapplication=5Fproperties=5F?= =?UTF-8?q?from=5Fall=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rupring/src/application_properties.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/rupring/src/application_properties.rs b/rupring/src/application_properties.rs index db813c7..27d154d 100644 --- a/rupring/src/application_properties.rs +++ b/rupring/src/application_properties.rs @@ -8,6 +8,16 @@ pub struct ApplicationProperties { pub etc: HashMap, } +impl Default for ApplicationProperties { + fn default() -> Self { + ApplicationProperties { + server: Server::default(), + environment: "dev".to_string(), + etc: HashMap::new(), + } + } +} + // Reference: https://docs.spring.io/spring-boot/appendix/application-properties/index.html#appendix.application-properties.server #[derive(Debug, PartialEq, Clone)] pub struct Server { @@ -198,3 +208,13 @@ mod tests { } } } + +// 알아서 모든 대상에 대해 application.properties를 읽어서 ApplicationProperties를 반환하는 함수 +pub fn load_application_properties_from_all() -> ApplicationProperties { + // 1. 현재 경로에 application.properties가 있는지 확인하고, 있다면 읽어서 반환합니다. + if let Ok(text) = std::fs::read_to_string("application.properties") { + return ApplicationProperties::from_properties(text); + } + + ApplicationProperties::default() +} From c82c7b2fc5833bc9820b638c85dfc7a9f3c49c5a Mon Sep 17 00:00:00 2001 From: myyrakle Date: Mon, 2 Sep 2024 22:53:25 +0900 Subject: [PATCH 5/8] =?UTF-8?q?[#108]=20di=20=EB=AA=A8=EB=93=88=EC=9D=84?= =?UTF-8?q?=20boot=EC=97=90=EC=84=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rupring/src/boot/mod.rs | 2 +- rupring/src/{boot/di.rs => di/mod.rs} | 0 rupring/src/lib.rs | 5 +++-- 3 files changed, 4 insertions(+), 3 deletions(-) rename rupring/src/{boot/di.rs => di/mod.rs} (100%) diff --git a/rupring/src/boot/mod.rs b/rupring/src/boot/mod.rs index 7e78cef..fa8c0e9 100644 --- a/rupring/src/boot/mod.rs +++ b/rupring/src/boot/mod.rs @@ -1,5 +1,5 @@ mod banner; -pub mod di; +use crate::di; mod parse; pub(crate) mod route; diff --git a/rupring/src/boot/di.rs b/rupring/src/di/mod.rs similarity index 100% rename from rupring/src/boot/di.rs rename to rupring/src/di/mod.rs diff --git a/rupring/src/lib.rs b/rupring/src/lib.rs index fc388d4..78a9862 100644 --- a/rupring/src/lib.rs +++ b/rupring/src/lib.rs @@ -604,6 +604,7 @@ pub fn get_user(request: rupring::Request, _: rupring::Response) -> rupring::Res */ pub(crate) mod boot; +pub(crate) mod di; /// header constants pub mod header; @@ -776,9 +777,9 @@ pub type Method = hyper::Method; pub type HeaderName = hyper::header::HeaderName; /// Dependency Injection Context for entire life cycle -pub use boot::di::DIContext; +pub use di::DIContext; /// Dependency Injection Provider -pub use boot::di::IProvider; +pub use di::IProvider; /// String wrapper type for ParamStringDeserializer. pub use request::ParamString; /// ParamStringDeserializer trait From f969f3b3e156c680398496b15c98717a3ce8e25d Mon Sep 17 00:00:00 2001 From: myyrakle Date: Mon, 2 Sep 2024 22:54:31 +0900 Subject: [PATCH 6/8] [#108] rename boot => core --- rupring/src/{boot => core}/banner.rs | 0 rupring/src/{boot => core}/mod.rs | 0 rupring/src/{boot => core}/parse.rs | 0 rupring/src/{boot => core}/route.rs | 0 rupring/src/lib.rs | 4 ++-- rupring/src/swagger/context.rs | 2 +- 6 files changed, 3 insertions(+), 3 deletions(-) rename rupring/src/{boot => core}/banner.rs (100%) rename rupring/src/{boot => core}/mod.rs (100%) rename rupring/src/{boot => core}/parse.rs (100%) rename rupring/src/{boot => core}/route.rs (100%) diff --git a/rupring/src/boot/banner.rs b/rupring/src/core/banner.rs similarity index 100% rename from rupring/src/boot/banner.rs rename to rupring/src/core/banner.rs diff --git a/rupring/src/boot/mod.rs b/rupring/src/core/mod.rs similarity index 100% rename from rupring/src/boot/mod.rs rename to rupring/src/core/mod.rs diff --git a/rupring/src/boot/parse.rs b/rupring/src/core/parse.rs similarity index 100% rename from rupring/src/boot/parse.rs rename to rupring/src/core/parse.rs diff --git a/rupring/src/boot/route.rs b/rupring/src/core/route.rs similarity index 100% rename from rupring/src/boot/route.rs rename to rupring/src/core/route.rs diff --git a/rupring/src/lib.rs b/rupring/src/lib.rs index 78a9862..37edde2 100644 --- a/rupring/src/lib.rs +++ b/rupring/src/lib.rs @@ -603,7 +603,7 @@ pub fn get_user(request: rupring::Request, _: rupring::Response) -> rupring::Res ``` */ -pub(crate) mod boot; +pub(crate) mod core; pub(crate) mod di; /// header constants @@ -864,7 +864,7 @@ impl RupringFactory { let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port); - let result = boot::run_server(socket_addr, self.root_module).await; + let result = core::run_server(socket_addr, self.root_module).await; return result; } diff --git a/rupring/src/swagger/context.rs b/rupring/src/swagger/context.rs index 01e708c..2f058e0 100644 --- a/rupring/src/swagger/context.rs +++ b/rupring/src/swagger/context.rs @@ -56,7 +56,7 @@ fn generate_swagger(swagger: &mut SwaggerSchema, root_module: Box Date: Mon, 2 Sep 2024 23:33:18 +0900 Subject: [PATCH 7/8] =?UTF-8?q?[#108]=20run=20=EC=88=8F=EC=BB=B7=20?= =?UTF-8?q?=EB=B0=8F=20application.properties=20=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application.properties | 1 + rupring/src/core/boot.rs | 26 ++++++++++++++++++++++++++ rupring/src/core/mod.rs | 1 + rupring/src/lib.rs | 14 ++++++++++++-- rupring_example/Cargo.toml | 1 - rupring_example/src/main.rs | 9 ++------- 6 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 application.properties create mode 100644 rupring/src/core/boot.rs diff --git a/application.properties b/application.properties new file mode 100644 index 0000000..a3ac65c --- /dev/null +++ b/application.properties @@ -0,0 +1 @@ +server.port=8080 \ No newline at end of file diff --git a/rupring/src/core/boot.rs b/rupring/src/core/boot.rs new file mode 100644 index 0000000..6eab760 --- /dev/null +++ b/rupring/src/core/boot.rs @@ -0,0 +1,26 @@ +use crate::IModule; + +/** shortcut to run the application + +```rust,ignore +use domains::root::module::RootModule; + +pub(crate) mod domains; +pub(crate) mod middlewares; + +fn main() { + rupring::run(RootModule {}) +} +``` +*/ +#[tokio::main] +pub async fn run(root_module: T) +where + T: IModule + Clone + Copy + Sync + Send + 'static, +{ + let app = crate::RupringFactory::create(root_module); + + let port = app.application_properties.server.port; + + app.listen(port).await.unwrap(); +} diff --git a/rupring/src/core/mod.rs b/rupring/src/core/mod.rs index fa8c0e9..061125d 100644 --- a/rupring/src/core/mod.rs +++ b/rupring/src/core/mod.rs @@ -1,4 +1,5 @@ mod banner; +pub mod boot; use crate::di; mod parse; pub(crate) mod route; diff --git a/rupring/src/lib.rs b/rupring/src/lib.rs index 37edde2..61fd83e 100644 --- a/rupring/src/lib.rs +++ b/rupring/src/lib.rs @@ -604,6 +604,7 @@ pub fn get_user(request: rupring::Request, _: rupring::Response) -> rupring::Res */ pub(crate) mod core; +pub use core::boot::run; pub(crate) mod di; /// header constants @@ -619,7 +620,10 @@ pub mod response; pub mod swagger; use std::panic::UnwindSafe; +use std::str::FromStr; +use application_properties::load_application_properties_from_all; +use application_properties::ApplicationProperties; /** Controller Annotation ```rust #[rupring::Get(path = /)] @@ -848,6 +852,7 @@ pub type NextFunction = fn(Request, Response) -> Response; #[derive(Debug, Clone)] pub struct RupringFactory { root_module: T, + pub application_properties: ApplicationProperties, } impl RupringFactory { @@ -855,14 +860,19 @@ impl RupringFactory { pub fn create(module: T) -> Self { RupringFactory { root_module: module, + application_properties: load_application_properties_from_all(), } } /// It receives the port number and runs the server. pub async fn listen(self, port: u16) -> Result<(), Box> { - use std::net::{IpAddr, Ipv4Addr, SocketAddr}; + use std::net::{IpAddr, SocketAddr}; - let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), port); + let host = self.application_properties.server.address.clone(); + + let ip = IpAddr::from_str(host.as_str())?; + + let socket_addr = SocketAddr::new(ip, port); let result = core::run_server(socket_addr, self.root_module).await; diff --git a/rupring_example/Cargo.toml b/rupring_example/Cargo.toml index 839b81f..bb3b923 100644 --- a/rupring_example/Cargo.toml +++ b/rupring_example/Cargo.toml @@ -5,5 +5,4 @@ edition = "2021" [dependencies] rupring={ version="0.8.1", path="../rupring" } -tokio = { version = "1", features = ["full"] } serde = { version="1.0.193", features=["derive"] } \ No newline at end of file diff --git a/rupring_example/src/main.rs b/rupring_example/src/main.rs index d79bfc1..0d3d545 100644 --- a/rupring_example/src/main.rs +++ b/rupring_example/src/main.rs @@ -3,11 +3,6 @@ use domains::root::module::RootModule; pub(crate) mod domains; pub(crate) mod middlewares; -#[rupring::tokio::main] -async fn main() -> Result<(), Box> { - let app = rupring::RupringFactory::create(RootModule {}); - - app.listen(3000).await?; - - Ok(()) +fn main() { + rupring::run(RootModule {}) } From 8ae986f2b7276861b96498c7dc96759da2e89752 Mon Sep 17 00:00:00 2001 From: myyrakle Date: Mon, 2 Sep 2024 23:35:26 +0900 Subject: [PATCH 8/8] =?UTF-8?q?[#108]=20v0.9.0=20=EB=B2=84=EC=A0=84=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=95=20=EB=B0=8F=20=EB=AC=B8=EC=84=9C=ED=99=94=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 ++++---------- rupring/Cargo.toml | 2 +- rupring/src/lib.rs | 9 ++------- rupring_example/Cargo.toml | 2 +- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 77a990e..935a9dc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # rupring -![](https://img.shields.io/badge/language-Rust-red) ![](https://img.shields.io/badge/version-0.8.2-brightgreen) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/myyrakle/rupring/blob/master/LICENSE) +![](https://img.shields.io/badge/language-Rust-red) ![](https://img.shields.io/badge/version-0.9.0-brightgreen) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/myyrakle/rupring/blob/master/LICENSE) spring on rust @@ -8,8 +8,7 @@ spring on rust required dependency list ```toml -rupring = "0.8.2" -tokio = { version = "1", features = ["full"] } +rupring = "0.9.0" serde = { version="1.0.193", features=["derive"] } ``` @@ -34,13 +33,8 @@ pub fn echo(request: rupring::Request) -> rupring::Response { rupring::Response::new().text(request.body) } -#[tokio::main] -async fn main() -> Result<(), Box> { - let app = rupring::RupringFactory::create(RootModule {}); - - app.listen(3000).await?; - - Ok(()) +fn main() { + rupring::run(RootModule {}) } ``` diff --git a/rupring/Cargo.toml b/rupring/Cargo.toml index 460968b..ac1c9b0 100644 --- a/rupring/Cargo.toml +++ b/rupring/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rupring" -version = "0.8.2" +version = "0.9.0" edition = "2021" license = "MIT" authors = ["myyrakle "] diff --git a/rupring/src/lib.rs b/rupring/src/lib.rs index 61fd83e..187fae1 100644 --- a/rupring/src/lib.rs +++ b/rupring/src/lib.rs @@ -24,13 +24,8 @@ pub fn echo(request: rupring::Request) -> rupring::Response { rupring::Response::new().text(request.body) } -#[tokio::main] -async fn main() -> Result<(), Box> { - let app = rupring::RupringFactory::create(RootModule {}); - - app.listen(3000).await?; - - Ok(()) +fn main() { + rupring::run(RootModule {}) } ``` diff --git a/rupring_example/Cargo.toml b/rupring_example/Cargo.toml index bb3b923..72864b6 100644 --- a/rupring_example/Cargo.toml +++ b/rupring_example/Cargo.toml @@ -4,5 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] -rupring={ version="0.8.1", path="../rupring" } +rupring={ version="0.9.0", path="../rupring" } serde = { version="1.0.193", features=["derive"] } \ No newline at end of file