mcfg: simplify populateParams

This commit is contained in:
Brian Picciano 2018-08-13 20:05:22 -04:00
parent 2e9790451f
commit be17ee942a
3 changed files with 33 additions and 55 deletions

View File

@ -123,12 +123,8 @@ func (cli SourceCLI) Parse(cfg *Cfg) ([]ParamValue, error) {
func (cli SourceCLI) cliParamVals(cfg *Cfg) (map[string]ParamValue, error) { func (cli SourceCLI) cliParamVals(cfg *Cfg) (map[string]ParamValue, error) {
m := map[string]ParamValue{} m := map[string]ParamValue{}
for _, pv := range cfg.allParamValues() { for _, pv := range cfg.allParamValues() {
key := cliKeyPrefix key := strings.Join(append(pv.Path, pv.Param.Name), cliKeyJoin)
if len(pv.Path) > 0 { m[cliKeyPrefix+key] = pv
key += strings.Join(pv.Path, cliKeyJoin) + cliKeyJoin
}
key += pv.Param.Name
m[key] = pv
} }
return m, nil return m, nil
} }

View File

@ -140,62 +140,23 @@ func (c *Cfg) populateParams(src Source) error {
} }
} }
// first dedupe the params. We use this param struct as the key by which to // dedupe the ParamValues based on their hashes, with the last ParamValue
// dedupe by. Its use depends on the json.Marshaler always ordering fields // taking precedence
// in a marshaled struct the same way, which isn't the best assumption but
// it's ok for now
type param struct {
Path []string `json:",omitempty"`
Name string
}
paramFullName := func(p param) string {
if len(p.Path) == 0 {
return p.Name
}
slice := append(make([]string, 0, len(p.Path)+1), p.Name)
slice = append(slice, p.Path...)
return strings.Join(slice, "-")
}
pvM := map[string]ParamValue{} pvM := map[string]ParamValue{}
for _, pv := range pvs { for _, pv := range pvs {
keyB, err := json.Marshal(param{Path: pv.Path, Name: pv.Name}) pvM[pv.hash()] = pv
if err != nil {
return err
}
pvM[string(keyB)] = pv
} }
// check for required params, again using the param struct and the existing // check for required params
// pvM for _, pv := range c.allParamValues() {
var requiredParams func(*Cfg) []param if !pv.Param.Required {
requiredParams = func(c *Cfg) []param {
var out []param
for _, p := range c.Params {
if !p.Required {
continue continue
} } else if _, ok := pvM[pv.hash()]; !ok {
out = append(out, param{Path: c.Path, Name: p.Name}) return fmt.Errorf("param %q is required", pv.displayName())
}
for _, child := range c.Children {
out = append(out, requiredParams(child)...)
}
return out
}
for _, reqP := range requiredParams(c) {
keyB, err := json.Marshal(reqP)
if err != nil {
return err
} else if _, ok := pvM[string(keyB)]; !ok {
return fmt.Errorf("param %s is required but wasn't populated by any configuration source", paramFullName(reqP))
} }
} }
for _, pv := range pvM { for _, pv := range pvM {
if pv.Into == nil {
continue
}
if err := json.Unmarshal(pv.Value, pv.Into); err != nil { if err := json.Unmarshal(pv.Value, pv.Into); err != nil {
return err return err
} }

View File

@ -1,6 +1,12 @@
package mcfg package mcfg
import "encoding/json" import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
)
// ParamValue describes a value for a parameter which has been parsed by a // ParamValue describes a value for a parameter which has been parsed by a
// Source // Source
@ -10,6 +16,21 @@ type ParamValue struct {
Value json.RawMessage Value json.RawMessage
} }
func (pv ParamValue) displayName() string {
return strings.Join(append(pv.Path, pv.Param.Name), "-")
}
func (pv ParamValue) hash() string {
h := md5.New()
for _, path := range pv.Path {
fmt.Fprintf(h, "pathEl:%q\n", path)
}
fmt.Fprintf(h, "name:%q\n", pv.Param.Name)
hStr := hex.EncodeToString(h.Sum(nil))
// we add the displayName to it to make debugging easier
return pv.displayName() + "/" + hStr
}
func (cfg *Cfg) allParamValues() []ParamValue { func (cfg *Cfg) allParamValues() []ParamValue {
pvs := make([]ParamValue, 0, len(cfg.Params)) pvs := make([]ParamValue, 0, len(cfg.Params))
for _, param := range cfg.Params { for _, param := range cfg.Params {