commit b18e4dd9c07535ad52434257da37c76b7858e996
parent 71c0966e1a7260957ced1f424f1bc2219207eda1
Author: francoispqt <francois@parquet.ninja>
Date: Tue, 24 Jul 2018 23:21:52 +0800
add time type
Diffstat:
M | decode.go | | | 16 | ++++++++++++++++ |
A | decode_time.go | | | 36 | ++++++++++++++++++++++++++++++++++++ |
A | decode_time_test.go | | | 141 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | encode_time.go | | | 62 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | encode_time_test.go | | | 156 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 411 insertions(+), 0 deletions(-)
diff --git a/decode.go b/decode.go
@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"reflect"
+ "time"
)
// UnmarshalJSONArray parses the JSON-encoded data and stores the result in the value pointed to by v.
@@ -482,6 +483,21 @@ func (dec *Decoder) String(v *string) error {
return nil
}
+// AddTime decodes the next key to a *time.Time with the given format
+func (dec *Decoder) AddTime(v *time.Time, format string) error {
+ return dec.Time(v, format)
+}
+
+// Time decodes the next key to a *time.Time with the given format
+func (dec *Decoder) Time(v *time.Time, format string) error {
+ err := dec.decodeTime(v, format)
+ if err != nil {
+ return err
+ }
+ dec.called |= 1
+ return nil
+}
+
// Object decodes the next key to a UnmarshalerJSONObject.
func (dec *Decoder) Object(value UnmarshalerJSONObject) error {
initialKeysDone := dec.keysDone
diff --git a/decode_time.go b/decode_time.go
@@ -0,0 +1,36 @@
+package gojay
+
+import (
+ "time"
+)
+
+// DecodeTime decodes time with the given format
+func (dec *Decoder) DecodeTime(v *time.Time, format string) error {
+ if dec.isPooled == 1 {
+ panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder"))
+ }
+ return dec.decodeTime(v, format)
+}
+
+func (dec *Decoder) decodeTime(v *time.Time, format string) error {
+ if format == time.RFC3339 {
+ var ej = make(EmbeddedJSON, 0, 20)
+ if err := dec.decodeEmbeddedJSON(&ej); err != nil {
+ return err
+ }
+ if err := v.UnmarshalJSON(ej); err != nil {
+ return err
+ }
+ return nil
+ }
+ var str string
+ if err := dec.decodeString(&str); err != nil {
+ return err
+ }
+ tt, err := time.Parse(format, str)
+ if err != nil {
+ return err
+ }
+ *v = tt
+ return nil
+}
diff --git a/decode_time_test.go b/decode_time_test.go
@@ -0,0 +1,141 @@
+package gojay
+
+import (
+ "strings"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestDecodeTime(t *testing.T) {
+ testCases := []struct {
+ name string
+ json string
+ format string
+ err bool
+ expectedTime string
+ }{
+ {
+ name: "basic",
+ json: `"2018-02-18"`,
+ format: `2006-01-02`,
+ err: false,
+ expectedTime: "2018-02-18",
+ },
+ {
+ name: "basic",
+ json: `"2017-01-02T15:04:05Z"`,
+ format: time.RFC3339,
+ err: false,
+ expectedTime: "2017-01-02T15:04:05Z",
+ },
+ {
+ name: "basic",
+ json: `"2017-01-02T15:04:05ZINVALID"`,
+ format: time.RFC3339,
+ err: true,
+ expectedTime: "",
+ },
+ {
+ name: "basic",
+ json: `"2017-01-02T15:04:05ZINVALID`,
+ format: time.RFC1123,
+ err: true,
+ expectedTime: "",
+ },
+ {
+ name: "basic",
+ json: `"2017-01-02T15:04:05ZINVALID"`,
+ format: time.RFC1123,
+ err: true,
+ expectedTime: "",
+ },
+ {
+ name: "basic",
+ json: `"2017-01-02T15:04:05ZINVALID`,
+ format: time.RFC3339,
+ err: true,
+ expectedTime: "",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ tm := time.Time{}
+ dec := NewDecoder(strings.NewReader(testCase.json))
+ err := dec.DecodeTime(&tm, testCase.format)
+ if !testCase.err {
+ assert.Nil(t, err)
+ assert.Equal(t, testCase.expectedTime, tm.Format(testCase.format))
+ return
+ }
+ assert.NotNil(t, err)
+ })
+ }
+}
+
+func TestDecodeAddTime(t *testing.T) {
+ testCases := []struct {
+ name string
+ json string
+ format string
+ err bool
+ expectedTime string
+ }{
+ {
+ name: "basic",
+ json: `"2018-02-18"`,
+ format: `2006-01-02`,
+ err: false,
+ expectedTime: "2018-02-18",
+ },
+ {
+ name: "basic",
+ json: ` "2017-01-02T15:04:05Z"`,
+ format: time.RFC3339,
+ err: false,
+ expectedTime: "2017-01-02T15:04:05Z",
+ },
+ {
+ name: "basic",
+ json: ` "2017-01-02T15:04:05ZINVALID"`,
+ format: time.RFC3339,
+ err: true,
+ expectedTime: "",
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ tm := time.Time{}
+ dec := NewDecoder(strings.NewReader(testCase.json))
+ err := dec.AddTime(&tm, testCase.format)
+ if !testCase.err {
+ assert.Nil(t, err)
+ assert.Equal(t, testCase.expectedTime, tm.Format(testCase.format))
+ return
+ }
+ assert.NotNil(t, err)
+ })
+ }
+}
+
+func TestDecoderTimePoolError(t *testing.T) {
+ // reset the pool to make sure it's not full
+ decPool = sync.Pool{
+ New: func() interface{} {
+ return NewDecoder(nil)
+ },
+ }
+ dec := NewDecoder(nil)
+ dec.Release()
+ defer func() {
+ err := recover()
+ assert.NotNil(t, err, "err shouldnt be nil")
+ assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError")
+ }()
+ _ = dec.DecodeTime(&time.Time{}, time.RFC3339)
+ assert.True(t, false, "should not be called as decoder should have panicked")
+}
diff --git a/encode_time.go b/encode_time.go
@@ -0,0 +1,62 @@
+package gojay
+
+import (
+ "time"
+)
+
+func (enc *Encoder) EncodeTime(t *time.Time, format string) error {
+ if enc.isPooled == 1 {
+ panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
+ }
+ _, _ = enc.encodeTime(t, format)
+ _, err := enc.Write()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// encodeInt encodes an int to JSON
+func (enc *Encoder) encodeTime(t *time.Time, format string) ([]byte, error) {
+ enc.writeByte('"')
+ enc.buf = t.AppendFormat(enc.buf, format)
+ enc.writeByte('"')
+ return enc.buf, nil
+}
+
+// AddTimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key
+func (enc *Encoder) AddTimeKey(key string, t *time.Time, format string) {
+ enc.TimeKey(key, t, format)
+}
+
+// TimeKey adds an *time.Time to be encoded with the given format, must be used inside an object as it will encode a key
+func (enc *Encoder) TimeKey(key string, t *time.Time, format string) {
+ enc.grow(10 + len(key))
+ r := enc.getPreviousRune()
+ if r != '{' {
+ enc.writeTwoBytes(',', '"')
+ } else {
+ enc.writeByte('"')
+ }
+ enc.writeStringEscape(key)
+ enc.writeBytes(objKeyStr)
+ enc.buf = t.AppendFormat(enc.buf, format)
+ enc.writeByte('"')
+}
+
+// AddTime adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddTime(t *time.Time, format string) {
+ enc.Time(t, format)
+}
+
+// Time adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Time(t *time.Time, format string) {
+ enc.grow(10)
+ r := enc.getPreviousRune()
+ if r != '[' {
+ enc.writeByte(',')
+ }
+ enc.writeByte('"')
+ enc.buf = t.AppendFormat(enc.buf, format)
+ enc.writeByte('"')
+}
diff --git a/encode_time_test.go b/encode_time_test.go
@@ -0,0 +1,156 @@
+package gojay
+
+import (
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEncodeTime(t *testing.T) {
+ testCases := []struct {
+ name string
+ tt string
+ format string
+ expectedJSON string
+ err bool
+ }{
+ {
+ name: "basic",
+ tt: "2018-02-01",
+ format: "2006-01-02",
+ expectedJSON: `"2018-02-01"`,
+ err: false,
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ b := strings.Builder{}
+ tt, err := time.Parse(testCase.format, testCase.tt)
+ assert.Nil(t, err)
+ enc := NewEncoder(&b)
+ err = enc.EncodeTime(&tt, testCase.format)
+ if !testCase.err {
+ assert.Nil(t, err)
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ }
+ })
+ }
+ t.Run("encode-time-pool-error", func(t *testing.T) {
+ builder := &strings.Builder{}
+ enc := NewEncoder(builder)
+ enc.Release()
+ defer func() {
+ err := recover()
+ assert.NotNil(t, err, "err should not be nil")
+ assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError")
+ }()
+ _ = enc.EncodeTime(&time.Time{}, "")
+ assert.True(t, false, "should not be called as encoder should have panicked")
+ })
+ t.Run("write-error", func(t *testing.T) {
+ w := TestWriterError("")
+ enc := BorrowEncoder(w)
+ defer enc.Release()
+ err := enc.EncodeTime(&time.Time{}, "")
+ assert.NotNil(t, err, "err should not be nil")
+ })
+}
+
+func TestAddTimeKey(t *testing.T) {
+ testCases := []struct {
+ name string
+ tt string
+ format string
+ expectedJSON string
+ baseJSON string
+ err bool
+ }{
+ {
+ name: "basic",
+ tt: "2018-02-01",
+ format: "2006-01-02",
+ baseJSON: "{",
+ expectedJSON: `{"test":"2018-02-01"`,
+ err: false,
+ },
+ {
+ name: "basic",
+ tt: "2018-02-01",
+ format: "2006-01-02",
+ baseJSON: `{""`,
+ expectedJSON: `{"","test":"2018-02-01"`,
+ err: false,
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ b := strings.Builder{}
+ tt, err := time.Parse(testCase.format, testCase.tt)
+ assert.Nil(t, err)
+ enc := NewEncoder(&b)
+ enc.writeString(testCase.baseJSON)
+ enc.AddTimeKey("test", &tt, testCase.format)
+ enc.Write()
+ if !testCase.err {
+ assert.Nil(t, err)
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ }
+ })
+ }
+}
+
+func TestAddTime(t *testing.T) {
+ testCases := []struct {
+ name string
+ tt string
+ format string
+ expectedJSON string
+ baseJSON string
+ err bool
+ }{
+ {
+ name: "basic",
+ tt: "2018-02-01",
+ format: "2006-01-02",
+ baseJSON: "[",
+ expectedJSON: `["2018-02-01"`,
+ err: false,
+ },
+ {
+ name: "basic",
+ tt: "2018-02-01",
+ format: "2006-01-02",
+ baseJSON: "[",
+ expectedJSON: `["2018-02-01"`,
+ err: false,
+ },
+ {
+ name: "basic",
+ tt: "2018-02-01",
+ format: "2006-01-02",
+ baseJSON: `[""`,
+ expectedJSON: `["","2018-02-01"`,
+ err: false,
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ b := strings.Builder{}
+ tt, err := time.Parse(testCase.format, testCase.tt)
+ assert.Nil(t, err)
+ enc := NewEncoder(&b)
+ enc.writeString(testCase.baseJSON)
+ enc.AddTime(&tt, testCase.format)
+ enc.Write()
+ if !testCase.err {
+ assert.Nil(t, err)
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ }
+ })
+ }
+}