Select Git revision
organization.go
number.go 3.61 KiB
package field
import (
"context"
"encoding/json"
"math"
"reflect"
"github.com/pkg/errors"
)
const (
NumberFormatInt = "int"
NumberFormatFloat = "float"
// The largest integer that can be represented in IEEE 754 double (64-bit) is a value from -2^53 to 2^53.
maxInt = 1<<53 - 1
minInt = -1<<53 + 1
)
var numberType = &NumberType{}
type NumberParameters struct {
Format string `json:"format,omitempty"`
}
func (NumberParameters) Type() Type { return numberType }
func (p NumberParameters) Clone(reset bool) Parameters { return &p }
func (p NumberParameters) GetField(f *Field, name string) *Field { return nil }
func (p NumberParameters) ListFields(f *Field, filter ...FieldFilterFunc) []*Field { return nil }
type NumberType struct{}
func (NumberType) Name() string {
return "number"
}
func (NumberType) NewParameters() Parameters {
return &NumberParameters{}
}
func (NumberType) IsEmpty(v interface{}) bool {
return v == nil
}
func toNumberReflect(i interface{}) (interface{}, error) {
v := reflect.ValueOf(i)
switch v.Kind() { //nolint:exhaustive //not necessary to add all types
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int(), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return v.Uint(), nil
case reflect.Float32, reflect.Float64:
return v.Float(), nil
default:
return 0, errors.Errorf("error convert \"%s\" to number", i)
}
}
func ToNumber(i interface{}) (interface{}, error) {
switch v := i.(type) {
case json.Number:
// Сначала пробуем парсить как целое число
intVal, err := v.Int64()
if err == nil {
return intVal, nil
}
// Если не получилось, пробуем как дробное число
floatVal, err := v.Float64()
if err == nil {
return floatVal, nil
}
return nil, err
case int64:
return v, nil
case int:
return int64(v), nil
case int8:
return int64(v), nil
case int32:
return int64(v), nil
case uint64:
return v, nil
case uint:
return uint64(v), nil
case uint8:
return uint64(v), nil
case uint32:
return uint64(v), nil
case float32:
return float64(v), nil
case float64:
return v, nil
default:
return toNumberReflect(v)
}
}
func (n NumberType) Decode(ctx context.Context, field *Field, v interface{}) (interface{}, error) {
return n.decode(ctx, field, v)
}
func (NumberType) decode(_ context.Context, field *Field, v interface{}) (interface{}, error) {
params, ok := field.Params.(*NumberParameters)
if !ok {
return nil, errors.New("field parameters required")
}
if v == nil {
return v, nil
}
n, err := ToNumber(v)
if err != nil {
return nil, err
}
switch params.Format {
case NumberFormatInt:
switch i := n.(type) {
case int64:
if i > maxInt || i < minInt {
return nil, errors.New("integer out of range")
}
return i, nil
case uint64:
if i > maxInt {
return nil, errors.New("integer out of range")
}
return i, nil
case float64:
if i > maxInt || i < minInt {
return nil, errors.New("integer out of range")
}
return int64(math.Round(i)), nil
}
case NumberFormatFloat:
switch i := n.(type) {
case float64:
return i, nil
case int64:
return float64(i), nil
case uint64:
return float64(i), nil
}
}
return n, nil
}
func (n NumberType) Encode(ctx context.Context, field *Field, v interface{}) (interface{}, error) {
return n.decode(ctx, field, v)
}
func Number(format string, o ...interface{}) *Field {
return NewField(&NumberParameters{Format: format}, o...)
}