67 lines
1.5 KiB
Go
67 lines
1.5 KiB
Go
|
package config
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// IsZero returns whether or not the parameter is the zero value for its type.
|
||
|
// If the parameter is a struct, only the exported fields are considered.
|
||
|
func IsZero(c interface{}) bool {
|
||
|
return isZero(reflect.ValueOf(c))
|
||
|
}
|
||
|
|
||
|
// AssertValid checks the fields in the structure and makes sure that they
|
||
|
// contain valid values as specified by the 'valid' flag. Empty fields are
|
||
|
// implicitly valid.
|
||
|
func AssertValid(c interface{}) error {
|
||
|
ct := reflect.TypeOf(c)
|
||
|
cv := reflect.ValueOf(c)
|
||
|
for i := 0; i < ct.NumField(); i++ {
|
||
|
ft := ct.Field(i)
|
||
|
if !isFieldExported(ft) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
valid := ft.Tag.Get("valid")
|
||
|
val := cv.Field(i)
|
||
|
if !isValid(val, valid) {
|
||
|
return fmt.Errorf("invalid value \"%v\" for option %q (valid options: %q)", val.Interface(), ft.Name, valid)
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func isZero(v reflect.Value) bool {
|
||
|
switch v.Kind() {
|
||
|
case reflect.Struct:
|
||
|
vt := v.Type()
|
||
|
for i := 0; i < v.NumField(); i++ {
|
||
|
if isFieldExported(vt.Field(i)) && !isZero(v.Field(i)) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
default:
|
||
|
return v.Interface() == reflect.Zero(v.Type()).Interface()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func isFieldExported(f reflect.StructField) bool {
|
||
|
return f.PkgPath == ""
|
||
|
}
|
||
|
|
||
|
func isValid(v reflect.Value, valid string) bool {
|
||
|
if valid == "" || isZero(v) {
|
||
|
return true
|
||
|
}
|
||
|
vs := fmt.Sprintf("%v", v.Interface())
|
||
|
for _, valid := range strings.Split(valid, ",") {
|
||
|
if vs == valid {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|