Update config for server and client

This commit is contained in:
fluo10 2025-05-03 14:57:15 +09:00
parent bacc24e801
commit aa7f31b396
8 changed files with 196 additions and 85 deletions

View file

@ -8,28 +8,75 @@ use serde::{
}; };
use chrono_tz::{Tz, UTC}; use chrono_tz::{Tz, UTC};
use tokio::sync::OnceCell;
use crate::{get_host_time_zone_or_utc, Error}; 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<ClientConfig>,
}
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<F, T>(&self, f: F) -> &ClientConfig where
F: FnOnce() -> T,
T: Future<Output = ClientConfig>
{
self.inner.get_or_init(f).await
}
}
pub struct ClientConfig { pub struct ClientConfig {
pub time_zone: Tz,
pub storage: ClientStorageConfig,
} }
impl TryFrom<&PartialClientConfig> for ClientConfig {
type Error = Error;
fn try_from(p: &PartialClientConfig) -> Result<ClientConfig, Self::Error> {
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)] #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct PartialClientConfig { pub struct PartialClientConfig {
time_zone: Option<Tz>, pub time_zone: Option<Tz>,
storage: Option<PartialClientStorageConfig>, pub storage: Option<ClientStorageConfig>,
} }
impl PartialClientConfig {
}
impl Default for PartialClientConfig {
fn default() -> Self {
PartialClientConfig {
time_zone: Some(get_host_time_zone_or_utc()),
storage: None
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
use std::path::PathBuf;
const EMPTY_CONFIG_TOML: &str = r#""#; const EMPTY_CONFIG_TOML: &str = r#""#;
static EMPTY_CONFIG_STRUCT: OnceCell<PartialClientConfig> = OnceCell::const_new(); static EMPTY_CONFIG_STRUCT: OnceCell<PartialClientConfig> = OnceCell::const_new();
@ -55,9 +102,7 @@ mod tests {
} }
const LOCAL_STORAGE_CONFIG_TOML: &str = r#"time_zone = "UTC" const LOCAL_STORAGE_CONFIG_TOML: &str = r#"time_zone = "UTC"
storage = "local"
[storage.local]
database_path = "."
"#; "#;
static LOCAL_STORAGE_CONFIG_STRUCT: OnceCell<PartialClientConfig> = OnceCell::const_new(); static LOCAL_STORAGE_CONFIG_STRUCT: OnceCell<PartialClientConfig> = OnceCell::const_new();
@ -65,11 +110,7 @@ database_path = "."
LOCAL_STORAGE_CONFIG_STRUCT.get_or_init(|| async { LOCAL_STORAGE_CONFIG_STRUCT.get_or_init(|| async {
PartialClientConfig{ PartialClientConfig{
time_zone: Some(UTC), time_zone: Some(UTC),
storage: Some(PartialClientStorageConfig::Local( storage: Some(ClientStorageConfig::Local),
PartialClientLocalStorageConfig {
database_path: Some(PathBuf::from_str(".").unwrap()),
}
)),
} }
}).await }).await
} }
@ -83,5 +124,35 @@ database_path = "."
async fn serialize_local_storage_client_config() { async fn serialize_local_storage_client_config() {
assert_eq!(LOCAL_STORAGE_CONFIG_TOML, toml::to_string(get_local_storage_client_config_struct().await).unwrap()); 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<PartialClientConfig> = 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());
}
} }

View file

@ -1,29 +1,18 @@
mod local;
mod remote; mod remote;
pub use local::{ pub use remote::ClientRemoteStorageConfig;
ClientLocalStorageConfig,
PartialClientLocalStorageConfig,
};
pub use remote::{
ClientRemoteStorageConfig,
PartialClientRemoteStorageConfig,
};
use serde::{ use serde::{
Deserialize, Deserialize,
Serialize Serialize
}; };
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ClientStorageConfig { pub enum ClientStorageConfig {
Local(ClientLocalStorageConfig), Local,
Remote(ClientRemoteStorageConfig), Remote(ClientRemoteStorageConfig),
} }
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum PartialClientStorageConfig {
Local(PartialClientLocalStorageConfig),
Remote(PartialClientRemoteStorageConfig),
}

View file

@ -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<PathBuf>,
}

View file

