-
Notifications
You must be signed in to change notification settings - Fork 247
Description
Nested structs for configuration are used pretty often.
When overridden with environment variables this works well if the field names do not have _ in names.
For example with:
#[derive(Deserialize, Debug)]
struct TestConfig {
single: String,
plain: SimpleInner,
}
#[derive(Deserialize, Debug)]
struct SimpleInner {
val: String,
}
Then we can override with:
PREFIX_SINGLE=test
PREFIX_PLAIN_VAL=simple
However, Rust default naming convention for fields is with _, so any fields which has multiple parts like timeout_millis
or host_name
becomes an issue.
The standard expectation for this is that single underscore will still work, like for example with:
PREFIX_INNER_TIMEOUT_MILLIS
, however this does not work because it matches the internal path inner.timeout.millis
instead of inner.timeout_millis
.
Currently is possible to workaround this issue by setting for example a double underscore __
as separator. For:
#[derive(Deserialize, Debug)]
struct TestConfig {
single: String,
plain: SimpleInner,
value_with_multipart_name: String,
inner_config: ComplexInner,
}
#[derive(Deserialize, Debug)]
struct SimpleInner {
val: String,
}
We can use it with:
let environment = Environment::default()
.prefix("PREFIX")
.separator("__");
And then we can override from env vars with:
PREFIX__SINGLE=test
PREFIX__PLAIN__VAL=simple
PREFIX__VALUE_WITH_MULTIPART_NAME=value1
PREFIX__INNER_CONFIG__ANOTHER_MULTIPART_NAME=value2
However, using double underscores also has some issues, is unexpected and is error prone (is easy to mistake _ vs __).
There is a related issue which discusses a bit the problem from the documentation and edge cases perspective:
#596.
There are there some proposals for solving the issue:
Another alternative is to:
3. try to match the combined segments from the nested name with the actual path. There is a draft PR with the implementation here: #703