How to set_default() using config-rs crate

How to set_default() using config-rs crate

(Jin Qing's Column, Nov., 2024)

config is a configuration system for Rust applications.

Example of examples\hierarchical-env:

rust 复制代码
#[derive(Debug, Deserialize)]
#[allow(unused)]
struct Database {
    url: String,
}

#[derive(Debug, Deserialize)]
#[allow(unused)]
pub(crate) struct Settings {
    debug: bool,
    database: Database,
}

impl Settings {
    pub(crate) fn new() -> Result<Self, ConfigError> {
        let s = Config::builder()
            .add_source(File::with_name("examples/hierarchical-env/config/default"))
            .build()?;

        // You can deserialize (and thus freeze) the entire configuration as
        s.try_deserialize()
    }
}

ConfigBuilder::set_default sets a default value at key:

rust 复制代码
pub fn set_default<S, T>(self, key: S, value: T) -> Result<Self, ConfigError>
where
    S: AsRef<str>,
    T: Into<Value>,

Such as:

rust 复制代码
        let s = Config::builder()
            .set_default("debug", true)?
            ...

But how to set a member like Database struct?

rust 复制代码
        let s = Config::builder()
            .set_default("database", Database{url: "".into()})?
            ...

fails:

复制代码
error[E0277]: the trait bound `ValueKind: From<Database>` is not satisfied
   --> examples\hierarchical-env\settings.rs:50:38
    |
50  |             .set_default("database", Database{url: "".into()})?
    |              -----------             ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<Database>` is not implemented for `ValueKind`, which is required by `Database: Into<Value>`
    |              |
    |              required by a bound introduced by this call
    |
    = help: the following other types implement trait `From<T>`:
              <ValueKind as From<&'a str>>
              ...
              <ValueKind as From<i32>>
            and 9 others
    = note: required for `Database` to implement `Into<ValueKind>`
    = note: required for `Value` to implement `From<Database>`
    = note: 1 redundant requirement hidden
    = note: required for `Database` to implement `Into<Value>`
note: required by a bound in `ConfigBuilder::<St>::set_default`
   --> D:\github\config-rs\src\builder.rs:156:12
    |
153 |     pub fn set_default<S, T>(mut self, key: S, value: T) -> Result<Self>
    |            ----------- required by a bound in this associated function
...
156 |         T: Into<Value>,
    |            ^^^^^^^^^^^ required by this bound in `ConfigBuilder::<St>::set_default`

The above links suggest to use ValueKind:

复制代码
        let s = Config::builder()
            .set_default(
                "database",
                ValueKind::Table(HashMap::<String, Value>::from([("url".into(), "".into())])),
            )?
            ...

The ergonomic way is to implement From<Database> for ValueKind:

rust 复制代码
impl From<Database> for ValueKind {
    fn from(value: Database) -> Self {
        let t = HashMap::<String, Value>::from([("url".into(), value.url.into())]);
        ValueKind::Table(t)
    }
}
rust 复制代码
        let s = Config::builder()
            .set_default("database", Database { url: "".into() })?
            ...

For nested struct:

rust 复制代码
#[derive(Debug, Default, Deserialize)]
#[allow(unused)]
struct Database {
    url: String,
    other: Other,
}

#[derive(Debug, Default, Deserialize)]
#[allow(unused)]
struct Other {}

impl From<Database> for ValueKind {
    fn from(value: Database) -> Self {
        let t = HashMap::<String, Value>::from([
            ("url".into(), value.url.into()),
            ("other".into(), value.other.into()),
        ]);
        ValueKind::Table(t)
    }
}

impl From<Other> for ValueKind {
    fn from(_value: Other) -> Self {
        ValueKind::Table(HashMap::new())
    }
}
rust 复制代码
        let s = Config::builder()
            .set_default("database", Database::default())?
            ...
相关推荐
doiito几秒前
【Agent Harness】Gliding Horse 设计细节 -- 不跟风开发自己的AI Agent
架构·rust·agent
doiito2 小时前
【Agent Harness】Gliding Horse 核心设计理念,不跟风开发自己的AI Agent
ai·rust·架构设计·系统设计·ai agent
花褪残红青杏小12 小时前
Rust图像处理第6节- 均值模糊 & 中值模糊:3×3 邻域的两种经典玩法
rust·webassembly·图形学
子兮曰16 小时前
前端工具链的「Rust 化」:一场没有赢家的军备竞赛?
前端·后端·rust
星栈18 小时前
写 Dioxus Demo 不难,难的是把它写成项目
前端·rust·前端框架
mCell20 小时前
【锐评】桌面端技术营销:别拿跑分当工程判断
前端·rust·electron
武子康1 天前
调查研究-201 Rust 里的 dev build 和 release build:为什么同一份代码性能差这么多?
后端·架构·rust
doiito1 天前
【Agent Harness】Gliding Horse 的 L2 作战地图:让多 Agent 协作从“摸黑”变成“透明”
ai·rust·架构设计·系统设计·ai agent
星栈2 天前
我用 Rust + Dioxus 做了个全栈跨平台笔记应用:再把新建、编辑和交付补上
前端·rust·前端框架
独孤留白2 天前
从C到Rust:基本类型 C 的隐式不确定 vs Rust 的显式确定
rust