-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initialization of marshaled types #64
Comments
Could you put up a PR that shows the failure you're talking about with |
Okay, this is a a tricky one. One of envconfig's promises is that it will deeply initialize pointer struct fields. In this case I think {
// https://github.com/sethvargo/go-envconfig/issues/64
name: "custom_decoder_uses_decoder_no_env",
input: &struct {
URL *url.URL
}{},
exp: &struct {
URL *url.URL
}{
URL: &url.URL{},
},
lookuper: MapLookuper(nil),
},
{
// https://github.com/sethvargo/go-envconfig/issues/64
name: "custom_decoder_uses_decoder_env_no_value",
input: &struct {
URL *url.URL `env:"URL"`
}{},
exp: &struct {
URL *url.URL `env:"URL"`
}{
URL: &url.URL{},
},
lookuper: MapLookuper(nil),
},
{
// https://github.com/sethvargo/go-envconfig/issues/64
name: "custom_decoder_uses_decoder_env_with_value",
input: &struct {
URL *url.URL `env:"URL"`
}{},
exp: &struct {
URL *url.URL `env:"URL"`
}{
URL: &url.URL{
Scheme: "https",
Host: "foo.bar",
},
},
lookuper: MapLookuper(map[string]string{
"URL": "https://foo.bar",
}),
}, I think the only thing we could do is to always process the decoder (reverting part of #62), so that decoder interfaces are always processed regardless of whether an environment variable is present. However, that breaks some other things, so I'm not really sure the best path here. I'm open to ideas. /cc @twmb |
I'm confused a little bit, why not add type Config struct {
URL *url.URL `env:"URL,noinit"`
} This is almost the same behavior being desired, except rather than This doesn't solve the use case of not using pointers, type Config struct {
URL url.URL `env:URL`
} In v0.7.0, this would initialize the URL with I agree that not calling unmarshal functions is new behavior as of #64. The #64 fix does feel correct to me -- if I don't have an env var for a field, why would the field be processed with no input? However, this does somewhat conflict conceptually with the always-deep-initialize aspect. AFAICT the only happy path to support both #62 and this issue is a new tag or two that supports one behavior or the other. My real preference is to not deeply initialize structs default, and only deeply initialize if some deep struct has a present env key. This is a breaking API change. |
Giving it some thought, this sounds like the best path forward. I like the idea of envconfig always initializing, and the
I believe this is an actual bug. Looking at the code the parent
I think this should be easily fixable by using |
**:warning: Breaking!** Envconfig no longer runs decoders on unset values! To restore the old behavior, add the `decodeunset` struct field annotation or pass the `DefaultDecodeUnset` configuration option as `true`. Prior to this change, envconfig would always call decoding functions, even for unset or empty values. This proved problematic for some libraries like `url` and `zap` which implement `TextUnmarshaller`, but error on the empty string (#61). #62 changed the behavior to only call the decoder if a value was explicitly provided, but that broke users unexpectedly (#64), so the change was reverted. After much discussion, we decided to keep the existing behavior until the 1.0 release. However, after further reflection, I think we need to support a user-level configurable option. Some fields and structs may still want the decoder to run on empty values. This PR changes envconfig to only process a field if any of the following are true: - A value was provided (the value can be set to the empty string, there is a distinction between "unset" and "set to empty") - A default value was provided - The `decodeunset` struct field tag is set - The `DefaultDecodeUnset` configuration option is true
**:warning: Breaking!** Envconfig no longer runs decoders on unset values! To restore the old behavior, add the `decodeunset` struct field annotation or pass the `DefaultDecodeUnset` configuration option as `true`. Prior to this change, envconfig would always call decoding functions, even for unset or empty values. This proved problematic for some libraries like `url` and `zap` which implement `TextUnmarshaller`, but error on the empty string (#61). #62 changed the behavior to only call the decoder if a value was explicitly provided, but that broke users unexpectedly (#64), so the change was reverted. After much discussion, we decided to keep the existing behavior until the 1.0 release. However, after further reflection, I think we need to support a user-level configurable option. Some fields and structs may still want the decoder to run on empty values. This PR changes envconfig to only process a field if any of the following are true: - A value was provided (the value can be set to the empty string, there is a distinction between "unset" and "set to empty") - A default value was provided - The `decodeunset` struct field tag is set - The `DefaultDecodeUnset` configuration option is true
When processing structs that implement a marshaller, this library has typically left it up to the unmarshaling function to populate the object. For example, given this config.
The result of processing this configuration through env-config would be equivalent to marshaling and unmarshaling a url.URL struct. For example:
Since v0.8.0 this test would fail. It seems that the library is skipping the decoder of a struct field if there are no values set. This generally seems ok for fields that can be controlled with
noinit
,default
tags but in this example, the initialization of fields internal tourl.URL
cannot be controlled with env tags.I think introducing the concept of managed vs unmanaged fields in the configuration will help with this. If a configuration field has an env tag it should follow the rules laid out by the env-config library, which will only use the decoder if a conf value is set (default or otherwise). Any struct fields without the env tag should default to using their underlying decoder no matter what. This will prevent potentially initializing fields that were not intended to be initialized.
I've included a draft PR to demonstrate this.
The text was updated successfully, but these errors were encountered: