| Crates.io | secra-config |
| lib.rs | secra-config |
| version | 0.1.1 |
| created_at | 2026-01-20 09:55:16.03877+00 |
| updated_at | 2026-01-20 11:17:26.297193+00 |
| description | An extensible Rust config loader: load from TOML file path with modular get/get_as APIs, designed to extend to Consul/Nacos. |
| homepage | |
| repository | https://github.com/BadassFree/secra-config |
| max_upload_size | |
| id | 2056253 |
| size | 35,921 |
一个可扩展的 Rust 配置获取库:先支持从指定 TOML 文件路径加载配置,并提供统一的 get(module_name) 接口获取模块配置;后续可以通过实现 ConfigSource 轻松接入 Consul / Nacos 等配置中心。
get(module_name) 返回对应模块配置(JSON 视图)。get_as::<T>(module_name) 直接反序列化成你的结构体。ConfigSource,未来新增 Consul/Nacos 只需实现一个 Source。RwLock:多读共享、少写互斥)。reload_if_changed():指纹未变则跳过加载/解析。在你的项目 Cargo.toml 中添加依赖(示例):
[dependencies]
secra-config = { path = "../secra-config" }
如果你发布到 crates.io 后,可以改为版本号依赖。
本库约定:TOML 顶层 key 即 module 名称。每个 module 是一个 TOML table。
示例 config.toml:
[db]
url = "postgres://localhost"
pool = 10
[redis]
addr = "127.0.0.1:6379"
db = 0
[feature]
enabled = true
读取时:
get("db") 得到 db 的整体配置get("redis") 得到 redis 的整体配置use secra_config::Config;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1) 给出 toml 配置文件路径
let cfg = Config::from_toml_file("config.toml")?;
// 2) get(module_name) 获取模块配置(JSON 视图)
let db = cfg.get("db").expect("missing [db] module");
let url = db.get("url").and_then(|v| v.as_str()).unwrap_or_default();
println!("db.url = {url}");
Ok(())
}
默认会从环境变量判断当前环境,并在指定目录下选择不同的 TOML 文件:
prod => config.prod.tomldev => config.dev.tomllocal => config.local.toml推荐的目录结构(示例):
your-app/
config/
config.dev.toml
config.prod.toml
config.local.toml
环境变量优先级(从高到低):
SECRA_CONFIG_PATH:直接指定配置文件路径(最高优先级)SECRA_CONFIG_ENV / APP_ENV / RUST_ENV:设置环境(支持 prod|production, dev|development, local)常见用法(示例):
# 选择生产环境配置文件:./config/config.prod.toml
export SECRA_CONFIG_ENV=prod
# 或者直接指定绝对/相对路径(将忽略 SECRA_CONFIG_ENV 等)
export SECRA_CONFIG_PATH=./config/config.local.toml
示例:
use secra_config::Config;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 例如:在运行时设置 SECRA_CONFIG_ENV=prod
// 然后从 ./config/ 目录下读取 config.prod.toml
let cfg = Config::from_env_toml_dir("./config")?;
let db = cfg.get("db").unwrap();
println!("db = {db}");
Ok(())
}
get(module_name):动态读取(JSON 视图)get 返回 Option<serde_json::Value>,适合:
use secra_config::Config;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cfg = Config::from_toml_file("config.toml")?;
if let Some(feature) = cfg.get("feature") {
let enabled = feature.get("enabled").and_then(|v| v.as_bool()).unwrap_or(false);
println!("feature.enabled = {enabled}");
}
Ok(())
}
get_as::<T>(module_name):强类型读取(推荐)强类型读取更适合:
use secra_config::Config;
#[derive(serde::Deserialize)]
struct DbCfg {
url: String,
pool: u32,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cfg = Config::from_toml_file("config.toml")?;
let db: DbCfg = cfg.get_as("db")?;
println!("db.pool = {}", db.pool);
Ok(())
}
reload()当你希望在某个事件发生时(例如收到信号、管理接口触发)强制刷新配置:
use secra_config::Config;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cfg = Config::from_toml_file("config.toml")?;
// ... 运行中 ...
cfg.reload()?; // 强制重新加载
Ok(())
}
reload_if_changed()当你希望避免频繁“无意义解析”:
Ok(false)Ok(true)use secra_config::Config;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cfg = Config::from_toml_file("config.toml")?;
if cfg.reload_if_changed()? {
println!("config changed and reloaded");
}
Ok(())
}
当前
TomlFileSource使用path + 文件长度 + mtime作为 fingerprint(兼顾成本与效果)。
库不强行创建后台线程(更贴合服务端/异步运行时的控制权),提供一个轮询闭包,方便你放进自己的线程/任务里:
use secra_config::Config;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cfg = Config::from_toml_file("config.toml")?;
let mut tick = cfg.poll_reload_loop(Duration::from_secs(2));
loop {
tick()?; // 内部会 reload_if_changed + sleep
// ... 你的业务逻辑 ...
}
}
默认最大 TOML 文件大小为 2MiB(可调整)。如果文件超过限制,会返回错误,避免超大文件导致内存/解析压力。
use secra_config::{Config, TomlFileSource};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let source = TomlFileSource::new("config.toml").with_max_bytes(4 * 1024 * 1024);
let cfg = Config::from_source(source)?;
let _ = cfg.get("db");
Ok(())
}
本库把“配置从哪里来”抽象为 ConfigSource:
load():拉取并解析配置,返回 ConfigSnapshotfingerprint():可选的变更检测(例如使用版本号、ETag、ModifyIndex、Revision 等)你只需要实现它,就能复用 Config 的缓存、并发读、reload 逻辑。
use secra_config::{Config, ConfigError, ConfigSnapshot, ConfigSource};
use serde_json::json;
use std::collections::BTreeMap;
use std::time::SystemTime;
struct MySource;
impl ConfigSource for MySource {
fn load(&self) -> Result<ConfigSnapshot, ConfigError> {
// 1) 从外部系统/文件/数据库拿到原始配置(这里用示例数据代替)
let mut modules = BTreeMap::new();
modules.insert("db".to_string(), json!({ "url": "postgres://x", "pool": 10 }));
Ok(ConfigSnapshot {
modules,
fingerprint: Some("v1".to_string()),
loaded_at: SystemTime::now(),
})
}
fn fingerprint(&self) -> Result<Option<String>, ConfigError> {
// 例如返回 Consul 的 ModifyIndex / Nacos 的 revision / HTTP 的 ETag
Ok(Some("v1".to_string()))
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cfg = Config::from_source(MySource)?;
let db = cfg.get("db").unwrap();
println!("{db}");
Ok(())
}
ModifyIndexmd5 / revision(以官方返回为准)BTreeMap<String, serde_json::Value>,保持 Config 的通用性reload_if_changed() 做轻量轮询get 返回 JSON,而不是 toml::Value?为了更好的通用性与 DX:
Value 在 Rust 生态中更常用(与 Web/日志/动态配置集成更方便)get_as 直接走 serde_json::from_value,类型反序列化体验更统一安全。内部使用 Arc<RwLock<ConfigSnapshot>>:
使用 snapshot():
let snap = cfg.snapshot();
println!("{:?}", snap.modules.keys().collect::<Vec<_>>());