func InitConfig(c interface{}, initFuncs ...InitFunc) (err error) { cval := reflect.ValueOf(c) for cval.Kind() == reflect.Ptr { cval = cval.Elem() } cf := cval.FieldByName("Config").Interface().(Config) if cf.Initialized { err = fmt.Errorf("Config %v is already Initialized.", cf) return } names, types, tags := StructFields(c) namesSet := set.NewStringSet() assertions := make(map[string]map[string][]Assertion) for _, key := range names { if !namesSet.HasMember(key) { namesSet.Add(key) assertions[key] = make(map[string][]Assertion) } else { err = fmt.Errorf("Coniguration keys must be unique - got '%s' a second time.", key) fmt.Println(err) return } } // init sets for key, type_ := range types { switch type_ { case "*set.StringSet": cval.FieldByName(key).Set(reflect.ValueOf(set.NewStringSet())) case "*set.IntSet": cval.FieldByName(key).Set(reflect.ValueOf(set.NewIntSet())) case "*set.Int64Set": cval.FieldByName(key).Set(reflect.ValueOf(set.NewInt64Set())) } } initC := Config{ MainConfig: cval, ConfigValues: make(map[string]interface{}), Initialized: false, ConfigKeys: namesSet, KeyAliases: set.NewStringSet(), AliasKeyMap: make(map[string]string), ConfigTypes: types, ConfigTags: tags, Assertions: assertions, LogSet: cf.LogSet, } initC.initFuncs = append( []InitFunc{ InitFunc{ F: initC.setDefaults, ExitOnError: true, }, InitFunc{ F: initC.readFromEnv, ExitOnError: true, }, }, append( initFuncs, InitFunc{ F: initC.Validate, ExitOnError: true, }, )..., ) // get aliases err = initC.extractAliases() if err == nil { // set Assertions err = initC.extractAssertions() if err == nil { log.Println("initC.extractAssertions() OK") } else { return } } else { return } // init functions might reference a global config - we now need to operate // on the actualy object.. cval.FieldByName("Config").Set(reflect.ValueOf(initC)) cfg := cval.FieldByName("Config").Interface().(Config) // run all init functions err = cfg.ReInit() if err == nil { log.Println("initC.ReInit() OK") cval.FieldByName("Initialized").SetBool(true) } return }
func TestDefaultValues(t *testing.T) { cfg := struct { Config BoolSetting bool `default:"true"` StringSetting string `default:"foo"` IntSetting int `default:"23"` FloatSetting float64 `default:"1.681"` SingleValueConfig struct { StringSetting string `default:"bar"` IntSetting int `default:"42"` FloatSetting float64 `default:"23.12"` } SliceConfig struct { StringSliceSetting []string `default:"foo,bar"` IntSliceSetting []int `default:"23,42"` FloatSliceSetting []float64 `default:"1.394,1.112"` } NestedValueConfig struct { StringSetting string `default:"NestedOuterFoo"` InnerNestedValueConfig struct { StringSetting string `default:"NestedInnerFoo"` } } StringSet *set.StringSet IntSet *set.IntSet }{} initFunc := InitFunc{ F: func() (err error) { cfg.StringSet = set.NewStringSet([]string{"foo", "bar"}...) cfg.IntSet = set.NewIntSet([]int{1, 2}...) return }, ExitOnError: false, } err := InitConfig(&cfg, initFunc) cv.Convey(`Initializing the config should pass.`, t, func() { cv.So(err, cv.ShouldBeNil) cv.So(cfg.BoolSetting, cv.ShouldBeTrue) cv.So(cfg.StringSetting, cv.ShouldEqual, "foo") cv.So(cfg.IntSetting, cv.ShouldEqual, 23) cv.So(cfg.FloatSetting, cv.ShouldEqual, 1.681) cv.So(cfg.SingleValueConfig.StringSetting, cv.ShouldEqual, "bar") cv.So(cfg.SingleValueConfig.IntSetting, cv.ShouldEqual, 42) cv.So(cfg.SingleValueConfig.FloatSetting, cv.ShouldEqual, 23.12) cv.So(cfg.SliceConfig.StringSliceSetting, cv.ShouldContain, "foo") cv.So(cfg.SliceConfig.StringSliceSetting, cv.ShouldContain, "bar") cv.So(cfg.SliceConfig.IntSliceSetting, cv.ShouldContain, 23) cv.So(cfg.SliceConfig.IntSliceSetting, cv.ShouldContain, 42) cv.So(cfg.SliceConfig.FloatSliceSetting, cv.ShouldContain, 1.394) cv.So(cfg.SliceConfig.FloatSliceSetting, cv.ShouldContain, 1.112) cv.So(cfg.NestedValueConfig.StringSetting, cv.ShouldEqual, "NestedOuterFoo") cv.So(cfg.NestedValueConfig.InnerNestedValueConfig.StringSetting, cv.ShouldEqual, "NestedInnerFoo") cv.So(cfg.StringSet.HasMembers("foo", "bar"), cv.ShouldBeTrue) cv.So(cfg.IntSet.HasMembers(1, 2), cv.ShouldBeTrue) }) }
func (c *Config) setValue(key string, value interface{}) (err error) { var field reflect.Value // fmt.Println("%", key, value) field, err = c.FieldForKey(key) if err != nil { return } ifaces, ok := value.([]interface{}) if ok { fmt.Println("[]interface{}...") var val interface{} for _, iface := range ifaces { // fmt.Println("> got", reflect.TypeOf(iface)) switch c.ConfigTypes[key] { case "bool": val = iface.(bool) case "[]bool": if reflect.TypeOf(val) == nil { val = []bool{} } val = append(val.([]bool), iface.(bool)) case "string": val = iface.(string) case "[]string": if reflect.TypeOf(val) == nil { val = []string{} } val = reflect.Append( reflect.ValueOf(val), reflect.ValueOf(iface), ).Interface() case "int": val = iface.(int) case "[]int": if reflect.TypeOf(val) == nil { val = []int{} } val = append(val.([]int), iface.(int)) case "float64": val = iface.(float64) case "[]float64": if reflect.TypeOf(val) == nil { val = []float64{} } val = append(val.([]float64), iface.(float64)) default: err = fmt.Errorf("Cannot set config from %v", value) fmt.Println("nope", err) return } err = c.setValue(key, val) if err != nil { break } } return } // fmt.Printf("set %s to a %v with value %v\n", key, c.ConfigTypes[key], value) // fmt.Println("-- Got", reflect.TypeOf(value)) var is interface{} switch c.ConfigTypes[key] { case "bool": var v bool v, ok := value.(bool) if !ok { v, err = boolFromInterface(value) if err != nil { return } } field.SetBool(v) case "string": var v string v, ok := value.(string) if !ok { vs, ok := value.([]string) if ok { v = vs[0] } } field.SetString(v) case "int": var v int64 v, ok := value.(int64) if !ok { v, err = intFromInterface(value) if err != nil { return } } field.SetInt(v) case "float64": var v float64 v, ok := value.(float64) if !ok { v, err = floatFromInterface(value) if err != nil { return } } field.SetFloat(v) case "[]string", "*set.StringSet": // log.Println("[]string", "*set.StringSet") var v []string v, ok := value.([]string) if !ok { var type_ string is, err = sliceFromStrings(value, type_) if err != nil { return } v = is.([]string) } if c.ConfigTypes[key] == "*set.StringSet" { field.Set(reflect.ValueOf(set.NewStringSet(v...))) } else { field.Set(reflect.ValueOf(v)) } case "[]int", "*set.IntSet": var v []int v, ok := value.([]int) if !ok { var type_ int is, err = sliceFromStrings(value, type_) if err != nil { return } v = is.([]int) } if c.ConfigTypes[key] == "*set.IntSet" { field.Set(reflect.ValueOf(set.NewIntSet(v...))) } else { field.Set(reflect.ValueOf(v)) } case "[]float64": var v []float64 v, ok := value.([]float64) if !ok { var type_ float64 is, err = sliceFromStrings(value, type_) if err != nil { return } v = is.([]float64) } field.Set(reflect.ValueOf(v)) case "interface{}": err = fmt.Errorf("Set interface{} - not implemented") case "[]interface{}": err = fmt.Errorf("Set []interface{} - not implemented") default: err = fmt.Errorf("Cannot deal with %v.", c.ConfigTypes[key]) } if c.LogSet && err == nil { fld, _ := c.FieldForKey(key) log.Printf("Set %s to %v.\n", key, fld.Interface()) } return }