func (e *SelectionValidator) processField(fieldName string, field *structs.Field) error { fieldName += field.Name() switch field.Kind() { case reflect.Struct: // this is used for error messages below, when we have an error at the // child properties add parent properties into the error message as well fieldName += "." for _, f := range field.Fields() { if err := e.processField(fieldName, f); err != nil { return err } } default: if field.IsZero() { return nil } selectionStr := field.Tag(e.SelectTagName) if selectionStr == "" { return nil } err := validateSelection(field, selectionStr) if err != nil { return err } } return nil }
// setFieldValue will try to set the value of a given field to v, which is // given as string and converted to the field type as required. func setFieldValue(field *structs.Field, v string) error { switch field.Kind() { case reflect.String: err := field.Set(v) if err != nil { return err } case reflect.Int, reflect.Int64: i, err := strconv.Atoi(v) if err != nil { return err } err = field.Set(i) if err != nil { return err } case reflect.Float64: f, err := strconv.ParseFloat(v, 64) if err != nil { return err } err = field.Set(f) if err != nil { return err } default: return fmt.Errorf("cannot set field value for field kind %s", field.Kind()) } return nil }
func (e *RequiredValidator) processField(fieldName string, field *structs.Field) error { fieldName += field.Name() switch field.Kind() { case reflect.Struct: // this is used for error messages below, when we have an error at the // child properties add parent properties into the error message as well fieldName += "." for _, f := range field.Fields() { if err := e.processField(fieldName, f); err != nil { return err } } default: val := field.Tag(e.RequiredTagName) if val != "true" && val != "1" { return nil } if field.IsZero() { return fmt.Errorf("Field '%s' is required", fieldName) } } return nil }
// processField gets leading name for the env variable and combines the current // field's name and generates environemnt variable names recursively func (e *EnvironmentLoader) processField(prefix string, field *structs.Field) error { fieldName := e.generateFieldName(prefix, field) switch { case field.Kind() == reflect.Struct && !implementsTextUnmarshaler(field): for _, f := range field.Fields() { if err := e.processField(fieldName, f); err != nil { return err } } case field.Kind() == reflect.Ptr: field.InitElem() return e.processField(prefix, field) default: v := os.Getenv(fieldName) if v == "" { return nil } if err := fieldSet(field, v); err != nil { return err } } return nil }
func validateSelection(field *structs.Field, selection string) error { selectionSet := strings.Split(selection, "|") switch field.Kind() { case reflect.Int: i := field.Value().(int) s := strconv.Itoa(i) for _, selection := range selectionSet { if s == selection { return nil } } return fmt.Errorf("validate: Field %s value %s, not in selection %s.", field.Name(), s, selection) case reflect.String: s := field.Value().(string) for _, selection := range selectionSet { if s == selection { return nil } } return fmt.Errorf("validate: Field %s value %s, not in selection %s.", field.Name(), s, selection) default: return nil } return nil }
// processField gets tagName and the field, recursively checks if the field has the given // tag, if yes, sets it otherwise ignores func (t *TagLoader) processField(tagName string, field *structs.Field) error { switch { case field.Kind() == reflect.Struct && !implementsTextUnmarshaler(field): for _, f := range field.Fields() { if err := t.processField(tagName, f); err != nil { return err } } case field.Kind() == reflect.Ptr: field.InitElem() return t.processField(tagName, field) default: defaultVal := field.Tag(t.DefaultTagName) if defaultVal == "" { return nil } err := fieldSet(field, defaultVal) if err != nil { return err } } return nil }
// processField gets leading name for the env variable and combines the current // field's name and generates environemnt variable names recursively func (e *EnvironmentLoader) processField(prefix string, field *structs.Field) error { fieldName := e.generateFieldName(prefix, field) switch field.Kind() { case reflect.Struct: for _, f := range field.Fields() { if err := e.processField(fieldName, f); err != nil { return err } } default: envTag := field.Tag(e.EnvTagName) var v string if envTag != "" { v = os.Getenv(envTag) } if v == "" { v = os.Getenv(fieldName) } if v == "" { return nil } if err := fieldSet(field, v); err != nil { return err } } return nil }
func (i *INILoader) setField(field *structs.Field, c *goconfig.ConfigFile, section string) error { //first process each subfield of a struct field switch field.Kind() { case reflect.Struct: for _, f := range field.Fields() { var subsection string if section == "" { subsection = field.Name() } else { subsection = section + "." + field.Name() } if err := i.setField(f, c, subsection); err != nil { return err } } default: v, err := c.GetValue(section, field.Name()) if err == nil && v != "" { err := fieldSet(field, v) if err != nil { return err } } } return nil }
func (e *RangeValidator) processField(fieldName string, field *structs.Field) error { fieldName += field.Name() switch field.Kind() { case reflect.Struct: // this is used for error messages below, when we have an error at the // child properties add parent properties into the error message as well fieldName += "." for _, f := range field.Fields() { if err := e.processField(fieldName, f); err != nil { return err } } default: if field.IsZero() { //if not initialized, we won't validate it return nil } minStr := field.Tag(e.MinTagName) maxStr := field.Tag(e.MaxTagName) if minStr == "" && maxStr == "" { return nil } err := validateRange(field, minStr, maxStr) if err != nil { return err } } return nil }
// processField gets tagName and the field, recursively checks if the field has the given // tag, if yes, sets it otherwise ignores func (t *TagLoader) processFieldDefaultValue(field *structs.Field) error { //first process each subfield of a struct field switch field.Kind() { case reflect.Struct: for _, f := range field.Fields() { if err := t.processFieldDefaultValue(f); err != nil { return err } } default: //Set default value for the field itself, including struct field // If there's a default value tag for struct field, it should be in Json format defaultVal := field.Tag(t.DefaultTagName) if defaultVal == "" { return nil } err := fieldSet(field, defaultVal) if err != nil { return err } } return nil }
func parseValue(field *structs.Field, val string) (interface{}, error) { switch field.Kind() { case reflect.Int: v, err := strconv.Atoi(val) if err != nil { return nil, err } return v, nil case reflect.Float64: v, err := strconv.ParseFloat(val, 64) if err != nil { return nil, err } return v, nil case reflect.Bool: v, err := strconv.ParseBool(val) if err != nil { return nil, err } return v, nil default: fieldVal := field.Value() if _, casted := fieldVal.(bson.ObjectId); casted { if bson.IsObjectIdHex(val) { return bson.ObjectIdHex(val), nil } else { return nil, fmt.Errorf("should be bson.ObjectId hex") } } if _, casted := (fieldVal).(time.Time); casted { v := &time.Time{} return v, v.UnmarshalText([]byte(val)) } if convertable, casted := fieldVal.(Converter); casted { converted, err := convertable.Convert(val) if err != nil { return nil, err } if enum, casted := fieldVal.(Enumer); casted { enumValues := enum.Enum() for _, eV := range enumValues { if eV == converted { return converted, nil } } return nil, fmt.Errorf("should be one of %v", enumValues) } return converted, nil } return val, nil } }
func validateString(field *structs.Field, regex string) error { switch field.Kind() { case reflect.String: i := field.Value().(string) matched, err := regexp.MatchString(regex, i) if err == nil && !matched { return fmt.Errorf("validate: Field %s value %s, regex %s not matched.", field.Name(), i, regex) } } return nil }
// printField prints the field of the config struct for the flag.Usage func (e *EnvironmentLoader) printField(prefix string, field *structs.Field) { fieldName := e.generateFieldName(prefix, field) switch field.Kind() { case reflect.Struct: for _, f := range field.Fields() { e.printField(fieldName, f) } default: fmt.Println(" ", fieldName) } }
// printField prints the field of the config struct for the flag.Usage func (e *EnvironmentLoader) printField(prefix string, field *structs.Field) { fieldName := e.generateFieldName(prefix, field) if field.IsExported() { switch { case field.Kind() == reflect.Struct && !implementsTextUnmarshaler(field): for _, f := range field.Fields() { e.printField(fieldName, f) } default: fmt.Println(" ", fieldName) } } }
// processField generates a flag based on the given field and fieldName. If a // nested struct is detected, a flag for each field of that nested struct is // generated too. func (f *FlagLoader) processField(fieldName string, field *structs.Field) error { if f.CamelCase { fieldName = strings.Join(camelcase.Split(fieldName), "-") } switch field.Kind() { case reflect.Struct: for _, ff := range field.Fields() { flagName := field.Name() + "-" + ff.Name() if f.Flatten { // first check if it's set or not, because if we have duplicate // we don't want to break the flag. Panic by giving a readable // output f.flagSet.VisitAll(func(fl *flag.Flag) { if strings.ToLower(ff.Name()) == fl.Name { // already defined panic(fmt.Sprintf("flag '%s' is already defined in outer struct", fl.Name)) } }) flagName = ff.Name() } if err := f.processField(flagName, ff); err != nil { return err } } default: // Add custom prefix to the flag if it's set if f.Prefix != "" { fieldName = f.Prefix + "-" + fieldName } // we only can get the value from expored fields, unexported fields panics if field.IsExported() { // use built-in or custom flag usage message flagUsageFunc := flagUsageDefault if f.FlagUsageFunc != nil { flagUsageFunc = f.FlagUsageFunc } f.flagSet.Var(newFieldValue(field), flagName(fieldName), flagUsageFunc(fieldName)) } } return nil }
// fieldSet sets field value from the given string value. It converts the // string value in a sane way and is usefulf or environment variables or flags // which are by nature in string types. func fieldSet(field *structs.Field, v string) error { // TODO: add support for other types switch field.Kind() { case reflect.Bool: val, err := strconv.ParseBool(v) if err != nil { return err } if err := field.Set(val); err != nil { return err } case reflect.Int: i, err := strconv.Atoi(v) if err != nil { return err } if err := field.Set(i); err != nil { return err } case reflect.String: field.Set(v) case reflect.Slice: // TODO add other typed slice support if _, ok := field.Value().([]string); !ok { return errors.New("can't set on non string slices") } if err := field.Set(strings.Split(v, ",")); err != nil { return err } case reflect.Float64: f, err := strconv.ParseFloat(v, 64) if err != nil { return err } if err := field.Set(f); err != nil { return err } default: return fmt.Errorf("multiconfig: not supported type: %s", field.Kind()) } return nil }
func validateLength(field *structs.Field, minlenStr string, maxlenStr string) error { switch field.Kind() { case reflect.String: i := field.Value().(string) l := len(i) min, minErr := strconv.Atoi(minlenStr) max, maxErr := strconv.Atoi(maxlenStr) if (minErr == nil && l < min) || (maxErr == nil && l > max) { return fmt.Errorf("validate: Field %s value %s, length limit: [%s, %s]", field.Name(), i, minlenStr, maxlenStr) } default: return nil } return nil }
// processField generates a flag based on the given field and fieldName. If a // nested struct is detected, a flag for each field of that nested struct is // generated too. func (f *FlagLoader) processField(flagSet *flag.FlagSet, fieldName string, field *structs.Field) error { if !field.IsExported() { return nil } if f.CamelCase { fieldName = strings.Join(camelcase.Split(fieldName), "-") } switch { case field.Kind() == reflect.Struct && !implementsTextUnmarshaler(field): for _, ff := range field.Fields() { flagName := fieldName + "-" + ff.Name() if f.Flatten { // first check if it's set or not, because if we have duplicate // we don't want to break the flag. Panic by giving a readable // output flagSet.VisitAll(func(fl *flag.Flag) { if strings.ToLower(ff.Name()) == fl.Name { // already defined panic(fmt.Sprintf("flag '%s' is already defined in outer struct", fl.Name)) } }) flagName = ff.Name() } if err := f.processField(flagSet, flagName, ff); err != nil { return err } } case field.Kind() == reflect.Ptr: field.InitElem() return f.processField(flagSet, fieldName, field) default: // Add custom prefix to the flag if it's set if f.Prefix != "" { fieldName = f.Prefix + "-" + fieldName } flagSet.Var(newFieldValue(field), flagName(fieldName), flagUsage(fieldName)) } return nil }
// processField gets tagName and the field, recursively checks if the field has the given // tag, if yes, sets it otherwise ignores func (t *TagLoader) processField(tagName string, field *structs.Field) error { switch field.Kind() { case reflect.Struct: for _, f := range field.Fields() { if err := t.processField(tagName, f); err != nil { return err } } default: defaultVal := field.Tag(t.DefaultTagName) if defaultVal == "" { return nil } err := fieldSet(field, defaultVal) if err != nil { return err } } return nil }
func getBytes(f *structs.Field) ([]byte, error) { var b *bytes.Buffer switch f.Kind() { case reflect.Slice: if reflect.ValueOf(f.Value()).Type().Elem().Kind() == reflect.Uint8 { // []byte return (f.Value()).([]byte), nil } case reflect.String: return []byte((f.Value()).(string)), nil case reflect.Bool: return boolconv.NewBool((f.Value()).(bool)).Bytes(), nil case reflect.Int8, reflect.Uint8: b = bytes.NewBuffer(make([]byte, 0, 2)) case reflect.Int16, reflect.Uint16: b = bytes.NewBuffer(make([]byte, 0, binary.MaxVarintLen16)) case reflect.Int32, reflect.Uint32: b = bytes.NewBuffer(make([]byte, 0, binary.MaxVarintLen32)) case reflect.Int64, reflect.Uint64, reflect.Int, reflect.Uint, reflect.Float32, reflect.Float64: b = bytes.NewBuffer(make([]byte, 0, binary.MaxVarintLen64)) } if b != nil { i := f.Value() if f.Kind() == reflect.Int { i = int64(i.(int)) } if f.Kind() == reflect.Uint { i = uint64(i.(uint)) } err := binary.Write(b, binary.BigEndian, i) return b.Bytes(), err } return nil, fmt.Errorf("cloth: unsupported type. %v", f.Kind()) }
func validateRange(field *structs.Field, minStr string, maxStr string) error { switch field.Kind() { case reflect.Int: i := field.Value().(int) min, minErr := strconv.Atoi(minStr) max, maxErr := strconv.Atoi(maxStr) if (minErr == nil && i < min) || (maxErr == nil && i > max) { return fmt.Errorf("validate: Field %s value %d, want: [%s, %s]", field.Name(), i, minStr, maxStr) } case reflect.Float64: i := field.Value().(float64) min, minErr := strconv.ParseFloat(minStr, 64) max, maxErr := strconv.ParseFloat(maxStr, 64) if (minErr == nil && i < min) || (maxErr == nil && i > max) { return fmt.Errorf("validate: Field %s value %f, want: [%s, %s]", field.Name(), i, minStr, maxStr) } case reflect.Int64: switch field.Value().(type) { case int64: i := field.Value().(int64) min, minErr := strconv.ParseInt(minStr, 10, 64) max, maxErr := strconv.ParseInt(maxStr, 10, 64) if (minErr == nil && i < min) || (maxErr == nil && i > max) { return fmt.Errorf("validate: Field %s value %d, want: [%s, %s]", field.Name(), i, minStr, maxStr) } default: return nil } default: return nil } return nil }
// fieldSet sets field value from the given string value. It converts the // string value in a sane way and is usefulf or environment variables or flags // which are by nature in string types. func fieldSet(field *structs.Field, v string) error { switch f := field.Value().(type) { case flag.Value: if v := reflect.ValueOf(field.Value()); v.IsNil() { typ := v.Type() if typ.Kind() == reflect.Ptr { typ = typ.Elem() } if err := field.Set(reflect.New(typ).Interface()); err != nil { return err } f = field.Value().(flag.Value) } return f.Set(v) } // TODO: add support for other types switch field.Kind() { case reflect.Bool: val, err := strconv.ParseBool(v) if err != nil { return err } if err := field.Set(val); err != nil { return err } case reflect.Int: i, err := strconv.Atoi(v) if err != nil { return err } if err := field.Set(i); err != nil { return err } case reflect.String: if err := field.Set(v); err != nil { return err } case reflect.Slice: switch t := field.Value().(type) { case []string: if err := field.Set(strings.Split(v, ",")); err != nil { return err } case []int: var list []int for _, in := range strings.Split(v, ",") { i, err := strconv.Atoi(in) if err != nil { return err } list = append(list, i) } if err := field.Set(list); err != nil { return err } default: return fmt.Errorf("multiconfig: field '%s' of type slice is unsupported: %s (%T)", field.Name(), field.Kind(), t) } case reflect.Float64: f, err := strconv.ParseFloat(v, 64) if err != nil { return err } if err := field.Set(f); err != nil { return err } case reflect.Int64: switch t := field.Value().(type) { case time.Duration: d, err := time.ParseDuration(v) if err != nil { return err } if err := field.Set(d); err != nil { return err } case int64: p, err := strconv.ParseInt(v, 10, 0) if err != nil { return err } if err := field.Set(p); err != nil { return err } default: return fmt.Errorf("multiconfig: field '%s' of type int64 is unsupported: %s (%T)", field.Name(), field.Kind(), t) } default: return fmt.Errorf("multiconfig: field '%s' has unsupported type: %s", field.Name(), field.Kind()) } return nil }
// fieldSet sets field value from the given string value. It converts the // string value in a sane way and is usefulf or environment variables or flags // which are by nature in string types. func fieldSet(field *structs.Field, v string) error { switch field.Kind() { case reflect.Bool: val, err := strconv.ParseBool(v) if err != nil { return err } if err := field.Set(val); err != nil { return err } case reflect.Int: i, err := strconv.Atoi(v) if err != nil { return err } if err := field.Set(i); err != nil { return err } case reflect.String: if err := field.Set(v); err != nil { return err } case reflect.Map: switch t := field.Value().(type) { case map[string]int: si := make(map[string]int) if err := json.Unmarshal([]byte(v), &si); err != nil { return err } if err := field.Set(si); err != nil { return err } case map[string]string: ss := make(map[string]string) if err := json.Unmarshal([]byte(v), &ss); err != nil { return err } if err := field.Set(ss); err != nil { return err } default: return fmt.Errorf("config: field '%s' of type map is unsupported: %s (%T)", field.Name(), field.Kind(), t) } case reflect.Slice: switch t := field.Value().(type) { case []string: if err := field.Set(strings.Split(v, ",")); err != nil { return err } case []int: var list []int for _, in := range strings.Split(v, ",") { i, err := strconv.Atoi(in) if err != nil { return err } list = append(list, i) } if err := field.Set(list); err != nil { return err } case []int64: var list []int64 for _, in := range strings.Split(v, ",") { i, err := strconv.ParseInt(in, 10, 64) if err != nil { return err } list = append(list, i) } if err := field.Set(list); err != nil { return err } case []float64: var list []float64 for _, in := range strings.Split(v, ",") { i, err := strconv.ParseFloat(in, 64) if err != nil { return err } list = append(list, i) } if err := field.Set(list); err != nil { return err } default: return fmt.Errorf("config: field '%s' of type slice is unsupported: %s (%T)", field.Name(), field.Kind(), t) } case reflect.Float64: f, err := strconv.ParseFloat(v, 64) if err != nil { return err } if err := field.Set(f); err != nil { return err } case reflect.Int64: switch t := field.Value().(type) { case time.Duration: d, err := time.ParseDuration(v) if err != nil { return err } if err := field.Set(d); err != nil { return err } case int64: p, err := strconv.ParseInt(v, 10, 64) if err != nil { return err } if err := field.Set(p); err != nil { return err } default: return fmt.Errorf("config: field '%s' of type int64 is unsupported: %s (%T)", field.Name(), field.Kind(), t) } default: return fmt.Errorf("config: field '%s' has unsupported type: %s", field.Name(), field.Kind()) } return nil }
// fieldSet sets field value from the given string value. It converts the // string value in a sane way and is usefulf or environment variables or flags // which are by nature in string types. func fieldSet(field *structs.Field, v string) error { // TODO: add support for other types switch field.Kind() { case reflect.Bool: val, err := strconv.ParseBool(v) if err != nil { return err } if err := field.Set(val); err != nil { return err } case reflect.Int: i, err := strconv.Atoi(v) if err != nil { return err } if field.RawValue.Type().Name() != "int" { // We're probably dealing with an enum type field.RawValue.SetInt(int64(i)) return nil } if err := field.Set(i); err != nil { return err } case reflect.String: if field.RawValue.Type().Name() != "string" { // We're probably dealing with an enum type field.RawValue.SetString(v) return nil } if err := field.Set(v); err != nil { return err } case reflect.Slice: switch t := field.Value().(type) { case []string: if err := field.Set(strings.Split(v, ",")); err != nil { return err } case []int: var list []int for _, in := range strings.Split(v, ",") { i, err := strconv.Atoi(in) if err != nil { return err } list = append(list, i) } if err := field.Set(list); err != nil { return err } default: return fmt.Errorf("multiconfig: field '%s' of type slice is unsupported: %s (%T)", field.Name(), field.Kind(), t) } case reflect.Float64: f, err := strconv.ParseFloat(v, 64) if err != nil { return err } if err := field.Set(f); err != nil { return err } case reflect.Int64: switch t := field.Value().(type) { case time.Duration: d, err := time.ParseDuration(v) if err != nil { return err } if err := field.Set(d); err != nil { return err } case int64: p, err := strconv.ParseInt(v, 10, 0) if err != nil { return err } if err := field.Set(p); err != nil { return err } default: return fmt.Errorf("multiconfig: field '%s' of type int64 is unsupported: %s (%T)", field.Name(), field.Kind(), t) } default: return fmt.Errorf("multiconfig: field '%s' has unsupported type: %s", field.Name(), field.Kind()) } return nil }