Types: Switch convert.StringToFloat64 to types.Number (#1415)

* Types: Add Number type

* Types: Switch StringToFloat64 for Number

This change mostly just renames the type.
convert package and StringToFloat64 represent actions, not types,
and make it misleading to use outside of the API context,
especially when using it for a Float64ToString operation.

* Common: Remove StringToFloat64

Replaced by types.Number

* fixup! Types: Switch StringToFloat64 for Number

Second pass at Okx

* Spellcheck: Fix whitespace handling for okx line
This commit is contained in:
Gareth Kirwan
2023-12-20 03:01:27 +01:00
committed by GitHub
parent 5042bf9790
commit f05f24da8b
21 changed files with 3158 additions and 3140 deletions

View File

@@ -1,9 +1,7 @@
package convert
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"math"
"strconv"
@@ -13,10 +11,6 @@ import (
"github.com/shopspring/decimal"
)
const jsonStringIdent = `"` // immutable byte sequence
var errUnhandledType = errors.New("unhandled type")
// FloatFromString format
func FloatFromString(raw interface{}) (float64, error) {
str, ok := raw.(string)
@@ -195,55 +189,6 @@ func InterfaceToStringOrZeroValue(r interface{}) string {
return ""
}
// StringToFloat64 is a float64 that unmarshals from a string. This is useful
// for APIs that return numbers as strings and return an empty string instead of
// 0.
type StringToFloat64 float64
// UnmarshalJSON implements the json.Unmarshaler interface.
// This implementation is slightly more performant than calling json.Unmarshal
// again.
func (f *StringToFloat64) UnmarshalJSON(data []byte) error {
if !bytes.HasPrefix(data, []byte(jsonStringIdent)) {
return fmt.Errorf("%w: %s", errUnhandledType, string(data))
}
data = data[1 : len(data)-1] // Remove quotes
if len(data) == 0 {
*f = StringToFloat64(0)
return nil
}
val, err := strconv.ParseFloat(string(data), 64)
if err != nil {
return err
}
*f = StringToFloat64(val)
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (f StringToFloat64) MarshalJSON() ([]byte, error) {
if f == 0 {
return []byte(jsonStringIdent + jsonStringIdent), nil
}
val := strconv.FormatFloat(float64(f), 'f', -1, 64)
return []byte(jsonStringIdent + val + jsonStringIdent), nil
}
// Float64 returns the float64 value of the FloatString.
func (f StringToFloat64) Float64() float64 {
return float64(f)
}
// Decimal returns the decimal value of the FloatString
// Warning: this does not handle big numbers as the underlying
// is still a float
func (f StringToFloat64) Decimal() decimal.Decimal {
return decimal.NewFromFloat(float64(f))
}
// ExchangeTime provides timestamp to time conversion method.
type ExchangeTime time.Time

View File

@@ -2,7 +2,6 @@ package convert
import (
"encoding/json"
"errors"
"strings"
"testing"
"time"
@@ -318,89 +317,6 @@ func TestInterfaceToStringOrZeroValue(t *testing.T) {
}
}
func TestStringToFloat64(t *testing.T) {
t.Parallel()
resp := struct {
Data StringToFloat64 `json:"data"`
}{}
err := json.Unmarshal([]byte(`{"data":"0.00000001"}`), &resp)
if err != nil {
t.Fatal(err)
}
if resp.Data.Float64() != 1e-8 {
t.Fatalf("expected 1e-8, got %v", resp.Data.Float64())
}
err = json.Unmarshal([]byte(`{"data":""}`), &resp)
if err != nil {
t.Fatal(err)
}
err = json.Unmarshal([]byte(`{"data":1337.37}`), &resp)
if !errors.Is(err, errUnhandledType) {
t.Fatalf("received %v but expected %v", err, errUnhandledType)
}
// Demonstrates that a suffix check is not needed.
err = json.Unmarshal([]byte(`{"data":"1337.37}`), &resp)
if err == nil {
t.Fatal("error cannot be nil")
}
err = json.Unmarshal([]byte(`{"data":"MEOW"}`), &resp)
if err == nil {
t.Fatal("error cannot be nil")
}
data, err := json.Marshal(StringToFloat64(0))
if err != nil {
t.Fatal(err)
}
if string(data) != `""` {
t.Fatalf("expected empty string, got %v", string(data))
}
data, err = json.Marshal(StringToFloat64(1337.1337))
if err != nil {
t.Fatal(err)
}
if string(data) != `"1337.1337"` {
t.Fatalf("expected \"1337.1337\" string, got %v", string(data))
}
}
func TestStringToFloat64Decimal(t *testing.T) {
t.Parallel()
resp := struct {
Data StringToFloat64 `json:"data"`
}{}
err := json.Unmarshal([]byte(`{"data":"0.00000001"}`), &resp)
if err != nil {
t.Fatal(err)
}
if !resp.Data.Decimal().Equal(decimal.NewFromFloat(0.00000001)) {
t.Errorf("received '%v' expected '%v'", resp.Data.Decimal(), 0.00000001)
}
}
// 2677173 428.9 ns/op 240 B/op 5 allocs/op
func BenchmarkStringToFloat64(b *testing.B) {
resp := struct {
Data StringToFloat64 `json:"data"`
}{}
for i := 0; i < b.N; i++ {
err := json.Unmarshal([]byte(`{"data":"0.00000001"}`), &resp)
if err != nil {
b.Fatal(err)
}
}
}
func TestExchangeTimeUnmarshalJSON(t *testing.T) {
t.Parallel()
unmarshaledResult := &struct {