Options to mutate value
Itsindigo opened this issue · comments
Context
In my application, I've had to base64 encode one of my environment variables because Docker does not support multi-line values in a .env
file.
I was wondering if it would be possible to add functionality to mutate environment variables at parse time and potentially return a different type.
I've tried a few different approaches, which I'll outline below.
Custom Parser Using a FuncMap
type B64 string
type AppConfig struct {
Coinbase CoinbaseConfig
}
type CoinbaseConfig struct {
ApiKeyName string `env:"CB_API_KEY,required"`
Secret B64 `env:"CB_API_PRIVACY_KEY_B64,required"`
}
func customParserOptions() env.Options {
return env.Options{FuncMap: map[reflect.Type]env.ParserFunc{
reflect.TypeOf(B64("")): func(v string) (interface{}, error) {
// do some decode logic
return B64(v), nil
},
}}
}
cfg := AppConfig{}
err := env.ParseWithOptions(&cfg, customParserOptions())
The problem with this approach is that you create a new cfg
type when mutating from B64
to string
, which is not allowed.
Attempt to Mutate with OnSet
Hook
I explored checking the tag for a substring and decoding if there was a match. While I can decode here, the issue is that OnSet
does not allow for direct mutation or returning values to be handled elsewhere.
return env.Options{
OnSet: func(tag string, value interface{}, isDefault bool) {
if strings.HasPrefix(tag, "B64_") || strings.HasSuffix(tag, "_B64") {
// Perform decode logic here
}
}
}
I tried currying the function by passing pointers to my config variable and an error pointer, but the issue arises once you have decoded, which struct property do you assign the new value to?
This lead me to try passing an map of struct properties to callbacks, but the code quickly became very convoluted and I think we can probably do better.
Suggestion: Would it be possible to pass a pointer to the parsed value that OnSet
can mutate, and allow OnSet
to return an error that could be bubbled up?
Attempt to Process Field with Custom FieldParams
I looked into doing something like this, but the processFieldFn
/ FieldParams
aren't exposed in the public API:
type CoinbaseConfig struct {
ApiKeyName string `env:"CB_API_KEY,required"`
Secret string `env:"CB_API_PRIVACY_KEY_B64,required,b64"`
}
Suggestion: Could the API be extended to support custom processors?
Let me know your thoughts and whether or not I'm missing something obvious!
If you think any of these suggestions make sense to implement, let me know and I'd be happy to help with the implementation!
Thanks :)
You can do so using text unmarshaller, see example:
Line 2018 in c4db909
Awesome, thank you