糊一个rust的分层配置

发布于 2024-06-16  27 次阅读


用到的库

figment clap once_cell

从参数读取

use crate::config::Config;

#[derive(Parser, Serialize, Deserialize)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
    #[clap(short, long, default_value = "tbot.toml", env("TG_BOT_CONF_PATH"))]
    pub conf_path: PathBuf,
    #[command(flatten)]
    pub conf: Config,
}

#[command(flatten)] 用来给clap拉平结构体

Config结构

use crate::{cli::Cli, error::BotError}

pub static CONFIG: Lazy<Config> = Lazy::new(|| {
    let args = Cli::parse();
    let conf = args.conf;
    let path = args.conf_path;

    match Config::figment(&path, conf) {
        Ok(conf) => conf,
        Err(e) => {
            eprintln!("failed to load config {}", e);
            exit(1);
        }
    }
}); 

#[derive(Debug, Args, Serialize, Deserialize)]
pub struct Config {
    #[clap(flatten)]
    pub tgbot: TgBot,
    #[clap(flatten)]
    pub log: LogLevel,
}

#[derive(Debug, Args, Serialize, Deserialize)]
pub struct TgBot {
    #[clap(long = "tgbot-token")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub token: Option<String>,
}

#[derive(Debug, Args, Serialize, Deserialize)]
pub struct LogLevel {
    #[clap(long = "log-level")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub level: Option<Log>,
}

#[derive(Debug, ValueEnum, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Log {
    Info,
    Warn,
    Debug,
    Error,
    Trace,
}

impl Config {
    pub fn figment(path: &Path, conf: Config) -> Result<Self, BotError> {
        let config = Figment::new()
            .merge(Toml::file(path))
            .merge(Env::prefixed("TG_BOT_").split("_"))
            .merge(Serialized::defaults(conf))
            .extract()?;

        Ok(config)
    }
}

#[serde(skip_serializing_if = "Option::is_none")] 用来跳过为None值时序列化,避免None覆盖掉上一层的配置

僕と契約して、魔法少女になってよ!
最后更新于 2024-06-16