As we're shooting towards the v1 release, the number of public methods on the Value type has been called into question.
Value methods
type Value
// Keep:
func (cv Value) ChildKeys() []string
func (cv Value) HasValue() bool
func (cv Value) IsDefault() bool
func (cv Value) LastUpdated() time.Time
func (cv Value) Populate(target interface{}) error
func (cv Value) Get(key string) Value
func (cv Value) Source() string
func (cv Value) Value() interface{}
func (cv Value) WithDefault(value interface{}) Value
// Decision zone ("helpers"):
func (cv Value) AsBool() bool
func (cv Value) AsFloat() float64
func (cv Value) AsInt() int
func (cv Value) AsString() string
func (cv Value) String() string
func (cv Value) TryAsBool() (bool, bool)
func (cv Value) TryAsFloat() (float64, bool)
func (cv Value) TryAsInt() (int, bool)
func (cv Value) TryAsString() (string, bool)
Godoc: https://godoc.org/go.uber.org/config#Value
Reasoning
Most of these helpers look very similar. They rely heavily on the Populate()
functionality and try to mimic what the users would otherwise do should these methods not be available.
Here are a subset of two:
// TryAsBool attempts to return the configuration value as a bool
func (cv Value) TryAsBool() (bool, bool) {
var res bool
err := newValueProvider(cv.Value()).Get(Root).Populate(&res)
return res, err == nil
}
// TryAsFloat attempts to return the configuration value as a float
func (cv Value) TryAsFloat() (float64, bool) {
var res float64
err := newValueProvider(cv.Value()).Get(Root).Populate(&res)
return res, err == nil
}
Options
A
Do nothing. As ever this is the first option.
The main problem for me is that we have a strange subset of types that we consider "worthy" of the own method. What about float32
, what about int16
? What about a []int
?
If we go down this road we'd need some sort of a heuristic to determine what warrants a conversion method.
B
Move these methods into functions in another package. func (cv Value) TryAsInt()
can become func TryAsInt(v Value)
.
For the sake of discussion lets name it package conv
.
This is a very attractive option, as we can have a more comprehensive coverage of available functions and types without diluting the API of Value struct
.
The question here is versioning. Ideally this would be a separate conversion library, or a package here in config that wouldn't be bound by the same semver rules (is that even a thing?)
C
Just flat out nuke the conversion methods, do not even provide a package conv
, as these methods are easy to re-create on a one-by-one basis in the few places that they are used.
I think this makes the API a less approachable, at least initially
D
Along the lines of Option B.
Outsource this to an external library, such as https://github.com/spf13/cast
The user experience (which we can try to slim down) would be:
cast.ToInt(cfg.Get("foo.bar").Value())
E
Open to other ideas as well.