Implement fmt.Stringer for atomic types (#76)
Add safe `String()` methods for atomic types that replicate the same
behavior as `fmt.Sprintf("%v", x.Load())` without the allocations.
As with json.Marshaler/Unmarshaler, we've omitted the `atomic.Value`
type for now.
Resolves #50
This commit is contained in:
17
atomic.go
17
atomic.go
@@ -25,6 +25,7 @@ package atomic
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
@@ -97,6 +98,11 @@ func (b *Bool) UnmarshalJSON(t []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (b *Bool) String() string {
|
||||
return strconv.FormatBool(b.Load())
|
||||
}
|
||||
|
||||
// Float64 is an atomic wrapper around float64.
|
||||
type Float64 struct {
|
||||
nocmp // disallow non-atomic comparison
|
||||
@@ -155,6 +161,12 @@ func (f *Float64) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (f *Float64) String() string {
|
||||
// 'g' is the behavior for floats with %v.
|
||||
return strconv.FormatFloat(f.Load(), 'g', -1, 64)
|
||||
}
|
||||
|
||||
// Duration is an atomic wrapper around time.Duration
|
||||
// https://godoc.org/time#Duration
|
||||
type Duration struct {
|
||||
@@ -213,6 +225,11 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (d *Duration) String() string {
|
||||
return d.Load().String()
|
||||
}
|
||||
|
||||
// Value shadows the type of the same name from sync/atomic
|
||||
// https://godoc.org/sync/atomic#Value
|
||||
type Value struct {
|
||||
|
||||
@@ -71,6 +71,19 @@ func TestBool(t *testing.T) {
|
||||
assertErrorJSONUnmarshalType(t, err,
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
})
|
||||
|
||||
t.Run("String", func(t *testing.T) {
|
||||
t.Run("true", func(t *testing.T) {
|
||||
assert.Equal(t, "true", NewBool(true).String(),
|
||||
"String() returned an unexpected value.")
|
||||
})
|
||||
|
||||
t.Run("false", func(t *testing.T) {
|
||||
var b Bool
|
||||
assert.Equal(t, "false", b.String(),
|
||||
"String() returned an unexpected value.")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloat64(t *testing.T) {
|
||||
@@ -106,6 +119,11 @@ func TestFloat64(t *testing.T) {
|
||||
assertErrorJSONUnmarshalType(t, err,
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
})
|
||||
|
||||
t.Run("String", func(t *testing.T) {
|
||||
assert.Equal(t, "42.5", NewFloat64(42.5).String(),
|
||||
"String() returned an unexpected value.")
|
||||
})
|
||||
}
|
||||
|
||||
func TestDuration(t *testing.T) {
|
||||
@@ -143,6 +161,11 @@ func TestDuration(t *testing.T) {
|
||||
assertErrorJSONUnmarshalType(t, err,
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
})
|
||||
|
||||
t.Run("String", func(t *testing.T) {
|
||||
assert.Equal(t, "42s", NewDuration(42*time.Second).String(),
|
||||
"String() returned an unexpected value.")
|
||||
})
|
||||
}
|
||||
|
||||
func TestValue(t *testing.T) {
|
||||
|
||||
7
int32.go
7
int32.go
@@ -24,6 +24,7 @@ package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
@@ -93,3 +94,9 @@ func (i *Int32) UnmarshalJSON(b []byte) error {
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Int32) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -63,4 +65,18 @@ func TestInt32(t *testing.T) {
|
||||
assertErrorJSONUnmarshalType(t, err,
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
})
|
||||
|
||||
t.Run("String", func(t *testing.T) {
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
atom := NewInt32(math.MaxInt32)
|
||||
assert.Equal(t, "2147483647", atom.String(),
|
||||
"String() returned an unexpected value.")
|
||||
})
|
||||
|
||||
t.Run("negative", func(t *testing.T) {
|
||||
atom := NewInt32(math.MinInt32)
|
||||
assert.Equal(t, "-2147483648", atom.String(),
|
||||
"String() returned an unexpected value.")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
7
int64.go
7
int64.go
@@ -24,6 +24,7 @@ package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
@@ -93,3 +94,9 @@ func (i *Int64) UnmarshalJSON(b []byte) error {
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Int64) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -63,4 +65,18 @@ func TestInt64(t *testing.T) {
|
||||
assertErrorJSONUnmarshalType(t, err,
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
})
|
||||
|
||||
t.Run("String", func(t *testing.T) {
|
||||
t.Run("positive", func(t *testing.T) {
|
||||
atom := NewInt64(math.MaxInt64)
|
||||
assert.Equal(t, "9223372036854775807", atom.String(),
|
||||
"String() returned an unexpected value.")
|
||||
})
|
||||
|
||||
t.Run("negative", func(t *testing.T) {
|
||||
atom := NewInt64(math.MinInt64)
|
||||
assert.Equal(t, "-9223372036854775808", atom.String(),
|
||||
"String() returned an unexpected value.")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -129,6 +129,7 @@ package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
@@ -204,4 +205,14 @@ func (i *{{ .Name }}) UnmarshalJSON(b []byte) error {
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *{{ .Name }}) String() string {
|
||||
v := i.Load()
|
||||
{{ if .Unsigned -}}
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
{{- else -}}
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
{{- end }}
|
||||
}
|
||||
`))
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
|
||||
package atomic
|
||||
|
||||
// String returns the wrapped value.
|
||||
func (s *String) String() string {
|
||||
return s.Load()
|
||||
}
|
||||
|
||||
// MarshalText encodes the wrapped string into a textual form.
|
||||
//
|
||||
// This makes it encodable as JSON, YAML, XML, and more.
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"encoding/xml"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -75,4 +76,10 @@ func TestString(t *testing.T) {
|
||||
require.NoError(t, err, "xml.Unmarshal errored unexpectedly.")
|
||||
require.Equal(t, "bar", atom.Load(), "xml.Unmarshal didn't set the correct value.")
|
||||
})
|
||||
|
||||
t.Run("String", func(t *testing.T) {
|
||||
atom := NewString("foo")
|
||||
assert.Equal(t, "foo", atom.String(),
|
||||
"String() returned an unexpected value.")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
@@ -93,3 +94,9 @@ func (i *Uint32) UnmarshalJSON(b []byte) error {
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Uint32) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -63,4 +65,12 @@ func TestUint32(t *testing.T) {
|
||||
assertErrorJSONUnmarshalType(t, err,
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
})
|
||||
|
||||
t.Run("String", func(t *testing.T) {
|
||||
// Use an integer with the signed bit set. If we're converting
|
||||
// incorrectly, we'll get a negative value here.
|
||||
atom := NewUint32(math.MaxUint32)
|
||||
assert.Equal(t, "4294967295", atom.String(),
|
||||
"String() returned an unexpected value.")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
@@ -93,3 +94,9 @@ func (i *Uint64) UnmarshalJSON(b []byte) error {
|
||||
i.Store(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String encodes the wrapped value as a string.
|
||||
func (i *Uint64) String() string {
|
||||
v := i.Load()
|
||||
return strconv.FormatUint(uint64(v), 10)
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ package atomic
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -63,4 +65,12 @@ func TestUint64(t *testing.T) {
|
||||
assertErrorJSONUnmarshalType(t, err,
|
||||
"json.Unmarshal failed with unexpected error %v, want UnmarshalTypeError.", err)
|
||||
})
|
||||
|
||||
t.Run("String", func(t *testing.T) {
|
||||
// Use an integer with the signed bit set. If we're converting
|
||||
// incorrectly, we'll get a negative value here.
|
||||
atom := NewUint64(math.MaxUint64)
|
||||
assert.Equal(t, "18446744073709551615", atom.String(),
|
||||
"String() returned an unexpected value.")
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user