Open brsnik opened 1 year ago
Hi!
Thanks for reporting this! Please have a look at the code in #392 - maybe I am missing something, because that example works for me (assuming you use config-rs from master).
The only difference I spot is that you're using the actix_web::main
macro for your main
function, but I doubt that this makes a difference...
FWIW, I also cannot get this working with 0.13.2 and nested structs in my config.
When I use .add_source(config::Environment::with_prefix("TEST"))
then I can successfully use environment variables like TEST_MYSTRUCT.MYFIELD
However, when I use .add_source(config::Environment::with_prefix("TEST").separator("_"))
, no environment variables seem to work at all:
TEST_MYSTRUCT.MYFIELD
-- no longer works, which is expected since the separator should be _
TEST_MYSTRUCT_MYFIELD
-- does not work, which is NOT expectedSomething about separator(...)
does not seem to behave as I would expect
Hi!
Thanks for reporting this! Please have a look at the code in #392 - maybe I am missing something, because that example works for me (assuming you use config-rs from master).
The only difference I spot is that you're using the
actix_web::main
macro for yourmain
function, but I doubt that this makes a difference...
Your example is exactly what I have, but it does not work for me. As for actix_web::main
I am trying to load up env variables and then start an Actix web server in main.
Any chance you can share the original code? Maybe a link to a git repository? So I can investigate a bit better?
@matthiasbeyer Sure thing, sent an invitation for: https://github.com/brsnik/actix-config-rs
Okay so, it works in lowercase app::config::get("dev_server_address")
and without the separator separator("_")
as @ianling suggested.
meh.
All this is really inconvenient. I hope I can make this better in the future with the rewrite I am planning.
Here is the problem:
https://github.com/mehcode/config-rs/blob/master/src/env.rs#L222
// If separator is given replace with `.`
if !separator.is_empty() {
key = key.replace(separator, ".");
}
If I have a struct in my config with a snake-case field (e.g. person.first_name
) and I try to set it using the environment variable PERSON_FIRST_NAME
, this block transforms the key into person.first.name
instead of person.first_name
The (bad) solution that immediately springs to mind is to only replace the first occurrence of the separator with a dot using replacen(..., 1)
instead of replace(...)
, but then that leaves structs whose names contain snake-case broken, as well as deeply nested structs:
PERSON_STRUCT_NAME
to set the struct field person_struct.name
won't work because this solution would set the key to person.struct_name
PERSON_NAME_FIRST_NAME
to set the nested struct field person.name.first_name
won't work because this solution would set the key to person.name_first_name
Hard saying what a good solution would be for this, as I can't think of a way for this crate to know for sure if the environment variable PERSON_NAME_FIRST_NAME
is supposed to mean person.name.first_name
or person_name.first_name
or even just person_name_first_name
....
I honestly fail to see the need for the separator whatsoever. Keys like person.first.name
and person.first_name
are incredibly confusing.
Should be snake case all the way.
This crate allows you to parse a config into an actual struct using .try_deserialize()
, rather than retrieving keys using strings:
My use case is ultimately setting values in my config struct using environment variables, but this gets messy in the cases I mentioned above due to some ambiguous combinations of dots and underscores.
This is something I'm running into now myself and I'm having to resort to using #[serde(alias = "oneWord")]
to allow fields like session_secret_key
to be seen as sessionkey
. Pretty undesirable, but it works.
In this vein, though probably not possible, if we could get config to know what kind of env to fill a field with in maybe some sort of more custom deserialization, that'd could allow people to specify the full env string a field could match to.
(Originally published at: https://jacky.wtf/2022/11/Uqih)
In this vein, though probably not possible, if we could get config to know what kind of env to fill a field with in maybe some sort of more custom deserialization, that'd could allow people to specify the full env string a field could match to.
That is how the popular Go module envconfig
works: https://github.com/kelseyhightower/envconfig#struct-tag-support
That said, Go's syntax works a little better for this, in my opinion, since it's all on one line, vs having an additional #[serde(...)]
line above each field in Rust.
What if for person.first_name
you use a a dunderscore __
as the separator
? Then you could write PERSON__FIRST_NAME
and the replace
would change it to PERSON.FIRST_NAME
?
It will work as expected if used like this:
.add_source(Environment::with_prefix("app").prefix_separator("__").separator("__"))
then
APP__BRAINTREE__MERCHANT_ID
will be correctly parsed to Braintree.merchant_id
with _snakecase setting name.
Probably an example could be added to cover this case.
@nahuakang Great idea. A little weird at first, but it is a perfectly good solution to the problem @ivan-mudrak and @ianling mention above with snake_case struct field names. I'll take that over the serde field name aliasing approach.
It would be good to surface this in the docs, leaving a comment here so I remember to come back later and do this.
I just spent half an hour running a debugger getting to the bottom of this, only to realize that fundamentally _
as the separator while having _
in the variable names will never work, based on the approach in the code. I assumed that it was doing something like 'for each variable in the config schema, check for an env-var', which would be predictable and scale with the size of the schema. But instead it's doing "check each envvar and if it matches the rules, try to insert it to the global config map", without any schema-enriched info, which doesn't work because _
s get translated to .
s. The approach taken also doesn't seem to surface a warning anywhere when a close-but-not-quite envvar was set but is having no impact.
It may also be good to default the separator to __
, since _
will be so common in struct field names.
Getting the following error when trying to get an environmental variable from config-rs:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: configuration property "DEV_SERVER_ADDRESS" not found', src/app/config.rs:14:27 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
ENV variable:
Trying to get the variable in main:
What am I doing wrong?