use config::{Config, Environment, File};
use serde::{Deserialize, de::DeserializeOwned};
use std::{collections::HashMap, fmt::Debug, path::PathBuf};
use validator::Validate;
#[derive(Debug, Clone)]
pub struct NamedConfig<T> {
configs: HashMap<String, T>,
}
impl<T> NamedConfig<T>
where
T: DeserializeOwned + Validate + Clone + Send + Sync + Default + 'static,
{
pub fn get(&'static self, name: &str) -> &'static T {
self.configs.get(name).expect("命名配置不存在")
}
}
#[cfg(feature = "sender")]
pub(crate) fn i18n_config() -> Config {
let config_root = std::env::var("UNI_CONFIG_ROOT")
.map(PathBuf::from)
.unwrap_or(PathBuf::from("./config"));
match Config::builder()
.add_source(File::from(config_root.join("i18n")).required(false))
.build()
{
Ok(c) => c,
Err(e) => {
panic!("加载配置失败:{e}");
}
}
}
pub fn build_config() -> Config {
let config_root = std::env::var("UNI_CONFIG_ROOT")
.map(PathBuf::from)
.unwrap_or(PathBuf::from("./config"));
let env = std::env::var("UNI_ENV").unwrap_or("dev".to_owned());
match Config::builder()
.add_source(File::from(config_root.join("default")).required(false))
.add_source(File::from(config_root.join(env)).required(false))
.add_source(
Environment::with_prefix("UNI")
.separator("__")
.list_separator(","),
)
.build()
{
Ok(c) => c,
Err(e) => {
panic!("加载配置失败:{e}");
}
}
}
pub fn load_name(cfg: &::config::Config) -> String {
match cfg.get::<String>("name") {
Ok(c) => {
let len = c.chars().count();
if len < 2 || len > 10 {
panic!("长度应介于 2 到 10 之间");
}
if !c.chars().all(|c| c.is_ascii_alphabetic()) {
panic!("应为 ASCII 字母");
}
c
}
Err(e) => {
panic!("加载'name'配置失败:{e}");
}
}
}
pub fn load_hostname(cfg: &::config::Config) -> String {
match cfg.get("hostname") {
Ok(c) => c,
Err(e) => {
panic!("加载'hostname'配置失败:{e}");
}
}
}
pub fn load_named_config<T>(config: &Config, section: &str) -> NamedConfig<T>
where
T: DeserializeOwned + Validate + Clone + Default,
{
let configs = match config.get::<HashMap<String, T>>(section) {
Ok(c) => c,
Err(e) => {
panic!("加载命名配置'{section}'失败:{e}");
}
};
for (key, cfg) in &configs {
if let Err(e) = cfg.validate() {
panic!("[{section}.{key}]命名配置验证失败:{e}");
}
}
NamedConfig { configs }
}
pub fn load_named_setting(
config: &config::Config,
section: &str,
) -> HashMap<String, HashMap<String, String>> {
let mut result = HashMap::new();
let input = config
.get::<HashMap<String, config::Value>>(section)
.unwrap_or(HashMap::new());
for (section, value) in input {
let map = match value.try_deserialize::<HashMap<String, String>>() {
Ok(c) => c,
Err(e) => {
panic!("加载命名配置'{section}'失败:{e}");
}
};
result.insert(section, map);
}
result
}
#[derive(Debug, Deserialize, Validate, Clone)]
#[serde(default)]
pub struct SubscribeConfig {
pub hotspot: bool,
pub interval: u64,
pub low: usize,
pub high: usize,
pub retain: u64,
}
impl Default for SubscribeConfig {
fn default() -> Self {
Self {
hotspot: false,
interval: 30,
low: 200,
high: 20000,
retain: 2 * 24,
}
}
}
#[derive(Debug, Deserialize, Validate, Clone)]
#[serde(default)]
pub struct SendConfig {
pub hotspot: bool,
pub interval: u64,
pub retain: u64,
}
impl Default for SendConfig {
fn default() -> Self {
Self {
hotspot: false,
interval: 5,
retain: 30,
}
}
}