diff --git a/fieldcollection/schema.go b/fieldcollection/schema.go index fec7bf6..e5aaf01 100644 --- a/fieldcollection/schema.go +++ b/fieldcollection/schema.go @@ -37,6 +37,7 @@ const ( SchemaFieldTypeAny SchemaFieldType = iota SchemaFieldTypeBool SchemaFieldTypeDuration + SchemaFieldTypeFloat64 SchemaFieldTypeInt64 SchemaFieldTypeString SchemaFieldTypeStringSlice @@ -110,7 +111,7 @@ func (f *FieldCollection) ValidateSchema(opts ...ValidateOpt) error { return nil } -//nolint:gocyclo // These are quite simple checks +//nolint:gocognit,gocyclo // These are quite simple checks func validateFieldType(f *FieldCollection, field SchemaField) (err error) { switch field.Type { case SchemaFieldTypeAny: @@ -138,6 +139,16 @@ func validateFieldType(f *FieldCollection, field SchemaField) (err error) { return fmt.Errorf("field %s is empty", field.Name) } + case SchemaFieldTypeFloat64: + v, err := f.Float64(field.Name) + if err != nil { + return fmt.Errorf("field %s is not of type float64: %w", field.Name, err) + } + + if field.NonEmpty && v == 0 { + return fmt.Errorf("field %s is empty", field.Name) + } + case SchemaFieldTypeInt64: v, err := f.Int64(field.Name) if err != nil { diff --git a/fieldcollection/typeFloat.go b/fieldcollection/typeFloat.go new file mode 100644 index 0000000..2e5b3a7 --- /dev/null +++ b/fieldcollection/typeFloat.go @@ -0,0 +1,67 @@ +package fieldcollection + +import ( + "fmt" + "strconv" + + "github.com/pkg/errors" +) + +// Float64 tries to read key name as float64 +func (f *FieldCollection) Float64(name string) (float64, error) { + if f == nil || f.data == nil { + return 0, errors.New("uninitialized field collection") + } + + f.lock.RLock() + defer f.lock.RUnlock() + + v, ok := f.data[name] + if !ok { + return 0, ErrValueNotSet + } + + switch v := v.(type) { + case int: + return float64(v), nil + + case int16: + return float64(v), nil + + case int32: + return float64(v), nil + + case int64: + return float64(v), nil + + case float64: + return v, nil + + case string: + pv, err := strconv.ParseFloat(v, 64) + if err != nil { + return 0, fmt.Errorf("parsing value: %w", err) + } + return pv, nil + } + + return 0, ErrValueMismatch +} + +// CanFloat64 tries to read key name as float64 and checks whether error is nil +func (f *FieldCollection) CanFloat64(name string) bool { + _, err := f.Float64(name) + return err == nil +} + +// MustFloat64 is a wrapper around Float64 and panics if an error was returned +func (f *FieldCollection) MustFloat64(name string, defVal *float64) float64 { + v, err := f.Float64(name) + if err != nil { + if defVal != nil { + return *defVal + } + panic(err) + } + return v +} diff --git a/fieldcollection/typeFloat_test.go b/fieldcollection/typeFloat_test.go new file mode 100644 index 0000000..4a0bb8c --- /dev/null +++ b/fieldcollection/typeFloat_test.go @@ -0,0 +1,57 @@ +package fieldcollection + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFloat64(t *testing.T) { + fc := FieldCollectionFromData(map[string]any{ + "int": int(12), + "int16": int16(12), + "int32": int32(12), + "int64": int64(12), + "float64": float64(12), + "bool": true, + "invalidString": "I'm a string!", + "validString": "12", + }) + + _, err := fc.Float64("_") + assert.ErrorIs(t, err, ErrValueNotSet) + + _, err = fc.Float64("bool") + assert.ErrorIs(t, err, ErrValueMismatch) + + _, err = fc.Float64("invalidString") + assert.Error(t, err) + + v, err := fc.Float64("int") + assert.NoError(t, err) + assert.Equal(t, float64(12), v) + + v, err = fc.Float64("int16") + assert.NoError(t, err) + assert.Equal(t, float64(12), v) + + v, err = fc.Float64("int32") + assert.NoError(t, err) + assert.Equal(t, float64(12), v) + + v, err = fc.Float64("int64") + assert.NoError(t, err) + assert.Equal(t, float64(12), v) + + v, err = fc.Float64("validString") + assert.NoError(t, err) + assert.Equal(t, float64(12), v) + + assert.True(t, fc.CanFloat64("int")) + assert.False(t, fc.CanFloat64("bool")) + + assert.NotPanics(t, func() { fc.MustFloat64("int32", nil) }) + assert.Panics(t, func() { fc.MustFloat64("bool", nil) }) + + assert.Equal(t, float64(5), fc.MustFloat64("_", func(v float64) *float64 { return &v }(5))) +}