@ -10,13 +10,9 @@ use std::{
net::IpAddr net::IpAddr
}; };
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct ClientRemoteStorageConfig { pub struct ClientRemoteStorageConfig {
pub endpoint: String, pub endpoint: String,
pub access_key: String, pub access_key: String,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct PartialClientRemoteStorageConfig {
pub endpoint: Option<String>,
pub access_key: Option<String>,
} }

View file

@ -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<u32>,
pub min_connections: Option<u32>,
pub connect_timeout: Option<Duration>,
pub acquire_timeout: Option<Duration>,
pub idle_timeout: Option<Duration>,
pub max_lifetime: Option<Duration>,
pub sqlx_logging: bool,
}
impl TryFrom<PartialDatabaseConfig> for DatabaseConfig{
type Error = Error;
fn try_from(p: PartialDatabaseConfig) -> Result<DatabaseConfig, Self::Error> {
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<String>,
pub max_connections: Option<u32>,
pub min_connections: Option<u32>,
pub connect_timeout: Option<Duration>,
pub acquire_timeout: Option<Duration>,
pub idle_timeout: Option<Duration>,
pub max_lifetime: Option<Duration>,
pub sqlx_logging: Option<bool>
}

View file

@ -1,20 +1,30 @@
#[cfg(feature="client")]
mod client; mod client;
mod database;
mod error; mod error;
#[cfg(feature="server")]
mod server; mod server;
mod user;
#[cfg(feature="client")]
pub use client::{
ClientConfig,
CLIENT_CONFIG,
};
pub use database::{
DatabaseConfig,
PartialDatabaseConfig,
};
pub use error::Error; pub use error::Error;
#[cfg(feature="server")]
pub use server::{ pub use server::{
ServerConfig, ServerConfig,
PartialServerConfig, PartialServerConfig,
SERVER_CONFIG, SERVER_CONFIG,
}; };
pub use client::{
ClientConfig,
PartialClientConfig,
};
use chrono_tz::{Tz, UTC}; use chrono_tz::{Tz, UTC};
fn get_host_time_zone_or_utc() -> Tz { fn get_host_time_zone_or_utc() -> Tz {

View file

@ -1,20 +1,26 @@
use chrono_tz::{Tz, UTC}; 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 serde::Deserialize;
use std::{ use std::{
default::Default,
str::FromStr, str::FromStr,
net::IpAddr net::IpAddr,
}; };
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
pub static SERVER_CONFIG: OnceServerConfig = OnceServerConfig::const_new(); pub static SERVER_CONFIG: OnceServerConfig = OnceServerConfig::const_new();
#[derive(Deserialize)] #[derive(Clone, Debug, Deserialize, PartialEq)]
pub struct ServerConfig { pub struct ServerConfig {
pub listen_ips: Vec<IpAddr>, pub listen_ips: Vec<IpAddr>,
pub port: u16, pub port: u16,
pub database_url: String,
pub time_zone: Tz, pub time_zone: Tz,
pub database: DatabaseConfig
} }
impl ServerConfig { impl ServerConfig {
@ -27,7 +33,7 @@ impl TryFrom<PartialServerConfig> for ServerConfig {
Ok(ServerConfig{ Ok(ServerConfig{
listen_ips: p.listen_ips.ok_or(Error::MissingConfig("listen_ips".to_string()))?, listen_ips: p.listen_ips.ok_or(Error::MissingConfig("listen_ips".to_string()))?,
port: p.port.ok_or(Error::MissingConfig("port".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()))?, 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 struct PartialServerConfig {
pub listen_ips: Option<Vec<IpAddr>>, pub listen_ips: Option<Vec<IpAddr>>,
pub port: Option<u16>, pub port: Option<u16>,
pub database_url: Option<String>, pub database: Option<PartialDatabaseConfig>,
pub time_zone: Option<Tz>, pub time_zone: Option<Tz>,
} }
@ -71,21 +77,11 @@ impl PartialServerConfig {
} }
impl Default for 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 { fn default() -> Self {
PartialServerConfig { PartialServerConfig {
listen_ips: Some(vec!["127.0.0.1".parse().unwrap(), "::1".parse().unwrap()]), listen_ips: Some(vec!["127.0.0.1".parse().unwrap(), "::1".parse().unwrap()]),
port: Some(3000), port: Some(3000),
database_url: None, database: None,
time_zone: Some(get_host_time_zone_or_utc()) time_zone: Some(get_host_time_zone_or_utc())
} }
} }
@ -95,17 +91,36 @@ impl FromStr for PartialServerConfig {
type Err = Error; type Err = Error;
/// #Examples /// #Examples
/// ``` /// ```
/// use dpts_config::PartialServerConfig; /// use dpts_config::{
/// let config: PartialServerConfig = r#" /// PartialServerConfig,
/// PartialDatabaseConfig,
/// };
/// use std::{
/// default::Default,
/// net::IpAddr
/// };
/// let config_from_str: PartialServerConfig = r#"
/// listen_ips = ["0.0.0.0"] /// listen_ips = ["0.0.0.0"]
/// port = 8000 /// port = 8000
/// database_url = "sqlite::memory:"
/// time_zone = "Asia/Tokyo" /// time_zone = "Asia/Tokyo"
///
/// [database]
/// url = "sqlite::memory:"
/// sqlx_logging = true
/// "#.parse().unwrap(); /// "#.parse().unwrap();
/// assert_eq!(config.listen_ips, Some(vec!["0.0.0.0".parse().unwrap()])); ///
/// assert_eq!(config.port, Some(8000)); /// let config: PartialServerConfig = PartialServerConfig{
/// assert_eq!(config.database_url, Some("sqlite::memory:".to_string())); /// listen_ips : Some(vec!["0.0.0.0".parse().unwrap()]),
/// assert_eq!(config.time_zone, Some("Asia/Tokyo".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<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {