func TestValidateSelection(t *testing.T) { test := &TestSelectionStruct{A: 100, B: "fish"} var err error for _, field := range structs.Fields(test) { switch field.Name() { case "A": err = validateSelection(field, "50|100|150") if err != nil { t.Error(err) } err = validateSelection(field, "0|25|50") if err == nil { t.Error("validate: Shouldn't match but matched") } case "B": err = validateSelection(field, "fish|cat|dog") if err != nil { t.Error(err) } err = validateSelection(field, "apple|peach|orange") if err == nil { t.Error("validate: Shouldn't match but matched") } } } }
// FilterArgs filters the given arguments and returns a filtered argument list. // It only contains the arguments which are declared in the given configuration // struct. func FilterArgs(conf interface{}, args []string) []string { configArgs := []string{} // need to be declared so we can call it recursively var addFields func(fields []*structs.Field) addFields = func(fields []*structs.Field) { for _, field := range fields { // don't forget nested structs if field.Kind() == reflect.Struct { addFields(field.Fields()) continue } fName := strings.ToLower(strings.Join(camelcase.Split(field.Name()), "-")) val, err := flags.Value(fName, args) if err != nil { continue } configArgs = append(configArgs, "--"+fName, val) } } addFields(structs.Fields(conf)) return configArgs }
func TestValidateRange(t *testing.T) { test := &TestRangeStruct{10, 20, 30.4, "xyz"} var err error for _, field := range structs.Fields(test) { switch field.Name() { case "A": err = validateRange(field, "-1", "30") if err != nil { t.Error(err) } err = validateRange(field, "", "20") if err != nil { t.Error(err) } err = validateRange(field, "11", "") if err == nil { t.Error("validate: Exceeds range but not checked") } err = validateRange(field, "", "9") if err == nil { t.Error("validate: Exceeds range but not checked") } case "B": err = validateRange(field, "-1", "30") if err != nil { t.Error(err) } err = validateRange(field, "", "20") if err != nil { t.Error(err) } err = validateRange(field, "11", "") if err != nil { t.Error(err) } err = validateRange(field, "", "19") if err == nil { t.Error("validate: Exceeds range but not checked") } case "C": err = validateRange(field, "29.0", "30.5") if err != nil { t.Error(err) } err = validateRange(field, "", "30.3") if err == nil { t.Error("validate: Exceeds range but not checked") } case "D": err = validateRange(field, "1", "2") if err != nil { t.Error(err) } } } }
// Validate validates the given struct agaist field's zero values. If // intentionaly, the value of a field is `zero-valued`(e.g false, 0, "") // required tag should not be set for that field. func (e *RequiredValidator) Validate(s interface{}) error { if e.RequiredTagName == "" { e.RequiredTagName = "required" } for _, field := range structs.Fields(s) { if err := e.processField("", field); err != nil { return err } } return nil }
func (e *SelectionValidator) Validate(s interface{}) error { if e.SelectTagName == "" { e.SelectTagName = "select" } for _, field := range structs.Fields(s) { if err := e.processField("", field); err != nil { return err } } return nil }
func (t *TagLoader) Load(s interface{}) error { if t.DefaultTagName == "" { t.DefaultTagName = "default" } for _, field := range structs.Fields(s) { if err := t.processFieldDefaultValue(field); err != nil { return err } } return nil }
//DeepDiff - list fields in A that are missing or not equal to fields in B func (d config) DeepDiff(c Config) (fields []structs.Field, err error) { err = CheckData(c.Data()) if err != nil { return []structs.Field{}, iodine.New(err, nil) } currFields := structs.Fields(d.Data()) newFields := structs.Fields(c.Data()) found := false for _, currField := range currFields { found = false for _, newField := range newFields { if reflect.DeepEqual(currField.Value(), newField.Value()) { found = true } } if !found { fields = append(fields, *currField) } } return fields, nil }
//DeepDiff - list fields in A that are missing or not equal to fields in B func (d config) DeepDiff(c Config) ([]structs.Field, *probe.Error) { var fields []structs.Field err := CheckData(c.Data()) if err != nil { return []structs.Field{}, err.Trace() } currFields := structs.Fields(d.Data()) newFields := structs.Fields(c.Data()) found := false for _, currField := range currFields { found = false for _, newField := range newFields { if reflect.DeepEqual(currField.Value(), newField.Value()) { found = true } } if !found { fields = append(fields, *currField) } } return fields, nil }
func TestReflect(t *testing.T) { test := &TestReflectStruct{} var err error for _, field := range structs.Fields(test) { switch field.Name() { case "A": err = fieldSet(field, "1") if err != nil { t.Error(err) } if test.A != 1 { t.Errorf("A: %d, wanted: %d", test.A, 1) } case "B": err = fieldSet(field, "3.14") if err != nil { t.Error(err) } if test.B != 3.14 { t.Errorf("B: %f, wanted: %f", test.B, 3.14) } case "C": err = fieldSet(field, "1,2,3") if err != nil { t.Error(err) } if test.C[2] != 3 { t.Errorf("C: %s, wanted: %s", test.C, []int{1, 2, 3}) } case "D": err = fieldSet(field, "{\"abc\":1,\"def\":2}") if err != nil { t.Error(err) } if test.D["abc"] != 1 { t.Errorf("D: %s, wanted: %s", test.D, map[string]int{"abc": 1, "def": 2}) } case "E": err = fieldSet(field, `{"abc":"123","def":"456"}`) if err != nil { t.Error(err) } if test.E["abc"] != "123" || test.E["def"] != "456" { t.Errorf("E: %s, wanted: %s", test.E, map[string]string{"abc": "123", "def": "456"}) } } } }
func (e *LengthValidator) Validate(s interface{}) error { if e.MaxlenTagName == "" { e.MaxlenTagName = "maxlen" } if e.MinlenTagName == "" { e.MinlenTagName = "minlen" } for _, field := range structs.Fields(s) { if err := e.processField("", field); err != nil { return err } } return nil }
func TestValidateString(t *testing.T) { test := &TestStringStruct{"seafood"} var err error for _, field := range structs.Fields(test) { switch field.Name() { case "A": err = validateString(field, "foo.*") if err != nil { t.Error(err) } err = validateString(field, "bar.*") if err == nil { t.Error("validate: Shouldn't match but matched") } } } }
func TestValidateLength(t *testing.T) { test := &TestLengthStruct{"xyz"} var err error for _, field := range structs.Fields(test) { switch field.Name() { case "A": err = validateLength(field, "1", "4") if err != nil { t.Error(err) } err = validateLength(field, "5", "6") if err == nil { t.Error("validate: Exceeds range but not checked") } } } }
func getData(data interface{}) (map[string]interface{}, error) { mp := make(map[string]interface{}) val := reflect.ValueOf(data) t := reflect.TypeOf(data) for t.Kind() == reflect.Ptr { val = val.Elem() t = t.Elem() } if t.Kind() == reflect.Map { keys := val.MapKeys() for _, k := range keys { ki := k.Interface() if strkey, ok := ki.(string); ok { mp[strkey] = val.MapIndex(k).Interface() } else { return nil, errors.New("Cannot insert/update map with non-string keys") } } return mp, nil } else if t.Kind() == reflect.Struct { fields := structs.Fields(data) for _, f := range fields { tag := f.Tag("db") if strings.Contains(tag, ",") { tag = strings.Split(tag, ",")[0] } if tag == "" { tag = f.Name() } mp[tag] = f.Value() } return mp, nil } else { return nil, errors.New("Can only insert maps and structs! (got a " + t.Kind().String() + ")") } }
// Load loads the source into the config defined by struct s // Defaults to using the Reader if provided, otherwise tries to read from the // file func (i *INILoader) Load(s interface{}) (err error) { var c *goconfig.ConfigFile if i.Path != "" { c, err = goconfig.LoadConfigFile(i.Path) if err != nil { return err } } else { return ErrSourceNotSet } //Now parse struct fields from ini file //Since INI file doesn't natually corresponds to a golang struct, // most current INI parsers do not provide Json or Toml functions that // directly write into the struct fields, we have to parse by ourselves. //We define the rule as follows // If load from global, then [section] means sub-struct // else [section] means the struct itself // Substructs are represented by dot separated sections // [parent.child.child] and etc. // Slices are separated by comma , such as // array=1,2,3 // Maps are defined by Json style // map={"key":1,"key":2} var section string if i.LoadFromGlobal { section = "" } else { section = structs.New(s).Name() } for _, field := range structs.Fields(s) { if err := i.setField(field, c, section); err != nil { return err } } return nil }
// ExcludeArgs exludes the given arguments declared in the configuration and // returns the remaining arguments. It's the opposite of FilterArgs func ExcludeArgs(conf interface{}, args []string) []string { // need to be declared so we can call it recursively var addFields func(fields []*structs.Field) addFields = func(fields []*structs.Field) { for _, field := range fields { // don't forget nested structs if field.Kind() == reflect.Struct { addFields(field.Fields()) continue } fName := strings.ToLower(strings.Join(camelcase.Split(field.Name()), "-")) args = flags.Exclude(fName, args) } } addFields(structs.Fields(conf)) return args }