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:
#[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 ass.try_deserialize()}
}
ConfigBuilder::set_default
sets a default value at key:
pub fn set_default<S, T>(self, key: S, value: T) -> Result<Self, ConfigError>
whereS: AsRef<str>,T: Into<Value>,
Such as:
let s = Config::builder().set_default("debug", true)?...
But how to set a member like Database
struct?
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`
- https://www.reddit.com/r/rust/comments/xgbtl0/how_to_properly_use_set_default_method_in/?rdt=37431
- https://www.reddit.com/r/rust/comments/v7744h/rust_crate_configrs_set_custom_struct_as_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
:
impl From<Database> for ValueKind {fn from(value: Database) -> Self {let t = HashMap::<String, Value>::from([("url".into(), value.url.into())]);ValueKind::Table(t)}
}
let s = Config::builder().set_default("database", Database { url: "".into() })?...
For nested struct:
#[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())}
}
let s = Config::builder().set_default("database", Database::default())?...