From aa7f31b39645093403c1bb7ae7f6cf3530ce87cc Mon Sep 17 00:00:00 2001 From: fluo10 Date: Sat, 3 May 2025 14:57:15 +0900 Subject: [PATCH] Update config for server and client --- dpts-config/src/client.rs | 103 +++++++++++++++++++---- dpts-config/src/client/storage.rs | 23 ++--- dpts-config/src/client/storage/local.rs | 15 ---- dpts-config/src/client/storage/remote.rs | 8 +- dpts-config/src/database.rs | 45 ++++++++++ dpts-config/src/lib.rs | 22 +++-- dpts-config/src/server.rs | 65 ++++++++------ dpts-config/src/user.rs | 0 8 files changed, 196 insertions(+), 85 deletions(-) delete mode 100644 dpts-config/src/client/storage/local.rs create mode 100644 dpts-config/src/database.rs delete mode 100644 dpts-config/src/user.rs diff --git a/dpts-config/src/client.rs b/dpts-config/src/client.rs index 5bab7f9..9a8edd2 100644 --- a/dpts-config/src/client.rs +++ b/dpts-config/src/client.rs @@ -8,28 +8,75 @@ use serde::{ }; use chrono_tz::{Tz, UTC}; +use tokio::sync::OnceCell; use crate::{get_host_time_zone_or_utc, Error}; -use std::{ - str::FromStr, - net::IpAddr -}; +pub static CLIENT_CONFIG: OnceClientConfig = OnceClientConfig::const_new(); + +pub struct OnceClientConfig { + inner: OnceCell, +} + +impl OnceClientConfig { + const fn const_new() -> Self { + Self { + inner: OnceCell::const_new(), + } + } + pub fn get(&self) -> Option<&ClientConfig> { + self.inner.get() + } + pub async fn get_or_init(&self, f: F) -> &ClientConfig where + F: FnOnce() -> T, + T: Future + { + self.inner.get_or_init(f).await + } +} pub struct ClientConfig { - + pub time_zone: Tz, + pub storage: ClientStorageConfig, } + +impl TryFrom<&PartialClientConfig> for ClientConfig { + type Error = Error; + fn try_from(p: &PartialClientConfig) -> Result { + Ok(ClientConfig{ + time_zone: p.time_zone.ok_or(Error::MissingConfig("time_zone".to_string()))?, + storage: p.clone().storage.ok_or(Error::MissingConfig("storage".to_string()))?, + }) + + } +} + #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct PartialClientConfig { - time_zone: Option, - storage: Option, + pub time_zone: Option, + pub storage: Option, } +impl PartialClientConfig { + +} + + +impl Default for PartialClientConfig { + fn default() -> Self { + PartialClientConfig { + time_zone: Some(get_host_time_zone_or_utc()), + storage: None + } + } +} + + + #[cfg(test)] mod tests { use super::*; use tokio::sync::OnceCell; - use std::path::PathBuf; const EMPTY_CONFIG_TOML: &str = r#""#; static EMPTY_CONFIG_STRUCT: OnceCell = OnceCell::const_new(); @@ -55,9 +102,7 @@ mod tests { } const LOCAL_STORAGE_CONFIG_TOML: &str = r#"time_zone = "UTC" - -[storage.local] -database_path = "." +storage = "local" "#; static LOCAL_STORAGE_CONFIG_STRUCT: OnceCell = OnceCell::const_new(); @@ -65,11 +110,7 @@ database_path = "." LOCAL_STORAGE_CONFIG_STRUCT.get_or_init(|| async { PartialClientConfig{ time_zone: Some(UTC), - storage: Some(PartialClientStorageConfig::Local( - PartialClientLocalStorageConfig { - database_path: Some(PathBuf::from_str(".").unwrap()), - } - )), + storage: Some(ClientStorageConfig::Local), } }).await } @@ -83,5 +124,35 @@ database_path = "." async fn serialize_local_storage_client_config() { assert_eq!(LOCAL_STORAGE_CONFIG_TOML, toml::to_string(get_local_storage_client_config_struct().await).unwrap()); } + + const REMOTE_STORAGE_CONFIG_TOML: &str = r#"time_zone = "UTC" + +[storage.remote] +endpoint = "https://example.com" +access_key = "test" +"#; + static REMOTE_STORAGE_CONFIG_STRUCT: OnceCell = OnceCell::const_new(); + + async fn get_remote_storage_client_config_struct() -> &'static PartialClientConfig { + REMOTE_STORAGE_CONFIG_STRUCT.get_or_init(|| async { + PartialClientConfig{ + time_zone: Some(UTC), + storage: Some(ClientStorageConfig::Remote(ClientRemoteStorageConfig { + endpoint: "https://example.com".to_string(), + access_key: "test".to_string(), + })), + } + }).await + } + #[tokio::test] + async fn deserialize_remote_storage_client_config() { + let config: PartialClientConfig = toml::from_str(REMOTE_STORAGE_CONFIG_TOML).unwrap(); + assert_eq!(&config, get_remote_storage_client_config_struct().await); + } + + #[tokio::test] + async fn serialize_remote_storage_client_config() { + assert_eq!(REMOTE_STORAGE_CONFIG_TOML, toml::to_string(get_remote_storage_client_config_struct().await).unwrap()); + } } diff --git a/dpts-config/src/client/storage.rs b/dpts-config/src/client/storage.rs index 5b991ab..0bebf3f 100644 --- a/dpts-config/src/client/storage.rs +++ b/dpts-config/src/client/storage.rs @@ -1,29 +1,18 @@ -mod local; mod remote; -pub use local::{ - ClientLocalStorageConfig, - PartialClientLocalStorageConfig, -}; - -pub use remote::{ - ClientRemoteStorageConfig, - PartialClientRemoteStorageConfig, -}; +pub use remote::ClientRemoteStorageConfig; use serde::{ Deserialize, Serialize }; +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] pub enum ClientStorageConfig { - Local(ClientLocalStorageConfig), + Local, Remote(ClientRemoteStorageConfig), } -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum PartialClientStorageConfig { - Local(PartialClientLocalStorageConfig), - Remote(PartialClientRemoteStorageConfig), -} \ No newline at end of file + + diff --git a/dpts-config/src/client/storage/local.rs b/dpts-config/src/client/storage/local.rs deleted file mode 100644 index ebcf729..0000000 --- a/dpts-config/src/client/storage/local.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::path::PathBuf; -use serde::{ - Deserialize, - Serialize -}; - -pub struct ClientLocalStorageConfig { - pub database_path: PathBuf -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -pub struct PartialClientLocalStorageConfig { - pub database_path: Option, -} - diff --git a/dpts-config/src/client/storage/remote.rs b/dpts-config/src/client/storage/remote.rs index c7c13b2..8b37a14 100644 --- a/dpts-config/src/client/storage/remote.rs +++ b/dpts-config/src/client/storage/remote.rs @@ -10,13 +10,9 @@ use std::{ net::IpAddr }; +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] + pub struct ClientRemoteStorageConfig { pub endpoint: String, pub access_key: String, -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -pub struct PartialClientRemoteStorageConfig { - pub endpoint: Option, - pub access_key: Option, } \ No newline at end of file diff --git a/dpts-config/src/database.rs b/dpts-config/src/database.rs new file mode 100644 index 0000000..898ae1f --- /dev/null +++ b/dpts-config/src/database.rs @@ -0,0 +1,45 @@ +use std::time::Duration; + +use serde::Deserialize; + +use crate::Error; + +#[derive(Clone, Debug, Deserialize, PartialEq)] +pub struct DatabaseConfig { + pub url: String, + pub max_connections: Option, + pub min_connections: Option, + pub connect_timeout: Option, + pub acquire_timeout: Option, + pub idle_timeout: Option, + pub max_lifetime: Option, + pub sqlx_logging: bool, +} + +impl TryFrom for DatabaseConfig{ + type Error = Error; + fn try_from(p: PartialDatabaseConfig) -> Result { + Ok(DatabaseConfig{ + url: p.url.ok_or(Error::MissingConfig("url".to_string()))?, + max_connections: p.max_connections, + min_connections: p.min_connections, + connect_timeout: p.connect_timeout, + acquire_timeout: p.acquire_timeout, + idle_timeout: p.idle_timeout, + max_lifetime: p.max_lifetime, + sqlx_logging: p.sqlx_logging.ok_or(Error::MissingConfig("sqlx_logging".to_string()))? + }) + } +} +#[derive(Clone, Debug, Default, Deserialize, PartialEq)] +pub struct PartialDatabaseConfig { + pub url: Option, + pub max_connections: Option, + pub min_connections: Option, + pub connect_timeout: Option, + pub acquire_timeout: Option, + pub idle_timeout: Option, + pub max_lifetime: Option, + pub sqlx_logging: Option +} + diff --git a/dpts-config/src/lib.rs b/dpts-config/src/lib.rs index 469b4c3..0bca5de 100644 --- a/dpts-config/src/lib.rs +++ b/dpts-config/src/lib.rs @@ -1,20 +1,30 @@ +#[cfg(feature="client")] mod client; +mod database; mod error; +#[cfg(feature="server")] mod server; -mod user; + +#[cfg(feature="client")] +pub use client::{ + ClientConfig, + CLIENT_CONFIG, +}; + +pub use database::{ + DatabaseConfig, + PartialDatabaseConfig, +}; pub use error::Error; + +#[cfg(feature="server")] pub use server::{ ServerConfig, PartialServerConfig, SERVER_CONFIG, }; -pub use client::{ - ClientConfig, - PartialClientConfig, -}; - use chrono_tz::{Tz, UTC}; fn get_host_time_zone_or_utc() -> Tz { diff --git a/dpts-config/src/server.rs b/dpts-config/src/server.rs index 113a0dd..5f73810 100644 --- a/dpts-config/src/server.rs +++ b/dpts-config/src/server.rs @@ -1,20 +1,26 @@ use chrono_tz::{Tz, UTC}; -use crate::{get_host_time_zone_or_utc, Error}; +use crate::{ + get_host_time_zone_or_utc, + DatabaseConfig, + PartialDatabaseConfig, + Error +}; use serde::Deserialize; use std::{ + default::Default, str::FromStr, - net::IpAddr + net::IpAddr, }; use tokio::sync::OnceCell; pub static SERVER_CONFIG: OnceServerConfig = OnceServerConfig::const_new(); -#[derive(Deserialize)] +#[derive(Clone, Debug, Deserialize, PartialEq)] pub struct ServerConfig { pub listen_ips: Vec, pub port: u16, - pub database_url: String, pub time_zone: Tz, + pub database: DatabaseConfig } impl ServerConfig { @@ -27,7 +33,7 @@ impl TryFrom for ServerConfig { Ok(ServerConfig{ listen_ips: p.listen_ips.ok_or(Error::MissingConfig("listen_ips".to_string()))?, port: p.port.ok_or(Error::MissingConfig("port".to_string()))?, - database_url: p.database_url.ok_or(Error::MissingConfig("database_url".to_string()))?, + database: p.database.ok_or(Error::MissingConfig("database.*".to_string()))?.try_into()?, time_zone: p.time_zone.ok_or(Error::MissingConfig("time_zone".to_string()))?, }) } @@ -54,12 +60,12 @@ impl OnceServerConfig { } } -#[derive(Deserialize)] +#[derive(Clone, Debug, Deserialize, PartialEq)] pub struct PartialServerConfig { pub listen_ips: Option>, pub port: Option, - pub database_url: Option, + pub database: Option, pub time_zone: Option, } @@ -71,21 +77,11 @@ impl PartialServerConfig { } impl Default for PartialServerConfig { - /// #Examples - /// ``` - /// use dpts_config::PartialServerConfig; - /// let config = PartialServerConfig::default(); - /// assert_eq!(config.listen_ips, Some(vec!["127.0.0.1".parse().unwrap(), "::1".parse().unwrap()])); - /// assert_eq!(config.port, Some(3000)); - /// assert_eq!(config.database_url, None); - /// assert_eq!(config.time_zone, Some(iana_time_zone::get_timezone().unwrap().parse().unwrap())) - /// ``` - /// fn default() -> Self { PartialServerConfig { listen_ips: Some(vec!["127.0.0.1".parse().unwrap(), "::1".parse().unwrap()]), port: Some(3000), - database_url: None, + database: None, time_zone: Some(get_host_time_zone_or_utc()) } } @@ -95,17 +91,36 @@ impl FromStr for PartialServerConfig { type Err = Error; /// #Examples /// ``` - /// use dpts_config::PartialServerConfig; - /// let config: PartialServerConfig = r#" + /// use dpts_config::{ + /// PartialServerConfig, + /// PartialDatabaseConfig, + /// }; + /// use std::{ + /// default::Default, + /// net::IpAddr + /// }; + /// let config_from_str: PartialServerConfig = r#" /// listen_ips = ["0.0.0.0"] /// port = 8000 - /// database_url = "sqlite::memory:" /// time_zone = "Asia/Tokyo" + /// + /// [database] + /// url = "sqlite::memory:" + /// sqlx_logging = true /// "#.parse().unwrap(); - /// assert_eq!(config.listen_ips, Some(vec!["0.0.0.0".parse().unwrap()])); - /// assert_eq!(config.port, Some(8000)); - /// assert_eq!(config.database_url, Some("sqlite::memory:".to_string())); - /// assert_eq!(config.time_zone, Some("Asia/Tokyo".parse().unwrap())) + /// + /// let config: PartialServerConfig = PartialServerConfig{ + /// listen_ips : Some(vec!["0.0.0.0".parse().unwrap()]), + /// port: Some(8000), + /// time_zone: Some(chrono_tz::Asia::Tokyo), + /// database: Some(PartialDatabaseConfig { + /// url: Some("sqlite::memory:".to_string()), + /// sqlx_logging: Some(true), + /// ..Default::default() + /// }), + /// }; + /// + /// assert_eq!(config_from_str, config); /// ``` /// fn from_str(s: &str) -> Result { diff --git a/dpts-config/src/user.rs b/dpts-config/src/user.rs deleted file mode 100644 index e69de29..0000000