diff --git a/components/input/value-binder.go b/components/input/value-binder.go index d024f5c..c420c44 100644 --- a/components/input/value-binder.go +++ b/components/input/value-binder.go @@ -3,6 +3,7 @@ package input import ( "encoding" "fmt" + "reflect" "strconv" "time" ) @@ -44,65 +45,14 @@ func (f ValueBindAny) StringValue() string { case encoding.TextMarshaler: data, _ := v.MarshalText() // Ignore any errors, as we can't handle them here. return string(data) - case *bool: - return strconv.FormatBool(*v) - case bool: - return strconv.FormatBool(v) - case *string: - return *v - case string: - return v - case *int: - return strconv.FormatInt(int64(*v), 10) - case int: - return strconv.FormatInt(int64(v), 10) - case *int8: - return strconv.FormatInt(int64(*v), 10) - case int8: - return strconv.FormatInt(int64(v), 10) - case *int16: - return strconv.FormatInt(int64(*v), 10) - case int16: - return strconv.FormatInt(int64(v), 10) - case *int32: - return strconv.FormatInt(int64(*v), 10) - case int32: - return strconv.FormatInt(int64(v), 10) - case *int64: - return strconv.FormatInt(int64(*v), 10) - case int64: - return strconv.FormatInt(int64(v), 10) - case *uint: - return strconv.FormatUint(uint64(*v), 10) - case uint: - return strconv.FormatUint(uint64(v), 10) - case *uint8: - return strconv.FormatUint(uint64(*v), 10) - case uint8: - return strconv.FormatUint(uint64(v), 10) - case *uint16: - return strconv.FormatUint(uint64(*v), 10) - case uint16: - return strconv.FormatUint(uint64(v), 10) - case *uint32: - return strconv.FormatUint(uint64(*v), 10) - case uint32: - return strconv.FormatUint(uint64(v), 10) - case *uint64: - return strconv.FormatUint(uint64(*v), 10) - case uint64: - return strconv.FormatUint(uint64(v), 10) - case *float32: - return strconv.FormatFloat(float64(*v), 'f', -1, 32) // TODO: Format number in current user's locale - case float32: - return strconv.FormatFloat(float64(v), 'f', -1, 32) // TODO: Format number in current user's locale - case *float64: - return strconv.FormatFloat(float64(*v), 'f', -1, 64) - case float64: - return strconv.FormatFloat(float64(v), 'f', -1, 64) } - return "" + // Determine the reflection type so we can support any type that wraps basic types. + str, err := valueBindAnyMarshalBasicValue(reflect.ValueOf(f.Value)) + if err != nil { + return err.Error() + } + return str } func (f ValueBindAny) SetStringValue(value string) error { @@ -117,91 +67,9 @@ func (f ValueBindAny) SetStringValue(value string) error { *v = val case encoding.TextUnmarshaler: return v.UnmarshalText([]byte(value)) - case *bool: - val, err := strconv.ParseBool(value) - if err != nil { - return err - } - *v = val - case *string: - *v = value - case *int: - val, err := strconv.ParseInt(value, 10, 0) - if err != nil { - return err - } - *v = int(val) - case *int8: - val, err := strconv.ParseInt(value, 10, 8) - if err != nil { - return err - } - *v = int8(val) - case *int16: - val, err := strconv.ParseInt(value, 10, 16) - if err != nil { - return err - } - *v = int16(val) - case *int32: - val, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return err - } - *v = int32(val) - case *int64: - val, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return err - } - *v = int64(val) - case *uint: - val, err := strconv.ParseUint(value, 10, 0) - if err != nil { - return err - } - *v = uint(val) - case *uint8: - val, err := strconv.ParseUint(value, 10, 8) - if err != nil { - return err - } - *v = uint8(val) - case *uint16: - val, err := strconv.ParseUint(value, 10, 16) - if err != nil { - return err - } - *v = uint16(val) - case *uint32: - val, err := strconv.ParseUint(value, 10, 32) - if err != nil { - return err - } - *v = uint32(val) - case *uint64: - val, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return err - } - *v = uint64(val) - case *float32: - val, err := strconv.ParseFloat(value, 32) - if err != nil { - return err - } - *v = float32(val) - case *float64: - val, err := strconv.ParseFloat(value, 64) - if err != nil { - return err - } - *v = float64(val) - default: - return fmt.Errorf("bound type %T is not supported", f.Value) } - return nil + return valueBindAnyUnmarshalBasicValue(value, reflect.ValueOf(f.Value)) } func (f ValueBindAny) HTMLInputType() string { @@ -220,3 +88,154 @@ func (f ValueBindAny) HTMLInputType() string { return "text" } + +func valueBindAnyMarshalBasicValue(v reflect.Value) (string, error) { + if !v.IsValid() { + return "", fmt.Errorf("bound value is invalid") + } + + switch v.Kind() { + case reflect.Pointer, reflect.Interface: + if v.IsNil() { + return "", fmt.Errorf("bound value pointer is nil") + } + return valueBindAnyMarshalBasicValue(v.Elem()) + case reflect.Bool: + return strconv.FormatBool(v.Bool()), nil + case reflect.String: + return v.String(), nil + case reflect.Int: + return strconv.FormatInt(v.Int(), 10), nil + case reflect.Int8: + return strconv.FormatInt(v.Int(), 10), nil + case reflect.Int16: + return strconv.FormatInt(v.Int(), 10), nil + case reflect.Int32: + return strconv.FormatInt(v.Int(), 10), nil + case reflect.Int64: + return strconv.FormatInt(v.Int(), 10), nil + case reflect.Uint: + return strconv.FormatUint(v.Uint(), 10), nil + case reflect.Uint8: + return strconv.FormatUint(v.Uint(), 10), nil + case reflect.Uint16: + return strconv.FormatUint(v.Uint(), 10), nil + case reflect.Uint32: + return strconv.FormatUint(v.Uint(), 10), nil + case reflect.Uint64: + return strconv.FormatUint(v.Uint(), 10), nil + case reflect.Float32: + return strconv.FormatFloat(v.Float(), 'f', -1, 32), nil + case reflect.Float64: + return strconv.FormatFloat(v.Float(), 'f', -1, 64), nil + } + + return "", fmt.Errorf("bound type %T is not supported", v.Interface()) +} + +func valueBindAnyUnmarshalBasicValue(str string, v reflect.Value) error { + if !v.IsValid() { + return fmt.Errorf("bound value is invalid") + } + + // Get value that is stored in pointer or interface. + kind := v.Kind() + if kind == reflect.Pointer || kind == reflect.Interface { + if v.IsNil() { + return fmt.Errorf("bound value pointer is nil") + } + return valueBindAnyUnmarshalBasicValue(str, v.Elem()) + } + + // Check if we are allowed to modify the value. + if !v.CanSet() { + return fmt.Errorf("bound value can't be modified") + } + + switch kind { + case reflect.Bool: + val, err := strconv.ParseBool(str) + if err != nil { + return err + } + v.SetBool(val) + case reflect.String: + v.SetString(str) + case reflect.Int: + val, err := strconv.ParseInt(str, 10, 0) + if err != nil { + return err + } + v.SetInt(val) + case reflect.Int8: + val, err := strconv.ParseInt(str, 10, 8) + if err != nil { + return err + } + v.SetInt(val) + case reflect.Int16: + val, err := strconv.ParseInt(str, 10, 16) + if err != nil { + return err + } + v.SetInt(val) + case reflect.Int32: + val, err := strconv.ParseInt(str, 10, 32) + if err != nil { + return err + } + v.SetInt(val) + case reflect.Int64: + val, err := strconv.ParseInt(str, 10, 64) + if err != nil { + return err + } + v.SetInt(val) + case reflect.Uint: + val, err := strconv.ParseUint(str, 10, 0) + if err != nil { + return err + } + v.SetUint(val) + case reflect.Uint8: + val, err := strconv.ParseUint(str, 10, 8) + if err != nil { + return err + } + v.SetUint(val) + case reflect.Uint16: + val, err := strconv.ParseUint(str, 10, 16) + if err != nil { + return err + } + v.SetUint(val) + case reflect.Uint32: + val, err := strconv.ParseUint(str, 10, 32) + if err != nil { + return err + } + v.SetUint(val) + case reflect.Uint64: + val, err := strconv.ParseUint(str, 10, 64) + if err != nil { + return err + } + v.SetUint(val) + case reflect.Float32: + val, err := strconv.ParseFloat(str, 32) + if err != nil { + return err + } + v.SetFloat(val) + case reflect.Float64: + val, err := strconv.ParseFloat(str, 64) + if err != nil { + return err + } + v.SetFloat(val) + default: + return fmt.Errorf("bound type %T is not supported", v.Interface()) + } + + return nil +}