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
diff --git a/atomic.go b/atomic.go
index b7b116c..8bdf5d7 100644
--- a/atomic.go
+++ b/atomic.go
@@ -25,6 +25,7 @@
import (
"encoding/json"
"math"
+ "strconv"
"sync/atomic"
"time"
)
@@ -97,6 +98,11 @@
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 @@
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 @@
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 {
diff --git a/atomic_test.go b/atomic_test.go
index b57d197..cb3768e 100644
--- a/atomic_test.go
+++ b/atomic_test.go
@@ -71,6 +71,19 @@
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 @@
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 @@
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) {
diff --git a/int32.go b/int32.go
index c3b7d4f..357f178 100644
--- a/int32.go
+++ b/int32.go
@@ -24,6 +24,7 @@
import (
"encoding/json"
+ "strconv"
"sync/atomic"
)
@@ -93,3 +94,9 @@
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)
+}
diff --git a/int32_test.go b/int32_test.go
index 19c9ef7..9992251 100644
--- a/int32_test.go
+++ b/int32_test.go
@@ -22,8 +22,10 @@
import (
"encoding/json"
+ "math"
"testing"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -63,4 +65,18 @@
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.")
+ })
+ })
}
diff --git a/int64.go b/int64.go
index 180dbbc..8448018 100644
--- a/int64.go
+++ b/int64.go
@@ -24,6 +24,7 @@
import (
"encoding/json"
+ "strconv"
"sync/atomic"
)
@@ -93,3 +94,9 @@
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)
+}
diff --git a/int64_test.go b/int64_test.go
index b012196..ed5a104 100644
--- a/int64_test.go
+++ b/int64_test.go
@@ -22,8 +22,10 @@
import (
"encoding/json"
+ "math"
"testing"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -63,4 +65,18 @@
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.")
+ })
+ })
}
diff --git a/internal/gen-atomicint/main.go b/internal/gen-atomicint/main.go
index 3bd4e92..4ffcb94 100644
--- a/internal/gen-atomicint/main.go
+++ b/internal/gen-atomicint/main.go
@@ -129,6 +129,7 @@
import (
"encoding/json"
+ "strconv"
"sync/atomic"
)
@@ -204,4 +205,14 @@
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 }}
+}
`))
diff --git a/string_ext.go b/string_ext.go
index c0357e3..a346358 100644
--- a/string_ext.go
+++ b/string_ext.go
@@ -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.
diff --git a/string_test.go b/string_test.go
index 8e1fcd2..a730f5f 100644
--- a/string_test.go
+++ b/string_test.go
@@ -25,6 +25,7 @@
"encoding/xml"
"testing"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -75,4 +76,10 @@
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.")
+ })
}
diff --git a/uint32.go b/uint32.go
index 3e61026..3fbb696 100644
--- a/uint32.go
+++ b/uint32.go
@@ -24,6 +24,7 @@
import (
"encoding/json"
+ "strconv"
"sync/atomic"
)
@@ -93,3 +94,9 @@
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)
+}
diff --git a/uint32_test.go b/uint32_test.go
index 4149a47..d251730 100644
--- a/uint32_test.go
+++ b/uint32_test.go
@@ -22,8 +22,10 @@
import (
"encoding/json"
+ "math"
"testing"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -63,4 +65,12 @@
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.")
+ })
}
diff --git a/uint64.go b/uint64.go
index 1ed01a2..7cec96b 100644
--- a/uint64.go
+++ b/uint64.go
@@ -24,6 +24,7 @@
import (
"encoding/json"
+ "strconv"
"sync/atomic"
)
@@ -93,3 +94,9 @@
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)
+}
diff --git a/uint64_test.go b/uint64_test.go
index 7ff370b..9b8f399 100644
--- a/uint64_test.go
+++ b/uint64_test.go
@@ -22,8 +22,10 @@
import (
"encoding/json"
+ "math"
"testing"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -63,4 +65,12 @@
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.")
+ })
}