commit 0329bd66c38182f4fe026f2f3d4924ace6936cd1
parent 9f5ebac1e960c619bbd22b2da9d6c13d64f24591
Author: Francois Parquet <francois.parquet@gmail.com>
Date: Mon, 25 Jun 2018 00:08:28 +0800
Merge pull request #40 from francoispqt/feature/generator
Feature/generator
Diffstat:
53 files changed, 6649 insertions(+), 20 deletions(-)
diff --git a/.travis.yml b/.travis.yml
@@ -6,6 +6,7 @@ go:
script:
- go get github.com/stretchr/testify
+ - go test ./gojay -race
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
diff --git a/README.md b/README.md
@@ -8,12 +8,17 @@
![stability-stable](https://img.shields.io/badge/stability-stable-green.svg)
# GoJay
+
+<img src="https://github.com/francoispqt/gojay/raw/feature/generator/gojay.png" width="200px">
+
GoJay is a performant JSON encoder/decoder for Golang (currently the most performant, [see benchmarks](#benchmark-results)).
It has a simple API and doesn't use reflection. It relies on small interfaces to decode/encode structures and slices.
Gojay also comes with powerful stream decoding features and an even faster [Unsafe](#unsafe-api) API.
+There is also a [code generation tool](https://github.com/francoispqt/gojay/tree/master/gojay) to make usage easier and faster.
+
# Why another JSON parser?
I looked at other fast decoder/encoder and realised it was mostly hardly readable static code generation or a lot of reflection, poor streaming features, and not so fast in the end.
@@ -31,7 +36,7 @@ go get github.com/francoispqt/gojay
* [Encoder](#encoding)
* [Decoder](#decoding)
* [Stream API](#stream-api)
-
+* [Code Generation](https://github.com/francoispqt/gojay/tree/master/gojay)
## Decoding
diff --git a/decode.go b/decode.go
@@ -294,7 +294,13 @@ func (dec *Decoder) AddUint64(v *uint64) error {
// AddFloat decodes the next key to a *float64.
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
func (dec *Decoder) AddFloat(v *float64) error {
- return dec.Float(v)
+ return dec.Float64(v)
+}
+
+// AddFloat64 decodes the next key to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) AddFloat64(v *float64) error {
+ return dec.Float64(v)
}
// AddFloat32 decodes the next key to a *float64.
@@ -428,6 +434,12 @@ func (dec *Decoder) Uint64(v *uint64) error {
// Float decodes the next key to a *float64.
// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
func (dec *Decoder) Float(v *float64) error {
+ return dec.Float64(v)
+}
+
+// Float64 decodes the next key to a *float64.
+// If next key value overflows float64, an InvalidUnmarshalError error will be returned.
+func (dec *Decoder) Float64(v *float64) error {
err := dec.decodeFloat64(v)
if err != nil {
return err
diff --git a/decode_array.go b/decode_array.go
@@ -106,3 +106,23 @@ func (dec *Decoder) skipArray() (int, error) {
}
return 0, dec.raiseInvalidJSONErr(dec.cursor)
}
+
+// DecodeArrayFunc is a custom func type implementing UnarshaleArray.
+// Use it to cast a func(*Decoder) to Unmarshal an object.
+//
+// str := ""
+// dec := gojay.NewDecoder(io.Reader)
+// dec.DecodeArray(gojay.DecodeArrayFunc(func(dec *gojay.Decoder, k string) error {
+// return dec.AddString(&str)
+// }))
+type DecodeArrayFunc func(*Decoder) error
+
+// UnmarshalJSONArray implements UnarshalerArray.
+func (f DecodeArrayFunc) UnmarshalJSONArray(dec *Decoder) error {
+ return f(dec)
+}
+
+// IsNil implements UnarshalerArray.
+func (f DecodeArrayFunc) IsNil() bool {
+ return f == nil
+}
diff --git a/decode_array_test.go b/decode_array_test.go
@@ -566,3 +566,8 @@ func TestDecodeArrayNullError(t *testing.T) {
assert.NotNil(t, err, "err should not be nil")
assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError")
}
+
+func TestDecoderArrayFunc(t *testing.T) {
+ var f DecodeArrayFunc
+ assert.True(t, f.IsNil())
+}
diff --git a/decode_number_float_test.go b/decode_number_float_test.go
@@ -630,3 +630,58 @@ func TestDecoderFloat32(t *testing.T) {
assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError")
})
}
+
+func TestDecoderFloat64Field(t *testing.T) {
+ var testCasesBasic = []struct {
+ name string
+ json string
+ value float64
+ }{
+ {
+ name: "basic",
+ json: "[1]",
+ value: float64(1),
+ },
+ {
+ name: "big",
+ json: "[0]",
+ value: float64(0),
+ },
+ }
+ for _, testCase := range testCasesBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var dec = NewDecoder(strings.NewReader(testCase.json))
+ var v float64
+ dec.DecodeArray(DecodeArrayFunc(func(dec *Decoder) error {
+ return dec.AddFloat64(&v)
+ }))
+ assert.Equal(t, testCase.value, v)
+ })
+ }
+ var testCasesBasicAlt = []struct {
+ name string
+ json string
+ value float64
+ }{
+ {
+ name: "basic",
+ json: "[1]",
+ value: float64(1),
+ },
+ {
+ name: "big",
+ json: "[0]",
+ value: float64(0),
+ },
+ }
+ for _, testCase := range testCasesBasicAlt {
+ t.Run(testCase.name, func(t *testing.T) {
+ var dec = NewDecoder(strings.NewReader(testCase.json))
+ var v float64
+ dec.DecodeArray(DecodeArrayFunc(func(dec *Decoder) error {
+ return dec.Float(&v)
+ }))
+ assert.Equal(t, testCase.value, v)
+ })
+ }
+}
diff --git a/encode_array.go b/encode_array.go
@@ -128,3 +128,22 @@ func (enc *Encoder) ArrayKeyOmitEmpty(key string, v MarshalerJSONArray) {
v.MarshalJSONArray(enc)
enc.writeByte(']')
}
+
+// EncodeArrayFunc is a custom func type implementing MarshaleArray.
+// Use it to cast a func(*Encoder) to Marshal an object.
+//
+// enc := gojay.NewEncoder(io.Writer)
+// enc.EncodeArray(gojay.EncodeArrayFunc(func(enc *gojay.Encoder) {
+// enc.AddStringKey("hello", "world")
+// }))
+type EncodeArrayFunc func(*Encoder)
+
+// MarshalJSONArray implements MarshalerJSONArray.
+func (f EncodeArrayFunc) MarshalJSONArray(enc *Encoder) {
+ f(enc)
+}
+
+// IsNil implements MarshalerJSONArray.
+func (f EncodeArrayFunc) IsNil() bool {
+ return f == nil
+}
diff --git a/encode_array_test.go b/encode_array_test.go
@@ -331,3 +331,8 @@ func TestEncoderArrErrors(t *testing.T) {
assert.True(t, false, "should not be called as it should have panicked")
})
}
+
+func TestEncoderArrayFunc(t *testing.T) {
+ var f EncodeArrayFunc
+ assert.True(t, f.IsNil())
+}
diff --git a/encode_number_float.go b/encode_number_float.go
@@ -41,17 +41,61 @@ func (enc *Encoder) encodeFloat32(n float32) ([]byte, error) {
// AddFloat adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) AddFloat(v float64) {
- enc.Float(v)
+ enc.Float64(v)
}
// AddFloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0,
// must be used inside a slice or array encoding (does not encode a key).
func (enc *Encoder) AddFloatOmitEmpty(v float64) {
- enc.FloatOmitEmpty(v)
+ enc.Float64OmitEmpty(v)
}
// Float adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) Float(v float64) {
+ enc.Float64(v)
+}
+
+// FloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) FloatOmitEmpty(v float64) {
+ enc.Float64OmitEmpty(v)
+}
+
+// AddFloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddFloatKey(key string, v float64) {
+ enc.Float64Key(key, v)
+}
+
+// AddFloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) AddFloatKeyOmitEmpty(key string, v float64) {
+ enc.Float64KeyOmitEmpty(key, v)
+}
+
+// FloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) FloatKey(key string, v float64) {
+ enc.Float64Key(key, v)
+}
+
+// FloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key
+func (enc *Encoder) FloatKeyOmitEmpty(key string, v float64) {
+ enc.Float64KeyOmitEmpty(key, v)
+}
+
+// AddFloat64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddFloat64(v float64) {
+ enc.Float(v)
+}
+
+// AddFloat64OmitEmpty adds a float64 to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddFloat64OmitEmpty(v float64) {
+ enc.FloatOmitEmpty(v)
+}
+
+// Float64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Float64(v float64) {
enc.grow(10)
r := enc.getPreviousRune()
if r != '[' {
@@ -60,9 +104,9 @@ func (enc *Encoder) Float(v float64) {
enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
}
-// FloatOmitEmpty adds a float64 to be encoded and skips it if its value is 0,
+// Float64OmitEmpty adds a float64 to be encoded and skips it if its value is 0,
// must be used inside a slice or array encoding (does not encode a key).
-func (enc *Encoder) FloatOmitEmpty(v float64) {
+func (enc *Encoder) Float64OmitEmpty(v float64) {
if v == 0 {
return
}
@@ -74,19 +118,19 @@ func (enc *Encoder) FloatOmitEmpty(v float64) {
enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
}
-// AddFloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key
-func (enc *Encoder) AddFloatKey(key string, v float64) {
+// AddFloat64Key adds a float64 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddFloat64Key(key string, v float64) {
enc.FloatKey(key, v)
}
-// AddFloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
+// AddFloat64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
// Must be used inside an object as it will encode a key
-func (enc *Encoder) AddFloatKeyOmitEmpty(key string, v float64) {
+func (enc *Encoder) AddFloat64KeyOmitEmpty(key string, v float64) {
enc.FloatKeyOmitEmpty(key, v)
}
-// FloatKey adds a float64 to be encoded, must be used inside an object as it will encode a key
-func (enc *Encoder) FloatKey(key string, value float64) {
+// Float64Key adds a float64 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Float64Key(key string, value float64) {
r := enc.getPreviousRune()
if r != '{' {
enc.writeByte(',')
@@ -98,9 +142,9 @@ func (enc *Encoder) FloatKey(key string, value float64) {
enc.buf = strconv.AppendFloat(enc.buf, value, 'f', -1, 64)
}
-// FloatKeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
+// Float64KeyOmitEmpty adds a float64 to be encoded and skips it if its value is 0.
// Must be used inside an object as it will encode a key
-func (enc *Encoder) FloatKeyOmitEmpty(key string, v float64) {
+func (enc *Encoder) Float64KeyOmitEmpty(key string, v float64) {
if v == 0 {
return
}
diff --git a/encode_number_float_test.go b/encode_number_float_test.go
@@ -0,0 +1,119 @@
+package gojay
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEncoderFloat64(t *testing.T) {
+ var testCasesBasic = []struct {
+ name string
+ v float64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: float64(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: float64(0),
+ expectedJSON: "[0,0]",
+ },
+ }
+ for _, testCase := range testCasesBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Float64(testCase.v)
+ enc.AddFloat64(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesOmitEmpty = []struct {
+ name string
+ v float64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: float64(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: float64(0),
+ expectedJSON: "[]",
+ },
+ }
+ for _, testCase := range testCasesOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Float64OmitEmpty(testCase.v)
+ enc.AddFloat64OmitEmpty(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyBasic = []struct {
+ name string
+ v float64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: float64(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: float64(0),
+ expectedJSON: `{"foo":0,"bar":0}`,
+ },
+ }
+ for _, testCase := range testCasesKeyBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Float64Key("foo", testCase.v)
+ enc.AddFloat64Key("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyOmitEmpty = []struct {
+ name string
+ v float64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: float64(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: float64(0),
+ expectedJSON: "{}",
+ },
+ }
+ for _, testCase := range testCasesKeyOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Float64KeyOmitEmpty("foo", testCase.v)
+ enc.AddFloat64KeyOmitEmpty("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+}
diff --git a/encode_number_int.go b/encode_number_int.go
@@ -191,3 +191,135 @@ func (enc *Encoder) Int64KeyOmitEmpty(key string, v int64) {
enc.writeBytes(objKey)
enc.buf = strconv.AppendInt(enc.buf, v, 10)
}
+
+// AddInt32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddInt32(v int32) {
+ enc.Int64(int64(v))
+}
+
+// AddInt32OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddInt32OmitEmpty(v int32) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// Int32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Int32(v int32) {
+ enc.Int64(int64(v))
+}
+
+// Int32OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Int32OmitEmpty(v int32) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// AddInt32Key adds an int32 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddInt32Key(key string, v int32) {
+ enc.Int64Key(key, int64(v))
+}
+
+// AddInt32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddInt32KeyOmitEmpty(key string, v int32) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
+
+// Int32Key adds an int32 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Int32Key(key string, v int32) {
+ enc.Int64Key(key, int64(v))
+}
+
+// Int32KeyOmitEmpty adds an int32 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Int32KeyOmitEmpty(key string, v int32) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
+
+// AddInt16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddInt16(v int16) {
+ enc.Int64(int64(v))
+}
+
+// AddInt16OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddInt16OmitEmpty(v int16) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// Int16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Int16(v int16) {
+ enc.Int64(int64(v))
+}
+
+// Int16OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Int16OmitEmpty(v int16) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// AddInt16Key adds an int16 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddInt16Key(key string, v int16) {
+ enc.Int64Key(key, int64(v))
+}
+
+// AddInt16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddInt16KeyOmitEmpty(key string, v int16) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
+
+// Int16Key adds an int16 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Int16Key(key string, v int16) {
+ enc.Int64Key(key, int64(v))
+}
+
+// Int16KeyOmitEmpty adds an int16 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Int16KeyOmitEmpty(key string, v int16) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
+
+// AddInt8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddInt8(v int8) {
+ enc.Int64(int64(v))
+}
+
+// AddInt8OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddInt8OmitEmpty(v int8) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// Int8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Int8(v int8) {
+ enc.Int64(int64(v))
+}
+
+// Int8OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Int8OmitEmpty(v int8) {
+ enc.Int64OmitEmpty(int64(v))
+}
+
+// AddInt8Key adds an int8 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddInt8Key(key string, v int8) {
+ enc.Int64Key(key, int64(v))
+}
+
+// AddInt8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddInt8KeyOmitEmpty(key string, v int8) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
+
+// Int8Key adds an int8 to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Int8Key(key string, v int8) {
+ enc.Int64Key(key, int64(v))
+}
+
+// Int8KeyOmitEmpty adds an int8 to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Int8KeyOmitEmpty(key string, v int8) {
+ enc.Int64KeyOmitEmpty(key, int64(v))
+}
diff --git a/encode_number_int_test.go b/encode_number_int_test.go
@@ -0,0 +1,538 @@
+package gojay
+
+import (
+ "math"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEncoderInt64(t *testing.T) {
+ var testCasesBasic = []struct {
+ name string
+ v int64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int64(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxInt64,
+ expectedJSON: "[9223372036854775807,9223372036854775807]",
+ },
+ {
+ name: "big",
+ v: int64(0),
+ expectedJSON: "[0,0]",
+ },
+ }
+ for _, testCase := range testCasesBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Int64(testCase.v)
+ enc.AddInt64(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+
+ var testCasesOmitEmpty = []struct {
+ name string
+ v int64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int64(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxInt64,
+ expectedJSON: "[9223372036854775807,9223372036854775807]",
+ },
+ {
+ name: "big",
+ v: int64(0),
+ expectedJSON: "[]",
+ },
+ }
+ for _, testCase := range testCasesOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Int64OmitEmpty(testCase.v)
+ enc.AddInt64OmitEmpty(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyBasic = []struct {
+ name string
+ v int64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int64(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxInt64,
+ expectedJSON: `{"foo":9223372036854775807,"bar":9223372036854775807}`,
+ },
+ {
+ name: "big",
+ v: int64(0),
+ expectedJSON: `{"foo":0,"bar":0}`,
+ },
+ }
+ for _, testCase := range testCasesKeyBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Int64Key("foo", testCase.v)
+ enc.AddInt64Key("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+
+ var testCasesKeyOmitEmpty = []struct {
+ name string
+ v int64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int64(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxInt64,
+ expectedJSON: `{"foo":9223372036854775807,"bar":9223372036854775807}`,
+ },
+ {
+ name: "big",
+ v: int64(0),
+ expectedJSON: `{}`,
+ },
+ }
+ for _, testCase := range testCasesKeyOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Int64KeyOmitEmpty("foo", testCase.v)
+ enc.AddInt64KeyOmitEmpty("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+}
+
+func TestEncoderInt32(t *testing.T) {
+ var testCasesBasic = []struct {
+ name string
+ v int32
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int32(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxInt32,
+ expectedJSON: "[2147483647,2147483647]",
+ },
+ {
+ name: "big",
+ v: int32(0),
+ expectedJSON: "[0,0]",
+ },
+ }
+ for _, testCase := range testCasesBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Int32(testCase.v)
+ enc.AddInt32(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesOmitEmpty = []struct {
+ name string
+ v int32
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int32(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxInt32,
+ expectedJSON: "[2147483647,2147483647]",
+ },
+ {
+ name: "big",
+ v: int32(0),
+ expectedJSON: "[]",
+ },
+ }
+ for _, testCase := range testCasesOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Int32OmitEmpty(testCase.v)
+ enc.AddInt32OmitEmpty(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyBasic = []struct {
+ name string
+ v int32
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int32(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxInt32,
+ expectedJSON: `{"foo":2147483647,"bar":2147483647}`,
+ },
+ {
+ name: "big",
+ v: int32(0),
+ expectedJSON: `{"foo":0,"bar":0}`,
+ },
+ }
+ for _, testCase := range testCasesKeyBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Int32Key("foo", testCase.v)
+ enc.AddInt32Key("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+
+ var testCasesKeyOmitEmpty = []struct {
+ name string
+ v int32
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int32(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxInt32,
+ expectedJSON: `{"foo":2147483647,"bar":2147483647}`,
+ },
+ {
+ name: "big",
+ v: int32(0),
+ expectedJSON: `{}`,
+ },
+ }
+ for _, testCase := range testCasesKeyOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Int32KeyOmitEmpty("foo", testCase.v)
+ enc.AddInt32KeyOmitEmpty("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+}
+
+func TestEncoderInt16(t *testing.T) {
+ var testCasesBasic = []struct {
+ name string
+ v int16
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int16(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxInt16,
+ expectedJSON: "[32767,32767]",
+ },
+ {
+ name: "big",
+ v: int16(0),
+ expectedJSON: "[0,0]",
+ },
+ }
+ for _, testCase := range testCasesBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Int16(testCase.v)
+ enc.AddInt16(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesOmitEmpty = []struct {
+ name string
+ v int16
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int16(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxInt16,
+ expectedJSON: "[32767,32767]",
+ },
+ {
+ name: "big",
+ v: int16(0),
+ expectedJSON: "[]",
+ },
+ }
+ for _, testCase := range testCasesOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Int16OmitEmpty(testCase.v)
+ enc.AddInt16OmitEmpty(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyBasic = []struct {
+ name string
+ v int16
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int16(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxInt16,
+ expectedJSON: `{"foo":32767,"bar":32767}`,
+ },
+ {
+ name: "big",
+ v: int16(0),
+ expectedJSON: `{"foo":0,"bar":0}`,
+ },
+ }
+ for _, testCase := range testCasesKeyBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Int16Key("foo", testCase.v)
+ enc.AddInt16Key("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+
+ var testCasesKeyOmitEmpty = []struct {
+ name string
+ v int16
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int16(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxInt16,
+ expectedJSON: `{"foo":32767,"bar":32767}`,
+ },
+ {
+ name: "big",
+ v: int16(0),
+ expectedJSON: `{}`,
+ },
+ }
+ for _, testCase := range testCasesKeyOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Int16KeyOmitEmpty("foo", testCase.v)
+ enc.AddInt16KeyOmitEmpty("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+}
+
+func TestEncoderInt8(t *testing.T) {
+ var testCasesBasic = []struct {
+ name string
+ v int8
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int8(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxInt8,
+ expectedJSON: "[127,127]",
+ },
+ {
+ name: "big",
+ v: int8(0),
+ expectedJSON: "[0,0]",
+ },
+ }
+ for _, testCase := range testCasesBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Int8(testCase.v)
+ enc.AddInt8(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesOmitEmpty = []struct {
+ name string
+ v int8
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int8(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxInt8,
+ expectedJSON: "[127,127]",
+ },
+ {
+ name: "big",
+ v: int8(0),
+ expectedJSON: "[]",
+ },
+ }
+ for _, testCase := range testCasesOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Int8OmitEmpty(testCase.v)
+ enc.AddInt8OmitEmpty(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyBasic = []struct {
+ name string
+ v int8
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int8(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxInt8,
+ expectedJSON: `{"foo":127,"bar":127}`,
+ },
+ {
+ name: "big",
+ v: int8(0),
+ expectedJSON: `{"foo":0,"bar":0}`,
+ },
+ }
+ for _, testCase := range testCasesKeyBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Int8Key("foo", testCase.v)
+ enc.AddInt8Key("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+
+ var testCasesKeyOmitEmpty = []struct {
+ name string
+ v int8
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: int8(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxInt8,
+ expectedJSON: `{"foo":127,"bar":127}`,
+ },
+ {
+ name: "big",
+ v: int8(0),
+ expectedJSON: `{}`,
+ },
+ }
+ for _, testCase := range testCasesKeyOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Int8KeyOmitEmpty("foo", testCase.v)
+ enc.AddInt8KeyOmitEmpty("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+}
diff --git a/encode_number_test.go b/encode_number_test.go
@@ -4,9 +4,10 @@ import (
"strings"
"testing"
- "github.com/stretchr/testify/assert"
- "math"
"fmt"
+ "math"
+
+ "github.com/stretchr/testify/assert"
)
func TestEncoderNumberEncodeAPI(t *testing.T) {
@@ -356,7 +357,7 @@ func TestAddNumberFunc(t *testing.T) {
})
}
-func TestEncoderUint64(t *testing.T) {
+func TestEncodeUint64(t *testing.T) {
builder := &strings.Builder{}
enc := BorrowEncoder(builder)
err := enc.Encode(uint64(145509))
diff --git a/encode_number_uint.go b/encode_number_uint.go
@@ -2,7 +2,7 @@ package gojay
import "strconv"
-// EncodeUint64 encodes an int64 to JSON
+// EncodeUint32 encodes an int64 to JSON
func (enc *Encoder) EncodeUint64(n uint64) error {
if enc.isPooled == 1 {
panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder"))
@@ -96,3 +96,135 @@ func (enc *Encoder) Uint64KeyOmitEmpty(key string, v uint64) {
enc.writeBytes(objKey)
enc.buf = strconv.AppendUint(enc.buf, v, 10)
}
+
+// AddUint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddUint32(v uint32) {
+ enc.Uint64(uint64(v))
+}
+
+// AddUint32OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddUint32OmitEmpty(v uint32) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// Uint32 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Uint32(v uint32) {
+ enc.Uint64(uint64(v))
+}
+
+// Uint32OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Uint32OmitEmpty(v uint32) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// AddUint32Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddUint32Key(key string, v uint32) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// AddUint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddUint32KeyOmitEmpty(key string, v uint32) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
+
+// Uint32Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Uint32Key(key string, v uint32) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// Uint32KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Uint32KeyOmitEmpty(key string, v uint32) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
+
+// AddUint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddUint16(v uint16) {
+ enc.Uint64(uint64(v))
+}
+
+// AddUint16OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddUint16OmitEmpty(v uint16) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// Uint16 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Uint16(v uint16) {
+ enc.Uint64(uint64(v))
+}
+
+// Uint16OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Uint16OmitEmpty(v uint16) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// AddUint16Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddUint16Key(key string, v uint16) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// AddUint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddUint16KeyOmitEmpty(key string, v uint16) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
+
+// Uint16Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Uint16Key(key string, v uint16) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// Uint16KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Uint16KeyOmitEmpty(key string, v uint16) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
+
+// AddUint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) AddUint8(v uint8) {
+ enc.Uint64(uint64(v))
+}
+
+// AddUint8OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) AddUint8OmitEmpty(v uint8) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// Uint8 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
+func (enc *Encoder) Uint8(v uint8) {
+ enc.Uint64(uint64(v))
+}
+
+// Uint8OmitEmpty adds an int to be encoded and skips it if its value is 0,
+// must be used inside a slice or array encoding (does not encode a key).
+func (enc *Encoder) Uint8OmitEmpty(v uint8) {
+ enc.Uint64OmitEmpty(uint64(v))
+}
+
+// AddUint8Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) AddUint8Key(key string, v uint8) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// AddUint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) AddUint8KeyOmitEmpty(key string, v uint8) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
+
+// Uint8Key adds an int to be encoded, must be used inside an object as it will encode a key
+func (enc *Encoder) Uint8Key(key string, v uint8) {
+ enc.Uint64Key(key, uint64(v))
+}
+
+// Uint8KeyOmitEmpty adds an int to be encoded and skips it if its value is 0.
+// Must be used inside an object as it will encode a key.
+func (enc *Encoder) Uint8KeyOmitEmpty(key string, v uint8) {
+ enc.Uint64KeyOmitEmpty(key, uint64(v))
+}
diff --git a/encode_number_uint_test.go b/encode_number_uint_test.go
@@ -0,0 +1,533 @@
+package gojay
+
+import (
+ "math"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEncoderUint64(t *testing.T) {
+ var testCasesBasic = []struct {
+ name string
+ v uint64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint64(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxUint64,
+ expectedJSON: "[18446744073709551615,18446744073709551615]",
+ },
+ {
+ name: "big",
+ v: uint64(0),
+ expectedJSON: "[0,0]",
+ },
+ }
+ for _, testCase := range testCasesBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Uint64(testCase.v)
+ enc.AddUint64(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesOmitEmpty = []struct {
+ name string
+ v uint64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint64(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxUint64,
+ expectedJSON: "[18446744073709551615,18446744073709551615]",
+ },
+ {
+ name: "big",
+ v: uint64(0),
+ expectedJSON: "[]",
+ },
+ }
+ for _, testCase := range testCasesOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Uint64OmitEmpty(testCase.v)
+ enc.AddUint64OmitEmpty(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyBasic = []struct {
+ name string
+ v uint64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint64(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxUint64,
+ expectedJSON: `{"foo":18446744073709551615,"bar":18446744073709551615}`,
+ },
+ {
+ name: "big",
+ v: uint64(0),
+ expectedJSON: `{"foo":0,"bar":0}`,
+ },
+ }
+ for _, testCase := range testCasesKeyBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Uint64Key("foo", testCase.v)
+ enc.AddUint64Key("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyOmitEmpty = []struct {
+ name string
+ v uint64
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint64(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxUint64,
+ expectedJSON: `{"foo":18446744073709551615,"bar":18446744073709551615}`,
+ },
+ {
+ name: "big",
+ v: uint64(0),
+ expectedJSON: "{}",
+ },
+ }
+ for _, testCase := range testCasesKeyOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Uint64KeyOmitEmpty("foo", testCase.v)
+ enc.AddUint64KeyOmitEmpty("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+}
+
+func TestEncoderUint32(t *testing.T) {
+ var testCasesBasic = []struct {
+ name string
+ v uint32
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint32(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxUint32,
+ expectedJSON: "[4294967295,4294967295]",
+ },
+ {
+ name: "big",
+ v: uint32(0),
+ expectedJSON: "[0,0]",
+ },
+ }
+ for _, testCase := range testCasesBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Uint32(testCase.v)
+ enc.AddUint32(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesOmitEmpty = []struct {
+ name string
+ v uint32
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint32(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxUint32,
+ expectedJSON: "[4294967295,4294967295]",
+ },
+ {
+ name: "big",
+ v: uint32(0),
+ expectedJSON: "[]",
+ },
+ }
+ for _, testCase := range testCasesOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Uint32OmitEmpty(testCase.v)
+ enc.AddUint32OmitEmpty(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyBasic = []struct {
+ name string
+ v uint32
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint32(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxUint32,
+ expectedJSON: `{"foo":4294967295,"bar":4294967295}`,
+ },
+ {
+ name: "big",
+ v: uint32(0),
+ expectedJSON: `{"foo":0,"bar":0}`,
+ },
+ }
+ for _, testCase := range testCasesKeyBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Uint32Key("foo", testCase.v)
+ enc.AddUint32Key("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyOmitEmpty = []struct {
+ name string
+ v uint32
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint32(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxUint32,
+ expectedJSON: `{"foo":4294967295,"bar":4294967295}`,
+ },
+ {
+ name: "big",
+ v: uint32(0),
+ expectedJSON: `{}`,
+ },
+ }
+ for _, testCase := range testCasesKeyOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Uint32KeyOmitEmpty("foo", testCase.v)
+ enc.AddUint32KeyOmitEmpty("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+}
+
+func TestEncoderUint16(t *testing.T) {
+ var testCasesBasic = []struct {
+ name string
+ v uint16
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint16(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxUint16,
+ expectedJSON: "[65535,65535]",
+ },
+ {
+ name: "big",
+ v: uint16(0),
+ expectedJSON: "[0,0]",
+ },
+ }
+ for _, testCase := range testCasesBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Uint16(testCase.v)
+ enc.AddUint16(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesOmitEmpty = []struct {
+ name string
+ v uint16
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint16(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxUint16,
+ expectedJSON: "[65535,65535]",
+ },
+ {
+ name: "big",
+ v: uint16(0),
+ expectedJSON: "[]",
+ },
+ }
+ for _, testCase := range testCasesOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Uint16OmitEmpty(testCase.v)
+ enc.AddUint16OmitEmpty(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyBasic = []struct {
+ name string
+ v uint16
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint16(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxUint16,
+ expectedJSON: `{"foo":65535,"bar":65535}`,
+ },
+ {
+ name: "big",
+ v: uint16(0),
+ expectedJSON: `{"foo":0,"bar":0}`,
+ },
+ }
+ for _, testCase := range testCasesKeyBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Uint16Key("foo", testCase.v)
+ enc.AddUint16Key("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyOmitEmpty = []struct {
+ name string
+ v uint16
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint16(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxUint16,
+ expectedJSON: `{"foo":65535,"bar":65535}`,
+ },
+ {
+ name: "big",
+ v: uint16(0),
+ expectedJSON: `{}`,
+ },
+ }
+ for _, testCase := range testCasesKeyOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Uint16KeyOmitEmpty("foo", testCase.v)
+ enc.AddUint16KeyOmitEmpty("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+}
+
+func TestEncoderUint8(t *testing.T) {
+ var testCasesBasic = []struct {
+ name string
+ v uint8
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint8(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxUint8,
+ expectedJSON: "[255,255]",
+ },
+ {
+ name: "big",
+ v: uint8(0),
+ expectedJSON: "[0,0]",
+ },
+ }
+ for _, testCase := range testCasesBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Uint8(testCase.v)
+ enc.AddUint8(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesOmitEmpty = []struct {
+ name string
+ v uint8
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint8(1),
+ expectedJSON: "[1,1]",
+ },
+ {
+ name: "big",
+ v: math.MaxUint8,
+ expectedJSON: "[255,255]",
+ },
+ {
+ name: "big",
+ v: uint8(0),
+ expectedJSON: "[]",
+ },
+ }
+ for _, testCase := range testCasesOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeArrayFunc(func(enc *Encoder) {
+ enc.Uint8OmitEmpty(testCase.v)
+ enc.AddUint8OmitEmpty(testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyBasic = []struct {
+ name string
+ v uint8
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint8(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxUint8,
+ expectedJSON: `{"foo":255,"bar":255}`,
+ },
+ {
+ name: "big",
+ v: uint8(0),
+ expectedJSON: `{"foo":0,"bar":0}`,
+ },
+ }
+ for _, testCase := range testCasesKeyBasic {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Uint8Key("foo", testCase.v)
+ enc.AddUint8Key("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+ var testCasesKeyOmitEmpty = []struct {
+ name string
+ v uint8
+ expectedJSON string
+ }{
+ {
+ name: "basic",
+ v: uint8(1),
+ expectedJSON: `{"foo":1,"bar":1}`,
+ },
+ {
+ name: "big",
+ v: math.MaxUint8,
+ expectedJSON: `{"foo":255,"bar":255}`,
+ },
+ {
+ name: "big",
+ v: uint8(0),
+ expectedJSON: `{}`,
+ },
+ }
+ for _, testCase := range testCasesKeyOmitEmpty {
+ t.Run(testCase.name, func(t *testing.T) {
+ var b = &strings.Builder{}
+ var enc = NewEncoder(b)
+ enc.Encode(EncodeObjectFunc(func(enc *Encoder) {
+ enc.Uint8KeyOmitEmpty("foo", testCase.v)
+ enc.AddUint8KeyOmitEmpty("bar", testCase.v)
+ }))
+ assert.Equal(t, testCase.expectedJSON, b.String())
+ })
+ }
+}
diff --git a/encode_stream.go b/encode_stream.go
@@ -169,12 +169,17 @@ func (s *StreamEncoder) AddInt(value int) {
s.Encoder.writeByte(s.delimiter)
}
-// AddFloat adds a float64 to be encoded.
-func (s *StreamEncoder) AddFloat(value float64) {
+// AddFloat64 adds a float64 to be encoded.
+func (s *StreamEncoder) AddFloat64(value float64) {
s.buf = strconv.AppendFloat(s.buf, value, 'f', -1, 64)
s.Encoder.writeByte(s.delimiter)
}
+// AddFloat adds a float64 to be encoded.
+func (s *StreamEncoder) AddFloat(value float64) {
+ s.AddFloat64(value)
+}
+
// Non exposed
func consume(init *StreamEncoder, s *StreamEncoder, m MarshalerStream) {
diff --git a/gojay.png b/gojay.png
Binary files differ.
diff --git a/gojay/Gopkg.lock b/gojay/Gopkg.lock
@@ -0,0 +1,14 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+[[projects]]
+ name = "github.com/fatih/structtag"
+ packages = ["."]
+ revision = "da3d9ab5b78fdc25d3a7614853b085200bd10da9"
+ version = "v0.1.0"
+
+[solve-meta]
+ analyzer-name = "dep"
+ analyzer-version = 1
+ inputs-digest = "859116499e74f6c88afba2065cef9e2ee1caa81d532e01ed144541f9d36faae8"
+ solver-name = "gps-cdcl"
+ solver-version = 1
diff --git a/gojay/Gopkg.toml b/gojay/Gopkg.toml
@@ -0,0 +1,26 @@
+
+# Gopkg.toml example
+#
+# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
+# for detailed Gopkg.toml documentation.
+#
+# required = ["github.com/user/thing/cmd/thing"]
+# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
+#
+# [[constraint]]
+# name = "github.com/user/project"
+# version = "1.0.0"
+#
+# [[constraint]]
+# name = "github.com/user/project2"
+# branch = "dev"
+# source = "github.com/myfork/project2"
+#
+# [[override]]
+# name = "github.com/x/y"
+# version = "2.4.0"
+
+
+[[constraint]]
+ name = "github.com/fatih/structtag"
+ version = "0.1.0"
diff --git a/gojay/Makefile b/gojay/Makefile
@@ -0,0 +1,15 @@
+.PHONY: build
+build:
+ go build ./
+
+.PHONY: test
+test:
+ go test -race -v .
+
+.PHONY: cover
+cover:
+ go test -tags test -coverprofile=coverage.out
+
+.PHONY: coverhtml
+coverhtml:
+ go tool cover -html=coverage.out
+\ No newline at end of file
diff --git a/gojay/README.md b/gojay/README.md
@@ -0,0 +1,63 @@
+# Gojay code generator
+
+This package provides a command line tool to generate gojay's marshaling and unmarshing interface implementation for struct, slice and map types.
+
+## Get started
+
+```sh
+go install github.com/francoispqt/gojay/gojay
+```
+
+## Generate code
+
+### Basic command
+
+The basic command is strait forward and easy to use:
+```sh
+gojay github.com/some/package TypeA,TypeB,TypeC output.go
+```
+If you just want to the output to stdout, omit the third parameter.
+
+### Using flags
+
+- p package to parse, relative path to $GOPATH/src
+- s file/dir to path, can be a relative or absolute path
+- t types to generate (comma separated)
+- o output file (relative or absolute path)
+
+Examples:
+- Specific types in a go package, to stdout:
+```sh
+gojay -p github.com/francoispqt/gojay/gojay/tests -t A,B,StrSlice
+```
+
+- Specific types in a go package, write to a file:
+```sh
+gojay -p github.com/francoispqt/gojay/gojay/tests -t A,B,StrSlice -o output.go
+```
+
+- Specific types in a go file, to stdout:
+```sh
+gojay -s somegofile.go -t SomeType
+```
+
+
+## Gojay tags
+
+You can add tags to your structs to control:
+- the JSON key
+- skip a struct field only for unmarshaling
+- skip a struct field only for marshaling
+- skip a struct field
+- the use of omit empty methods for marshaling
+
+### Example:
+```go
+type A struct {
+ Str string `gojay:"string"`
+ StrOmitEmpty string `gojay:"string,omitempty"`
+ SkipUnmarshal string `gojay:"skipUnmarshal,-u"`
+ SkipMarshal string `gojay:"skipMarshal,-m"`
+ Skip string `gojay:"-"`
+}
+```
diff --git a/gojay/gen.go b/gojay/gen.go
@@ -0,0 +1,146 @@
+package main
+
+import (
+ "go/ast"
+ "log"
+ "strings"
+ "text/template"
+)
+
+const gojayAnnotation = "//gojay:json"
+const genFileSuffix = "_gojay.go"
+const omitEmptyFuncName = "OmitEmpty"
+
+var pkgTpl *template.Template
+var genHeader = []byte("// Code generated by GoJay. DO NOT EDIT.\n\n")
+var gojayImport = []byte("import \"github.com/francoispqt/gojay\"\n")
+
+// Gen is the structure representing a generator
+type Gen struct {
+ b *strings.Builder
+ pkg string
+ src string
+ types []string
+ genTypes map[string]*ast.TypeSpec
+ vis *vis
+}
+
+type genTpl struct {
+ strTpl string
+ tpl *template.Template
+}
+
+type templateList map[string]*genTpl
+
+func init() {
+ t, err := template.New("pkgDef").
+ Parse("package {{.PkgName}} \n\n")
+ if err != nil {
+ log.Fatal(err)
+ }
+ pkgTpl = t
+}
+
+func parseTemplates(tpls templateList, pfx string) {
+ for k, t := range tpls {
+ tpl, err := template.New(pfx + k).Parse(t.strTpl)
+ if err != nil {
+ log.Fatal(err)
+ }
+ t.tpl = tpl
+ }
+}
+
+// NewGen returns a new generator
+func NewGen(p string, types []string) *Gen {
+ g := &Gen{
+ src: p,
+ types: types,
+ b: &strings.Builder{},
+ genTypes: make(map[string]*ast.TypeSpec),
+ }
+ return g
+}
+
+func (g *Gen) writePkg(pkg string) error {
+ err := pkgTpl.Execute(g.b, struct {
+ PkgName string
+ }{
+ PkgName: pkg,
+ })
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) writeGojayImport() error {
+ _, err := g.b.Write(gojayImport)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) writeGenHeader() error {
+ _, err := g.b.Write(genHeader)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) isGenType(typeName string) bool {
+ // check if type is in types
+ for _, t := range g.types {
+ if t == typeName {
+ return true
+ }
+ }
+ return false
+}
+
+// Gen starts the generation writing to the string builder
+func (g *Gen) Gen() error {
+ err := g.writeGenHeader()
+ if err != nil {
+ return err
+ }
+
+ // write package
+ err = g.writePkg(g.pkg)
+ if err != nil {
+ return err
+ }
+ // write import of gojay
+ err = g.writeGojayImport()
+ if err != nil {
+ return err
+ }
+ // range over specs
+ // generate interfaces implementations based on type
+ for _, s := range g.genTypes {
+ switch t := s.Type.(type) {
+ // is struct
+ case *ast.StructType:
+ err = g.genStruct(s.Name.String(), t)
+ if err != nil {
+ return err
+ }
+ // is array
+ case *ast.ArrayType:
+ err = g.genArray(s.Name.String(), t)
+ if err != nil {
+ return err
+ }
+ // is map
+ case *ast.MapType:
+ // TODO: generate for map type
+ err = g.genMap(s.Name.String(), t)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
diff --git a/gojay/gen_array.go b/gojay/gen_array.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "go/ast"
+)
+
+func (g *Gen) genArray(n string, s *ast.ArrayType) error {
+ err := g.arrGenUnmarshal(n, s)
+ if err != nil {
+ return err
+ }
+ err = g.arrGenMarshal(n, s)
+ if err != nil {
+ return err
+ }
+ return g.arrGenIsNil(n)
+}
diff --git a/gojay/gen_array_marshal.go b/gojay/gen_array_marshal.go
@@ -0,0 +1,241 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "go/ast"
+ "log"
+)
+
+func init() {}
+
+func (g *Gen) arrGenIsNil(n string) error {
+ err := arrMarshalTpl["isNil"].tpl.Execute(g.b, struct {
+ TypeName string
+ }{
+ TypeName: n,
+ })
+ return err
+}
+
+func (g *Gen) arrGenMarshal(n string, s *ast.ArrayType) error {
+ err := arrMarshalTpl["def"].tpl.Execute(g.b, struct {
+ TypeName string
+ }{
+ TypeName: n,
+ })
+ if err != nil {
+ return err
+ }
+ // determine type of element in array
+ switch t := s.Elt.(type) {
+ case *ast.Ident:
+ err := g.arrGenMarshalIdent(t, false)
+ if err != nil {
+ return err
+ }
+ case *ast.StarExpr:
+ switch ptrExp := t.X.(type) {
+ case *ast.Ident:
+ err := g.arrGenMarshalIdent(ptrExp, true)
+ if err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("Unknown type %s", n)
+ }
+ }
+ _, err = g.b.Write([]byte("}\n"))
+ if err != nil {
+ return err
+ }
+ return err
+}
+
+func (g *Gen) arrGenMarshalIdent(i *ast.Ident, ptr bool) error {
+ switch i.String() {
+ case "string":
+ g.arrMarshalString(ptr)
+ case "bool":
+ g.arrMarshalBool(ptr)
+ case "int":
+ g.arrMarshalInt("", ptr)
+ case "int64":
+ g.arrMarshalInt("64", ptr)
+ case "int32":
+ g.arrMarshalInt("32", ptr)
+ case "int16":
+ g.arrMarshalInt("16", ptr)
+ case "int8":
+ g.arrMarshalInt("8", ptr)
+ case "uint64":
+ g.arrMarshalUint("64", ptr)
+ case "uint32":
+ g.arrMarshalUint("32", ptr)
+ case "uint16":
+ g.arrMarshalUint("16", ptr)
+ case "uint8":
+ g.arrMarshalUint("8", ptr)
+ case "float64":
+ g.arrMarshalFloat("64", ptr)
+ case "float32":
+ g.arrMarshalFloat("32", ptr)
+ default:
+ // if ident is already in our spec list
+ if sp, ok := g.genTypes[i.Name]; ok {
+ return g.arrMarshalNonPrim(sp, ptr)
+ } else if i.Obj != nil {
+ // else check the obj infos
+ switch t := i.Obj.Decl.(type) {
+ case *ast.TypeSpec:
+ return g.arrMarshalNonPrim(t, ptr)
+ default:
+ return errors.New("could not determine what to do with type " + i.String())
+ }
+ }
+ return fmt.Errorf("Unknown type %s", i.Name)
+ }
+ return nil
+}
+
+func (g *Gen) arrMarshalNonPrim(sp *ast.TypeSpec, ptr bool) error {
+ switch sp.Type.(type) {
+ case *ast.StructType:
+ g.arrMarshalStruct(sp, ptr)
+ case *ast.ArrayType:
+ g.arrMarshalArr(sp, ptr)
+ }
+ return nil
+}
+
+func (g *Gen) arrMarshalString(ptr bool) {
+ if ptr {
+ err := arrMarshalTpl["stringPtr"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrMarshalTpl["string"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{"&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrMarshalBool(ptr bool) {
+ if ptr {
+ err := arrMarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrMarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{"&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrMarshalInt(intLen string, ptr bool) {
+ if ptr {
+ err := arrMarshalTpl["int"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrMarshalTpl["int"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrMarshalFloat(intLen string, ptr bool) {
+ if ptr {
+ err := arrMarshalTpl["float"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrMarshalTpl["float"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrMarshalUint(intLen string, ptr bool) {
+ if ptr {
+ err := arrMarshalTpl["uint"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrMarshalTpl["uint"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrMarshalStruct(st *ast.TypeSpec, ptr bool) {
+ if ptr {
+ err := arrMarshalTpl["structPtr"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrMarshalTpl["struct"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrMarshalArr(st *ast.TypeSpec, ptr bool) {
+ if ptr {
+ err := arrMarshalTpl["arrPtr"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrMarshalTpl["arr"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
diff --git a/gojay/gen_array_marshal_tpl.go b/gojay/gen_array_marshal_tpl.go
@@ -0,0 +1,63 @@
+package main
+
+var arrMarshalTpl = templateList{
+ "def": &genTpl{
+ strTpl: "\n// MarshalJSONArray implements gojay's MarshalerJSONArray" +
+ "\nfunc (v *{{.TypeName}}) MarshalJSONArray(enc *gojay.Encoder) {\n",
+ },
+ "isNil": &genTpl{
+ strTpl: "\n// IsNil implements gojay's MarshalerJSONArray" +
+ "\nfunc (v *{{.TypeName}}) IsNil() bool {\n" +
+ "\treturn *v == nil || len(*v) == 0\n" +
+ "}\n",
+ },
+ "string": &genTpl{
+ strTpl: "\tfor _, s := range *v {\n" +
+ "\t\tenc.String(s)\n" +
+ "\t}\n",
+ },
+ "bool": &genTpl{
+ strTpl: "\tfor _, s := range *v {\n" +
+ "\t\tenc.Bool(s)\n" +
+ "\t}\n",
+ },
+ "int": &genTpl{
+ strTpl: "\tfor _, s := range *v {\n" +
+ "\t\tenc.Int{{.IntLen}}(s)\n" +
+ "\t}\n",
+ },
+ "uint": &genTpl{
+ strTpl: "\tfor _, s := range *v {\n" +
+ "\t\tenc.Uint{{.IntLen}}(s)\n" +
+ "\t}\n",
+ },
+ "float": &genTpl{
+ strTpl: "\tfor _, s := range *v {\n" +
+ "\t\tenc.Float{{.IntLen}}(s)\n" +
+ "\t}\n",
+ },
+ "struct": &genTpl{
+ strTpl: "\tfor _, s := range *v {\n" +
+ "\t\tenc.Object(s)\n" +
+ "\t}\n",
+ },
+ "structPtr": &genTpl{
+ strTpl: "\tfor _, s := range *v {\n" +
+ "\t\tenc.Object(s)\n" +
+ "\t}\n",
+ },
+ "arr": &genTpl{
+ strTpl: "\tfor _, s := range *v {\n" +
+ "\t\tenc.Array(s)\n" +
+ "\t}\n",
+ },
+ "arrPtr": &genTpl{
+ strTpl: "\tfor _, s := range *v {\n" +
+ "\t\tenc.Array(s)\n" +
+ "\t}\n",
+ },
+}
+
+func init() {
+ parseTemplates(arrMarshalTpl, "arrMarshal")
+}
diff --git a/gojay/gen_array_test.go b/gojay/gen_array_test.go
@@ -0,0 +1,436 @@
+package main
+
+import (
+ "io"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGenArray(t *testing.T) {
+ testCases := map[string]struct {
+ input io.Reader
+ expectedResult string
+ }{
+ "basicStringSlice": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type StrSlice []string
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *StrSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var str string
+ if err := dec.String(&str); err != nil {
+ return err
+ }
+ *v = append(*v, str)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *StrSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.String(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *StrSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ "basicStringBool": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type BoolSlice []bool
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *BoolSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var b bool
+ if err := dec.Bool(&b); err != nil {
+ return err
+ }
+ *v = append(*v, b)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *BoolSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.Bool(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *BoolSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ "basicIntSlice": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntSlice []int
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var i int
+ if err := dec.Int(&i); err != nil {
+ return err
+ }
+ *v = append(*v, i)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.Int(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *IntSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ "basicInt8Slice": {
+ input: strings.NewReader(`package test
+
+ //gojay:json
+ type IntSlice []int8
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var i int8
+ if err := dec.Int8(&i); err != nil {
+ return err
+ }
+ *v = append(*v, i)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.Int8(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *IntSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ "basicInt16Slice": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntSlice []int16
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var i int16
+ if err := dec.Int16(&i); err != nil {
+ return err
+ }
+ *v = append(*v, i)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.Int16(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *IntSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ "basicInt32Slice": {
+ input: strings.NewReader(`package test
+
+ //gojay:json
+ type IntSlice []int32
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var i int32
+ if err := dec.Int32(&i); err != nil {
+ return err
+ }
+ *v = append(*v, i)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.Int32(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *IntSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ "basicInt64Slice": {
+ input: strings.NewReader(`package test
+
+ //gojay:json
+ type IntSlice []int64
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var i int64
+ if err := dec.Int64(&i); err != nil {
+ return err
+ }
+ *v = append(*v, i)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.Int64(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *IntSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ "basicUint64Slice": {
+ input: strings.NewReader(`package test
+
+ //gojay:json
+ type IntSlice []uint64
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var i uint64
+ if err := dec.Uint64(&i); err != nil {
+ return err
+ }
+ *v = append(*v, i)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.Uint64(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *IntSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ "basicFloatSlice": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntSlice []float64
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var i float64
+ if err := dec.Float64(&i); err != nil {
+ return err
+ }
+ *v = append(*v, i)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.Float64(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *IntSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ "basicFloat32Slice": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntSlice []float32
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *IntSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var i float32
+ if err := dec.Float32(&i); err != nil {
+ return err
+ }
+ *v = append(*v, i)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *IntSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.Float32(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *IntSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ "basicStructSlice": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type StructSlice []*Struct
+
+type Struct struct{
+ Str string
+}
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *StructSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var s = &Struct{}
+ if err := dec.Object(s); err != nil {
+ return err
+ }
+ *v = append(*v, s)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *StructSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.Object(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *StructSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ "basicSliceSlice": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type SliceStrSlice []StrSlice
+
+type StrSlice []string
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray
+func (v *SliceStrSlice) UnmarshalJSONArray(dec *gojay.Decoder) error {
+ var s = make(StrSlice, 0)
+ if err := dec.Array(&s); err != nil {
+ return err
+ }
+ *v = append(*v, s)
+ return nil
+}
+
+// MarshalJSONArray implements gojay's MarshalerJSONArray
+func (v *SliceStrSlice) MarshalJSONArray(enc *gojay.Encoder) {
+ for _, s := range *v {
+ enc.Array(s)
+ }
+}
+
+// IsNil implements gojay's MarshalerJSONArray
+func (v *SliceStrSlice) IsNil() bool {
+ return *v == nil || len(*v) == 0
+}
+`,
+ },
+ }
+ for n, testCase := range testCases {
+ t.Run(n, func(t *testing.T) {
+ g, err := MakeGenFromReader(testCase.input)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = g.Gen()
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(
+ t,
+ string(genHeader)+testCase.expectedResult,
+ g.b.String(),
+ )
+ })
+ }
+}
diff --git a/gojay/gen_array_unmarshal.go b/gojay/gen_array_unmarshal.go
@@ -0,0 +1,230 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "go/ast"
+ "log"
+)
+
+func (g *Gen) arrGenUnmarshal(n string, s *ast.ArrayType) error {
+ err := arrUnmarshalTpl["def"].tpl.Execute(g.b, struct {
+ TypeName string
+ }{
+ TypeName: n,
+ })
+ if err != nil {
+ return err
+ }
+ // determine type of element in array
+ switch t := s.Elt.(type) {
+ case *ast.Ident:
+ err := g.arrGenUnmarshalIdent(t, false)
+ if err != nil {
+ return err
+ }
+ case *ast.StarExpr:
+ switch ptrExp := t.X.(type) {
+ case *ast.Ident:
+ err := g.arrGenUnmarshalIdent(ptrExp, true)
+ if err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("Unknown type %s", n)
+ }
+ }
+ _, err = g.b.Write(structUnmarshalClose)
+ if err != nil {
+ return err
+ }
+ return err
+}
+
+func (g *Gen) arrGenUnmarshalIdent(i *ast.Ident, ptr bool) error {
+ switch i.String() {
+ case "string":
+ g.arrUnmarshalString(ptr)
+ case "bool":
+ g.arrUnmarshalBool(ptr)
+ case "int":
+ g.arrUnmarshalInt("", ptr)
+ case "int64":
+ g.arrUnmarshalInt("64", ptr)
+ case "int32":
+ g.arrUnmarshalInt("32", ptr)
+ case "int16":
+ g.arrUnmarshalInt("16", ptr)
+ case "int8":
+ g.arrUnmarshalInt("8", ptr)
+ case "uint64":
+ g.arrUnmarshalUint("64", ptr)
+ case "uint32":
+ g.arrUnmarshalUint("32", ptr)
+ case "uint16":
+ g.arrUnmarshalUint("16", ptr)
+ case "uint8":
+ g.arrUnmarshalUint("8", ptr)
+ case "float64":
+ g.arrUnmarshalFloat("64", ptr)
+ case "float32":
+ g.arrUnmarshalFloat("32", ptr)
+ default:
+ // if ident is already in our spec list
+ if sp, ok := g.genTypes[i.Name]; ok {
+ return g.arrUnmarshalNonPrim(sp, ptr)
+ } else if i.Obj != nil {
+ // else check the obj infos
+ switch t := i.Obj.Decl.(type) {
+ case *ast.TypeSpec:
+ return g.arrUnmarshalNonPrim(t, ptr)
+ default:
+ return errors.New("could not determine what to do with type " + i.String())
+ }
+ }
+ return fmt.Errorf("Unknown type %s", i.Name)
+ }
+ return nil
+}
+
+func (g *Gen) arrUnmarshalNonPrim(sp *ast.TypeSpec, ptr bool) error {
+ switch sp.Type.(type) {
+ case *ast.StructType:
+ g.arrUnmarshalStruct(sp, ptr)
+ case *ast.ArrayType:
+ g.arrUnmarshalArr(sp, ptr)
+ }
+ return nil
+}
+
+func (g *Gen) arrUnmarshalString(ptr bool) {
+ if ptr {
+ err := arrUnmarshalTpl["stringPtr"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrUnmarshalTpl["string"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{"&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrUnmarshalBool(ptr bool) {
+ if ptr {
+ err := arrUnmarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrUnmarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{"&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrUnmarshalInt(intLen string, ptr bool) {
+ if ptr {
+ err := arrUnmarshalTpl["int"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrUnmarshalTpl["int"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrUnmarshalUint(intLen string, ptr bool) {
+ if ptr {
+ err := arrUnmarshalTpl["uint"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrUnmarshalTpl["uint"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrUnmarshalFloat(intLen string, ptr bool) {
+ if ptr {
+ err := arrUnmarshalTpl["float"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrUnmarshalTpl["float"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrUnmarshalStruct(st *ast.TypeSpec, ptr bool) {
+ if ptr {
+ err := arrUnmarshalTpl["structPtr"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrUnmarshalTpl["struct"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) arrUnmarshalArr(st *ast.TypeSpec, ptr bool) {
+ if ptr {
+ err := arrUnmarshalTpl["arrPtr"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := arrUnmarshalTpl["arr"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
diff --git a/gojay/gen_array_unmarshal_tpl.go b/gojay/gen_array_unmarshal_tpl.go
@@ -0,0 +1,70 @@
+package main
+
+var arrUnmarshalTpl = templateList{
+ "def": &genTpl{
+ strTpl: "\n// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray" +
+ "\nfunc (v *{{.TypeName}}) UnmarshalJSONArray(dec *gojay.Decoder) error {\n",
+ },
+ "string": &genTpl{
+ strTpl: "\tvar str string" +
+ "\n\tif err := dec.String(&str); err != nil {\n" +
+ "\t\treturn err\n\t}\n" +
+ "\t*v = append(*v, str)\n",
+ },
+ "stringPtr": &genTpl{
+ strTpl: "\n// UnmarshalJSONArray implements gojay's UnmarshalerJSONArray" +
+ "\nfunc (v *{{.TypeName}}) UnmarshalJSONArray(dec *gojay.Decoder) error {\n",
+ },
+ "int": &genTpl{
+ strTpl: "\tvar i int{{.IntLen}}" +
+ "\n\tif err := dec.Int{{.IntLen}}(&i); err != nil {\n" +
+ "\t\treturn err\n\t}\n" +
+ "\t*v = append(*v, i)\n",
+ },
+ "uint": &genTpl{
+ strTpl: "\tvar i uint{{.IntLen}}" +
+ "\n\tif err := dec.Uint{{.IntLen}}(&i); err != nil {\n" +
+ "\t\treturn err\n\t}\n" +
+ "\t*v = append(*v, i)\n",
+ },
+ "float": &genTpl{
+ strTpl: "\tvar i float{{.IntLen}}" +
+ "\n\tif err := dec.Float{{.IntLen}}(&i); err != nil {\n" +
+ "\t\treturn err\n\t}\n" +
+ "\t*v = append(*v, i)\n",
+ },
+ "bool": &genTpl{
+ strTpl: "\tvar b bool" +
+ "\n\tif err := dec.Bool(&b); err != nil {\n" +
+ "\t\treturn err\n\t}\n" +
+ "\t*v = append(*v, b)\n",
+ },
+ "struct": &genTpl{
+ strTpl: "\tvar s = {{.StructName}}{}" +
+ "\n\tif err := dec.Object(&s); err != nil {\n" +
+ "\t\treturn err\n\t}\n" +
+ "\t*v = append(*v, s)\n",
+ },
+ "structPtr": &genTpl{
+ strTpl: "\tvar s = &{{.StructName}}{}" +
+ "\n\tif err := dec.Object(s); err != nil {\n" +
+ "\t\treturn err\n\t}\n" +
+ "\t*v = append(*v, s)\n",
+ },
+ "arr": &genTpl{
+ strTpl: "\tvar s = make({{.StructName}}, 0)" +
+ "\n\tif err := dec.Array(&s); err != nil {\n" +
+ "\t\treturn err\n\t}\n" +
+ "\t*v = append(*v, s)\n",
+ },
+ "arrPtr": &genTpl{
+ strTpl: "\tvar s = make({{.StructName}}, 0)" +
+ "\n\tif err := dec.Array(&s); err != nil {\n" +
+ "\t\treturn err\n\t}\n" +
+ "\t*v = append(*v, &s)\n",
+ },
+}
+
+func init() {
+ parseTemplates(arrUnmarshalTpl, "arrUnarshal")
+}
diff --git a/gojay/gen_map.go b/gojay/gen_map.go
@@ -0,0 +1,19 @@
+package main
+
+import "go/ast"
+
+func (g *Gen) genMap(n string, s *ast.MapType) error {
+ err := g.mapGenUnmarshalObj(n, s)
+ if err != nil {
+ return err
+ }
+ err = g.mapGenNKeys(n, 0)
+ if err != nil {
+ return err
+ }
+ err = g.mapGenMarshalObj(n, s)
+ if err != nil {
+ return err
+ }
+ return g.mapGenIsNil(n)
+}
diff --git a/gojay/gen_map_marshal.go b/gojay/gen_map_marshal.go
@@ -0,0 +1,201 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "go/ast"
+ "log"
+)
+
+func (g *Gen) mapGenIsNil(n string) error {
+ err := mapMarshalTpl["isNil"].tpl.Execute(g.b, struct {
+ StructName string
+ }{
+ StructName: n,
+ })
+ return err
+}
+
+func (g *Gen) mapGenMarshalObj(n string, s *ast.MapType) error {
+ err := mapMarshalTpl["def"].tpl.Execute(g.b, struct {
+ StructName string
+ }{
+ StructName: n,
+ })
+ if err != nil {
+ return err
+ }
+ switch t := s.Value.(type) {
+ case *ast.Ident:
+ var err error
+ err = g.mapGenMarshalIdent(t, false)
+ if err != nil {
+ return err
+ }
+ case *ast.StarExpr:
+ switch ptrExp := t.X.(type) {
+ case *ast.Ident:
+ var err error
+ err = g.mapGenMarshalIdent(ptrExp, true)
+ if err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("Unknown type %s", n)
+ }
+ }
+ _, err = g.b.Write([]byte("}\n"))
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) mapGenMarshalIdent(i *ast.Ident, ptr bool) error {
+ switch i.String() {
+ case "string":
+ g.mapMarshalString(ptr)
+ case "bool":
+ g.mapMarshalBool(ptr)
+ case "int":
+ g.mapMarshalInt("", ptr)
+ case "int64":
+ g.mapMarshalInt("64", ptr)
+ case "int32":
+ g.mapMarshalInt("32", ptr)
+ case "int16":
+ g.mapMarshalInt("16", ptr)
+ case "int8":
+ g.mapMarshalInt("8", ptr)
+ case "uint64":
+ g.mapMarshalUint("64", ptr)
+ case "uint32":
+ g.mapMarshalUint("32", ptr)
+ case "uint16":
+ g.mapMarshalUint("16", ptr)
+ case "uint8":
+ g.mapMarshalUint("8", ptr)
+ case "float64":
+ g.mapMarshalFloat("64", ptr)
+ case "float32":
+ g.mapMarshalFloat("32", ptr)
+ default:
+ // if ident is already in our spec list
+ if sp, ok := g.genTypes[i.Name]; ok {
+ g.mapMarshalNonPrim(sp, ptr)
+ } else if i.Obj != nil {
+ switch t := i.Obj.Decl.(type) {
+ case *ast.TypeSpec:
+ g.mapMarshalNonPrim(t, ptr)
+ default:
+ return errors.New("could not determine what to do with type " + i.String())
+ }
+ } else {
+ return fmt.Errorf("Unknown type %s", i.Name)
+ }
+ }
+ return nil
+}
+
+func (g *Gen) mapMarshalNonPrim(sp *ast.TypeSpec, ptr bool) {
+ switch sp.Type.(type) {
+ case *ast.StructType:
+ g.mapMarshalStruct(sp, ptr)
+ case *ast.ArrayType:
+ g.mapMarshalArr(sp, ptr)
+ }
+}
+
+func (g *Gen) mapMarshalString(ptr bool) {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := mapMarshalTpl["string"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) mapMarshalBool(ptr bool) {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := mapMarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) mapMarshalInt(intLen string, ptr bool) {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := mapMarshalTpl["int"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) mapMarshalUint(intLen string, ptr bool) {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := mapMarshalTpl["uint"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) mapMarshalFloat(intLen string, ptr bool) {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := mapMarshalTpl["float"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) mapMarshalStruct(st *ast.TypeSpec, ptr bool) {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ var err = mapMarshalTpl["struct"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) mapMarshalArr(st *ast.TypeSpec, ptr bool) {
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ var err = mapMarshalTpl["arr"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/gojay/gen_map_marshal_tpl.go b/gojay/gen_map_marshal_tpl.go
@@ -0,0 +1,60 @@
+package main
+
+var mapMarshalTpl = templateList{
+ "def": &genTpl{
+ strTpl: "\n// MarshalJSONObject implements gojay's MarshalerJSONObject" +
+ "\nfunc (v {{.StructName}}) MarshalJSONObject(enc *gojay.Encoder) {\n",
+ },
+ "isNil": &genTpl{
+ strTpl: `
+// IsNil returns wether the structure is nil value or not
+func (v {{.StructName}}) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "string": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.StringKey(k, {{.Ptr}}s)
+ }
+`,
+ },
+ "int": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.Int{{.IntLen}}Key(k, {{.Ptr}}s)
+ }
+`,
+ },
+ "uint": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.Uint{{.IntLen}}Key(k, {{.Ptr}}s)
+ }
+`,
+ },
+ "float": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.Float{{.IntLen}}Key(k, {{.Ptr}}s)
+ }
+`,
+ },
+ "bool": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.BoolKey(k, {{.Ptr}}s)
+ }
+`,
+ },
+ "struct": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.ObjectKey(k, s)
+ }
+`,
+ },
+ "arr": &genTpl{
+ strTpl: ` for k, s := range v {
+ enc.ArrayKey(k, s)
+ }
+`,
+ },
+}
+
+func init() {
+ parseTemplates(mapMarshalTpl, "mapMarshal")
+}
diff --git a/gojay/gen_map_test.go b/gojay/gen_map_test.go
@@ -0,0 +1,616 @@
+package main
+
+import (
+ "io"
+ "log"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGenMap(t *testing.T) {
+ testCases := map[string]struct {
+ input io.Reader
+ expectedResult string
+ }{
+ "basicMapStringString": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type StrMap map[string]string
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v StrMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var str string
+ if err := dec.String(&str); err != nil {
+ return err
+ }
+ v[k] = str
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v StrMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v StrMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.StringKey(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v StrMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringStringPtr": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type StrMap map[string]*string
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v StrMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var str string
+ if err := dec.String(&str); err != nil {
+ return err
+ }
+ v[k] = &str
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v StrMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v StrMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.StringKey(k, *s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v StrMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringInt": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]int
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i int
+ if err := dec.Int(&i); err != nil {
+ return err
+ }
+ v[k] = i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.IntKey(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringIntPtr": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]*int
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i int
+ if err := dec.Int(&i); err != nil {
+ return err
+ }
+ v[k] = &i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.IntKey(k, *s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringInt64": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]int64
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i int64
+ if err := dec.Int64(&i); err != nil {
+ return err
+ }
+ v[k] = i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.Int64Key(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringInt64Ptr": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]*int64
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i int64
+ if err := dec.Int64(&i); err != nil {
+ return err
+ }
+ v[k] = &i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.Int64Key(k, *s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringInt32": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]int32
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i int32
+ if err := dec.Int32(&i); err != nil {
+ return err
+ }
+ v[k] = i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.Int32Key(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringInt16": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]int16
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i int16
+ if err := dec.Int16(&i); err != nil {
+ return err
+ }
+ v[k] = i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.Int16Key(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringInt8": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]int8
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i int8
+ if err := dec.Int8(&i); err != nil {
+ return err
+ }
+ v[k] = i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.Int8Key(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringUint64": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]uint64
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i uint64
+ if err := dec.Uint64(&i); err != nil {
+ return err
+ }
+ v[k] = i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.Uint64Key(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringUint64Ptr": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]*uint64
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i uint64
+ if err := dec.Uint64(&i); err != nil {
+ return err
+ }
+ v[k] = &i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.Uint64Key(k, *s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringUint32": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]uint32
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i uint32
+ if err := dec.Uint32(&i); err != nil {
+ return err
+ }
+ v[k] = i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.Uint32Key(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringUint16": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]uint16
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i uint16
+ if err := dec.Uint16(&i); err != nil {
+ return err
+ }
+ v[k] = i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.Uint16Key(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringUint8": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type IntMap map[string]uint8
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v IntMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var i uint8
+ if err := dec.Uint8(&i); err != nil {
+ return err
+ }
+ v[k] = i
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v IntMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v IntMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.Uint8Key(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v IntMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringBool": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type BoolMap map[string]bool
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v BoolMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var b bool
+ if err := dec.Bool(&b); err != nil {
+ return err
+ }
+ v[k] = b
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v BoolMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v BoolMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.BoolKey(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v BoolMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringBoolPtr": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type BoolMap map[string]*bool
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v BoolMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var b bool
+ if err := dec.Bool(&b); err != nil {
+ return err
+ }
+ v[k] = &b
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v BoolMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v BoolMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.BoolKey(k, *s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v BoolMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ "basicMapStringStruct": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type BoolMap map[string]*Test
+
+type Test struct{}
+`),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v BoolMap) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ var s = &Test{}
+ if err := dec.Object(s); err != nil {
+ return err
+ }
+ v[k] = s
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v BoolMap) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v BoolMap) MarshalJSONObject(enc *gojay.Encoder) {
+ for k, s := range v {
+ enc.ObjectKey(k, s)
+ }
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v BoolMap) IsNil() bool { return v == nil || len(v) == 0 }
+`,
+ },
+ }
+ for n, testCase := range testCases {
+ t.Run(n, func(t *testing.T) {
+ g, err := MakeGenFromReader(testCase.input)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = g.Gen()
+ if err != nil {
+ t.Fatal(err)
+ }
+ log.Print(g.b.String())
+ assert.Equal(
+ t,
+ string(genHeader)+testCase.expectedResult,
+ g.b.String(),
+ )
+ })
+ }
+}
diff --git a/gojay/gen_map_unmarshal.go b/gojay/gen_map_unmarshal.go
@@ -0,0 +1,242 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "go/ast"
+ "log"
+)
+
+func (g *Gen) mapGenNKeys(n string, count int) error {
+ err := mapUnmarshalTpl["nKeys"].tpl.Execute(g.b, struct {
+ NKeys int
+ StructName string
+ }{
+ NKeys: count,
+ StructName: n,
+ })
+ return err
+}
+
+func (g *Gen) mapGenUnmarshalObj(n string, s *ast.MapType) error {
+ err := mapUnmarshalTpl["def"].tpl.Execute(g.b, struct {
+ TypeName string
+ }{
+ TypeName: n,
+ })
+ if err != nil {
+ return err
+ }
+ switch t := s.Value.(type) {
+ case *ast.Ident:
+ var err error
+ err = g.mapGenUnmarshalIdent(t, false)
+ if err != nil {
+ return err
+ }
+ case *ast.StarExpr:
+ switch ptrExp := t.X.(type) {
+ case *ast.Ident:
+ var err error
+ err = g.mapGenUnmarshalIdent(ptrExp, true)
+ if err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("Unknown type %s", n)
+ }
+ }
+ _, err = g.b.Write(structUnmarshalClose)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (g *Gen) mapGenUnmarshalIdent(i *ast.Ident, ptr bool) error {
+ switch i.String() {
+ case "string":
+ g.mapUnmarshalString(ptr)
+ case "bool":
+ g.mapUnmarshalBool(ptr)
+ case "int":
+ g.mapUnmarshalInt("", ptr)
+ case "int64":
+ g.mapUnmarshalInt("64", ptr)
+ case "int32":
+ g.mapUnmarshalInt("32", ptr)
+ case "int16":
+ g.mapUnmarshalInt("16", ptr)
+ case "int8":
+ g.mapUnmarshalInt("8", ptr)
+ case "uint64":
+ g.mapUnmarshalUint("64", ptr)
+ case "uint32":
+ g.mapUnmarshalUint("32", ptr)
+ case "uint16":
+ g.mapUnmarshalUint("16", ptr)
+ case "uint8":
+ g.mapUnmarshalUint("8", ptr)
+ case "float64":
+ g.mapUnmarshalFloat("64", ptr)
+ case "float32":
+ g.mapUnmarshalFloat("32", ptr)
+ default:
+ // if ident is already in our spec list
+ if sp, ok := g.genTypes[i.Name]; ok {
+ err := g.mapUnmarshalNonPrim(sp, ptr)
+ if err != nil {
+ return err
+ }
+ } else if i.Obj != nil {
+ // else check the obj infos
+ switch t := i.Obj.Decl.(type) {
+ case *ast.TypeSpec:
+ err := g.mapUnmarshalNonPrim(t, ptr)
+ if err != nil {
+ return err
+ }
+ default:
+ return errors.New("could not determine what to do with type " + i.String())
+ }
+ } else {
+ return fmt.Errorf("Unknown type %s", i.Name)
+ }
+ }
+ return nil
+}
+
+func (g *Gen) mapUnmarshalNonPrim(sp *ast.TypeSpec, ptr bool) error {
+ switch sp.Type.(type) {
+ case *ast.StructType:
+ g.mapUnmarshalStruct(sp, ptr)
+ return nil
+ case *ast.ArrayType:
+ g.mapUnmarshalArr(sp, ptr)
+ return nil
+ }
+ return errors.New("Unknown type")
+}
+
+func (g *Gen) mapUnmarshalString(ptr bool) {
+ if ptr {
+ err := mapUnmarshalTpl["string"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{"&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := mapUnmarshalTpl["string"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) mapUnmarshalBool(ptr bool) {
+ if ptr {
+ err := mapUnmarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{"&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := mapUnmarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Ptr string
+ }{""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) mapUnmarshalInt(intLen string, ptr bool) {
+ if ptr {
+ err := mapUnmarshalTpl["int"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := mapUnmarshalTpl["int"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) mapUnmarshalUint(intLen string, ptr bool) {
+ if ptr {
+ err := mapUnmarshalTpl["uint"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := mapUnmarshalTpl["uint"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) mapUnmarshalFloat(intLen string, ptr bool) {
+ if ptr {
+ err := mapUnmarshalTpl["float"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := mapUnmarshalTpl["float"].tpl.Execute(g.b, struct {
+ IntLen string
+ Ptr string
+ }{intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) mapUnmarshalStruct(st *ast.TypeSpec, ptr bool) {
+ if ptr {
+ err := mapUnmarshalTpl["structPtr"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err := mapUnmarshalTpl["struct"].tpl.Execute(g.b, struct {
+ StructName string
+ }{st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) mapUnmarshalArr(st *ast.TypeSpec, ptr bool) {
+ err := mapUnmarshalTpl["arr"].tpl.Execute(g.b, struct {
+ TypeName string
+ }{st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/gojay/gen_map_unmarshal_tpl.go b/gojay/gen_map_unmarshal_tpl.go
@@ -0,0 +1,82 @@
+package main
+
+var mapUnmarshalTpl = templateList{
+ "def": &genTpl{
+ strTpl: "\n// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject" +
+ "\nfunc (v {{.TypeName}}) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {\n",
+ },
+ "nKeys": &genTpl{
+ strTpl: `
+// NKeys returns the number of keys to unmarshal
+func (v {{.StructName}}) NKeys() int { return {{.NKeys}} }
+`,
+ },
+ "string": &genTpl{
+ strTpl: ` var str string
+ if err := dec.String(&str); err != nil {
+ return err
+ }
+ v[k] = {{.Ptr}}str
+`,
+ },
+ "int": &genTpl{
+ strTpl: ` var i int{{.IntLen}}
+ if err := dec.Int{{.IntLen}}(&i); err != nil {
+ return err
+ }
+ v[k] = {{.Ptr}}i
+`,
+ },
+ "uint": &genTpl{
+ strTpl: ` var i uint{{.IntLen}}
+ if err := dec.Uint{{.IntLen}}(&i); err != nil {
+ return err
+ }
+ v[k] = {{.Ptr}}i
+`,
+ },
+ "float": &genTpl{
+ strTpl: ` var i float{{.IntLen}}
+ if err := dec.Float{{.IntLen}}(&i); err != nil {
+ return err
+ }
+ v[k] = {{.Ptr}}i
+`,
+ },
+ "bool": &genTpl{
+ strTpl: ` var b bool
+ if err := dec.Bool(&b); err != nil {
+ return err
+ }
+ v[k] = {{.Ptr}}b
+`,
+ },
+ "struct": &genTpl{
+ strTpl: ` var s = {{.StructName}}{}
+ if err := dec.Object(&s); err != nil {
+ return err
+ }
+ v[k] = s
+`,
+ },
+ "structPtr": &genTpl{
+ strTpl: ` var s = &{{.StructName}}{}
+ if err := dec.Object(s); err != nil {
+ return err
+ }
+ v[k] = s
+`,
+ },
+ "arr": &genTpl{
+ strTpl: ` var s = &{{.StructName}}{}
+ if err := dec.Array(s); err != nil {
+ return err
+ }
+ v[k] = s
+`,
+ },
+}
+
+func init() {
+ parseTemplates(mapUnmarshalTpl, "mapUnmarshal")
+}
diff --git a/gojay/gen_parse.go b/gojay/gen_parse.go
@@ -0,0 +1,63 @@
+//+build !test
+
+package main
+
+import (
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "os"
+)
+
+func (g *Gen) parse() error {
+ var f, err = os.Stat(g.src)
+ if err != nil {
+ return err
+ }
+ if f.IsDir() {
+ err = g.parseDir()
+ } else {
+ err = g.parseFile()
+ }
+ return err
+}
+
+func (g *Gen) parseDir() error {
+ // parse the given path
+ fset := token.NewFileSet()
+ pkgs, err := parser.ParseDir(fset, g.src, nil, parser.ParseComments)
+ if err != nil {
+ return err
+ }
+ // range across packages
+ for pkgName, pkg := range pkgs {
+ v := newVisitor(g, pkgName)
+ g.pkg = pkgName
+ // range on files in package
+ for _, f := range pkg.Files {
+ ast.Walk(v, f)
+ if err != nil {
+ return err
+ }
+ }
+ g.vis = v
+ }
+ return nil
+}
+
+func (g *Gen) parseFile() error {
+ // parse the given path
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, g.src, nil, parser.ParseComments)
+ if err != nil {
+ return err
+ }
+ g.pkg = f.Name.Name
+ v := newVisitor(g, g.pkg)
+ ast.Walk(v, f)
+ if err != nil {
+ return err
+ }
+ g.vis = v
+ return nil
+}
diff --git a/gojay/gen_struct_marshal.go b/gojay/gen_struct_marshal.go
@@ -0,0 +1,290 @@
+package main
+
+import (
+ "fmt"
+ "go/ast"
+ "log"
+)
+
+func (g *Gen) structGenIsNil(n string) error {
+ err := structMarshalTpl["isNil"].tpl.Execute(g.b, struct {
+ StructName string
+ }{
+ StructName: n,
+ })
+ return err
+}
+
+func (g *Gen) structGenMarshalObj(n string, s *ast.StructType) (int, error) {
+ err := structMarshalTpl["def"].tpl.Execute(g.b, struct {
+ StructName string
+ }{
+ StructName: n,
+ })
+ if err != nil {
+ return 0, err
+ }
+ keys := 0
+ if len(s.Fields.List) > 0 {
+ // TODO: check tags
+ for _, field := range s.Fields.List {
+ // check if has hide tag
+ var omitEmpty string
+ if field.Tag != nil {
+ if hasTagMarshalHide(field.Tag) {
+ continue
+ }
+ if hasTagOmitEmpty(field.Tag) {
+ omitEmpty = omitEmptyFuncName
+ }
+ }
+ switch t := field.Type.(type) {
+ case *ast.Ident:
+ var err error
+ keys, err = g.structGenMarshalIdent(field, t, keys, omitEmpty, false)
+ if err != nil {
+ return 0, err
+ }
+ case *ast.StarExpr:
+ switch ptrExp := t.X.(type) {
+ case *ast.Ident:
+ var err error
+ keys, err = g.structGenMarshalIdent(field, ptrExp, keys, omitEmpty, true)
+ if err != nil {
+ return 0, err
+ }
+ default:
+ return 0, fmt.Errorf("Unknown type %s", n)
+ }
+ }
+ }
+ }
+ _, err = g.b.Write([]byte("}\n"))
+ if err != nil {
+ return 0, err
+ }
+ return keys, nil
+}
+
+func (g *Gen) structGenMarshalIdent(field *ast.Field, i *ast.Ident, keys int, omitEmpty string, ptr bool) (int, error) {
+ var keyV = getStructFieldJSONKey(field)
+
+ switch i.String() {
+ case "string":
+ g.structMarshalString(field, keyV, omitEmpty, ptr)
+ keys++
+ case "bool":
+ g.structMarshalBool(field, keyV, omitEmpty, ptr)
+ keys++
+ case "int":
+ g.structMarshalInt(field, keyV, "", omitEmpty, ptr)
+ keys++
+ case "int64":
+ g.structMarshalInt(field, keyV, "64", omitEmpty, ptr)
+ keys++
+ case "int32":
+ g.structMarshalInt(field, keyV, "32", omitEmpty, ptr)
+ keys++
+ case "int16":
+ g.structMarshalInt(field, keyV, "16", omitEmpty, ptr)
+ keys++
+ case "int8":
+ g.structMarshalInt(field, keyV, "8", omitEmpty, ptr)
+ keys++
+ case "uint64":
+ g.structMarshalUint(field, keyV, "64", omitEmpty, ptr)
+ keys++
+ case "uint32":
+ g.structMarshalUint(field, keyV, "32", omitEmpty, ptr)
+ keys++
+ case "uint16":
+ g.structMarshalUint(field, keyV, "16", omitEmpty, ptr)
+ keys++
+ case "uint8":
+ g.structMarshalUint(field, keyV, "8", omitEmpty, ptr)
+ keys++
+ case "float64":
+ g.structMarshalFloat(field, keyV, "64", omitEmpty, ptr)
+ keys++
+ case "float32":
+ g.structMarshalFloat(field, keyV, "32", omitEmpty, ptr)
+ keys++
+ default:
+ // if ident is already in our spec list
+ if sp, ok := g.genTypes[i.Name]; ok {
+ err := g.structMarshalNonPrim(field, keyV, sp, omitEmpty, ptr)
+ if err != nil {
+ return 0, err
+ }
+ keys++
+ } else if i.Obj != nil {
+ switch t := i.Obj.Decl.(type) {
+ case *ast.TypeSpec:
+ var err = g.structMarshalNonPrim(field, keyV, t, omitEmpty, ptr)
+ if err != nil {
+ return 0, err
+ }
+ keys++
+ default:
+ g.structMarshalAny(field, keyV, sp, ptr)
+ keys++
+ }
+ } else {
+ g.structMarshalAny(field, keyV, sp, ptr)
+ keys++
+ }
+ }
+ return keys, nil
+}
+
+func (g *Gen) structMarshalNonPrim(field *ast.Field, keyV string, sp *ast.TypeSpec, omitEmpty string, ptr bool) error {
+ switch sp.Type.(type) {
+ case *ast.StructType:
+ g.structMarshalStruct(field, keyV, sp, omitEmpty, ptr)
+ return nil
+ case *ast.ArrayType:
+ g.structMarshalArr(field, keyV, sp, omitEmpty, ptr)
+ return nil
+ default:
+ g.structMarshalAny(field, keyV, sp, ptr)
+ }
+ return nil
+}
+
+func (g *Gen) structMarshalString(field *ast.Field, keyV string, omitEmpty string, ptr bool) {
+ key := field.Names[0].String()
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := structMarshalTpl["string"].tpl.Execute(g.b, struct {
+ Field string
+ Key string
+ OmitEmpty string
+ Ptr string
+ }{key, keyV, omitEmpty, ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) structMarshalBool(field *ast.Field, keyV string, omitEmpty string, ptr bool) {
+ key := field.Names[0].String()
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := structMarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Field string
+ Key string
+ OmitEmpty string
+ Ptr string
+ }{key, keyV, omitEmpty, ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) structMarshalInt(field *ast.Field, keyV string, intLen string, omitEmpty string, ptr bool) {
+ key := field.Names[0].String()
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := structMarshalTpl["int"].tpl.Execute(g.b, struct {
+ Field string
+ IntLen string
+ Key string
+ OmitEmpty string
+ Ptr string
+ }{key, intLen, keyV, omitEmpty, ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) structMarshalUint(field *ast.Field, keyV string, intLen string, omitEmpty string, ptr bool) {
+ key := field.Names[0].String()
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := structMarshalTpl["uint"].tpl.Execute(g.b, struct {
+ Field string
+ IntLen string
+ Key string
+ OmitEmpty string
+ Ptr string
+ }{key, intLen, keyV, omitEmpty, ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) structMarshalFloat(field *ast.Field, keyV string, intLen string, omitEmpty string, ptr bool) {
+ key := field.Names[0].String()
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ err := structMarshalTpl["float"].tpl.Execute(g.b, struct {
+ Field string
+ IntLen string
+ Key string
+ OmitEmpty string
+ Ptr string
+ }{key, intLen, keyV, omitEmpty, ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) structMarshalStruct(field *ast.Field, keyV string, st *ast.TypeSpec, omitEmpty string, ptr bool) {
+ key := field.Names[0].String()
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ var err = structMarshalTpl["struct"].tpl.Execute(g.b, struct {
+ Key string
+ Field string
+ OmitEmpty string
+ Ptr string
+ }{keyV, key, omitEmpty, ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) structMarshalArr(field *ast.Field, keyV string, st *ast.TypeSpec, omitEmpty string, ptr bool) {
+ key := field.Names[0].String()
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ var err = structMarshalTpl["arr"].tpl.Execute(g.b, struct {
+ Key string
+ Field string
+ OmitEmpty string
+ Ptr string
+ }{keyV, key, omitEmpty, ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func (g *Gen) structMarshalAny(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) {
+ key := field.Names[0].String()
+ ptrStr := ""
+ if ptr {
+ ptrStr = "*"
+ }
+ var err = structMarshalTpl["any"].tpl.Execute(g.b, struct {
+ Key string
+ Field string
+ Ptr string
+ }{keyV, key, ptrStr})
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/gojay/gen_struct_marshal_tpl.go b/gojay/gen_struct_marshal_tpl.go
@@ -0,0 +1,42 @@
+package main
+
+var structMarshalTpl = templateList{
+ "def": &genTpl{
+ strTpl: "\n// MarshalJSONObject implements gojay's MarshalerJSONObject" +
+ "\nfunc (v *{{.StructName}}) MarshalJSONObject(enc *gojay.Encoder) {\n",
+ },
+ "isNil": &genTpl{
+ strTpl: `
+// IsNil returns wether the structure is nil value or not
+func (v *{{.StructName}}) IsNil() bool { return v == nil }
+`,
+ },
+ "string": &genTpl{
+ strTpl: "\tenc.StringKey{{.OmitEmpty}}(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n",
+ },
+ "int": &genTpl{
+ strTpl: "\tenc.Int{{.IntLen}}Key{{.OmitEmpty}}(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n",
+ },
+ "uint": &genTpl{
+ strTpl: "\tenc.Uint{{.IntLen}}Key{{.OmitEmpty}}(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n",
+ },
+ "float": &genTpl{
+ strTpl: "\tenc.Float{{.IntLen}}Key{{.OmitEmpty}}(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n",
+ },
+ "bool": &genTpl{
+ strTpl: "\tenc.BoolKey{{.OmitEmpty}}(\"{{.Key}}\", {{.Ptr}}v.{{.Field}})\n",
+ },
+ "struct": &genTpl{
+ strTpl: "\tenc.ObjectKey{{.OmitEmpty}}(\"{{.Key}}\", v.{{.Field}})\n",
+ },
+ "arr": &genTpl{
+ strTpl: "\tenc.ArrayKey{{.OmitEmpty}}(\"{{.Key}}\", v.{{.Field}})\n",
+ },
+ "any": &genTpl{
+ strTpl: "\tenc.AnyKey(\"{{.Key}}\", v.{{.Field}})\n",
+ },
+}
+
+func init() {
+ parseTemplates(structMarshalTpl, "structMarshal")
+}
diff --git a/gojay/gen_struct_test.go b/gojay/gen_struct_test.go
@@ -0,0 +1,433 @@
+package main
+
+import (
+ "io"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGenStruct(t *testing.T) {
+ testCases := map[string]struct {
+ input io.Reader
+ expectedResult string
+ }{
+ "basicStruct": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type Struct struct{
+ Int int
+ Int8 int8
+ Int16 int16
+ Int32 int32
+ Int64 int64
+ Uint8 uint8
+ Uint16 uint16
+ Uint32 uint32
+ Uint64 uint64
+ Float64 float64
+ Float32 float32
+ Str string
+ Bool bool
+ Unknown UnknownType
+}
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ case "int":
+ return dec.Int(&v.Int)
+ case "int8":
+ return dec.Int8(&v.Int8)
+ case "int16":
+ return dec.Int16(&v.Int16)
+ case "int32":
+ return dec.Int32(&v.Int32)
+ case "int64":
+ return dec.Int64(&v.Int64)
+ case "uint8":
+ return dec.Uint8(&v.Uint8)
+ case "uint16":
+ return dec.Uint16(&v.Uint16)
+ case "uint32":
+ return dec.Uint32(&v.Uint32)
+ case "uint64":
+ return dec.Uint64(&v.Uint64)
+ case "float64":
+ return dec.Float64(&v.Float64)
+ case "float32":
+ return dec.Float32(&v.Float32)
+ case "str":
+ return dec.String(&v.Str)
+ case "bool":
+ return dec.Bool(&v.Bool)
+ case "unknown":
+ return dec.Any(&v.Unknown)
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v *Struct) NKeys() int { return 14 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("int", v.Int)
+ enc.Int8Key("int8", v.Int8)
+ enc.Int16Key("int16", v.Int16)
+ enc.Int32Key("int32", v.Int32)
+ enc.Int64Key("int64", v.Int64)
+ enc.Uint8Key("uint8", v.Uint8)
+ enc.Uint16Key("uint16", v.Uint16)
+ enc.Uint32Key("uint32", v.Uint32)
+ enc.Uint64Key("uint64", v.Uint64)
+ enc.Float64Key("float64", v.Float64)
+ enc.Float32Key("float32", v.Float32)
+ enc.StringKey("str", v.Str)
+ enc.BoolKey("bool", v.Bool)
+ enc.AnyKey("unknown", v.Unknown)
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v *Struct) IsNil() bool { return v == nil }
+`,
+ },
+ "basicStructPtr": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type Struct struct{
+ Int *int
+ Str *string
+}
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ case "int":
+ return dec.Int(v.Int)
+ case "str":
+ return dec.String(v.Str)
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v *Struct) NKeys() int { return 2 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("int", *v.Int)
+ enc.StringKey("str", *v.Str)
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v *Struct) IsNil() bool { return v == nil }
+`,
+ },
+ "basicStructTags": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type Struct struct{
+ Int int ` + "`gojay:\"someInt\"`" + `
+ Str string ` + "`gojay:\"someStr\"`" + `
+}
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ case "someInt":
+ return dec.Int(&v.Int)
+ case "someStr":
+ return dec.String(&v.Str)
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v *Struct) NKeys() int { return 2 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("someInt", v.Int)
+ enc.StringKey("someStr", v.Str)
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v *Struct) IsNil() bool { return v == nil }
+`,
+ },
+ "basicStructTagsHideUnmarshal": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type Struct struct{
+ Int int ` + "`gojay:\"-u\"`" + `
+ Str string ` + "`gojay:\"-u\"`" + `
+}
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v *Struct) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("int", v.Int)
+ enc.StringKey("str", v.Str)
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v *Struct) IsNil() bool { return v == nil }
+`,
+ },
+ "basicStructTagsHideUnmarshal2": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type Struct struct{
+ Int int ` + "`gojay:\"someInt,-u\"`" + `
+ Str string ` + "`gojay:\"someStr,-u\"`" + `
+}
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v *Struct) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.IntKey("someInt", v.Int)
+ enc.StringKey("someStr", v.Str)
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v *Struct) IsNil() bool { return v == nil }
+`,
+ },
+ "basicStructTagsHideUnmarshal3": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type Struct struct{
+ Int int ` + "`gojay:\"someInt,-m\"`" + `
+ Str string ` + "`gojay:\"someStr,-m\"`" + `
+}
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ case "someInt":
+ return dec.Int(&v.Int)
+ case "someStr":
+ return dec.String(&v.Str)
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v *Struct) NKeys() int { return 2 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) {
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v *Struct) IsNil() bool { return v == nil }
+`,
+ },
+ "basicStructTagsHideUnmarshal4": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type Struct struct{
+ Int int ` + "`gojay:\"-\"`" + `
+ Str string ` + "`gojay:\"-\"`" + `
+}
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v *Struct) NKeys() int { return 0 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) {
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v *Struct) IsNil() bool { return v == nil }
+`,
+ },
+ "complexStructStructTag": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type Struct struct{
+ Struct Struct ` + "`gojay:\"someStruct\"`" + `
+}
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ case "someStruct":
+ if v.Struct == nil {
+ v.Struct = Struct{}
+ }
+ return dec.Object(v.Struct)
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v *Struct) NKeys() int { return 1 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.ObjectKey("someStruct", v.Struct)
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v *Struct) IsNil() bool { return v == nil }
+`,
+ },
+ "complexStructStructPtrTag": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type Struct struct{
+ Struct *Struct ` + "`gojay:\"someStruct\"`" + `
+}
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ case "someStruct":
+ if v.Struct == nil {
+ v.Struct = &Struct{}
+ }
+ return dec.Object(v.Struct)
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v *Struct) NKeys() int { return 1 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.ObjectKey("someStruct", v.Struct)
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v *Struct) IsNil() bool { return v == nil }
+`,
+ },
+ "complexStructStructPtrTagOmitEmpty": {
+ input: strings.NewReader(`package test
+
+//gojay:json
+type Struct struct{
+ Struct *Struct ` + "`gojay:\"someStruct,omitempty\"`" + `
+}
+ `),
+ expectedResult: `package
+
+import "github.com/francoispqt/gojay"
+
+// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject
+func (v *Struct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
+ switch k {
+ case "someStruct":
+ if v.Struct == nil {
+ v.Struct = &Struct{}
+ }
+ return dec.Object(v.Struct)
+ }
+ return nil
+}
+
+// NKeys returns the number of keys to unmarshal
+func (v *Struct) NKeys() int { return 1 }
+
+// MarshalJSONObject implements gojay's MarshalerJSONObject
+func (v *Struct) MarshalJSONObject(enc *gojay.Encoder) {
+ enc.ObjectKeyOmitEmpty("someStruct", v.Struct)
+}
+
+// IsNil returns wether the structure is nil value or not
+func (v *Struct) IsNil() bool { return v == nil }
+`,
+ },
+ }
+ for n, testCase := range testCases {
+ t.Run(n, func(t *testing.T) {
+ g, err := MakeGenFromReader(testCase.input)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = g.Gen()
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(
+ t,
+ string(genHeader)+testCase.expectedResult,
+ g.b.String(),
+ )
+ })
+ }
+}
diff --git a/gojay/gen_struct_unmarshal.go b/gojay/gen_struct_unmarshal.go
@@ -0,0 +1,378 @@
+package main
+
+import (
+ "fmt"
+ "go/ast"
+ "log"
+)
+
+var structUnmarshalSwitchOpen = []byte("\tswitch k {\n")
+var structUnmarshalClose = []byte("\treturn nil\n}\n")
+
+func (g *Gen) structGenNKeys(n string, count int) error {
+ err := structUnmarshalTpl["nKeys"].tpl.Execute(g.b, struct {
+ NKeys int
+ StructName string
+ }{
+ NKeys: count,
+ StructName: n,
+ })
+ return err
+}
+
+func (g *Gen) structGenUnmarshalObj(n string, s *ast.StructType) (int, error) {
+ err := structUnmarshalTpl["def"].tpl.Execute(g.b, struct {
+ StructName string
+ }{
+ StructName: n,
+ })
+ if err != nil {
+ return 0, err
+ }
+ keys := 0
+ if len(s.Fields.List) > 0 {
+ // open switch statement
+ g.b.Write(structUnmarshalSwitchOpen)
+ // TODO: check tags
+ // check type of field
+ // add accordingly
+ for _, field := range s.Fields.List {
+ // check if has hide tag
+ if field.Tag != nil && hasTagUnmarshalHide(field.Tag) {
+ continue
+ }
+ switch t := field.Type.(type) {
+ case *ast.Ident:
+ var err error
+ keys, err = g.structGenUnmarshalIdent(field, t, keys, false)
+ if err != nil {
+ return 0, err
+ }
+ case *ast.StarExpr:
+ switch ptrExp := t.X.(type) {
+ case *ast.Ident:
+ var err error
+ keys, err = g.structGenUnmarshalIdent(field, ptrExp, keys, true)
+ if err != nil {
+ return 0, err
+ }
+ default:
+ return 0, fmt.Errorf("Unknown type %s", n)
+ }
+ }
+ }
+ // close switch statement
+ g.b.Write([]byte("\t}\n"))
+ }
+ _, err = g.b.Write(structUnmarshalClose)
+ if err != nil {
+ return 0, err
+ }
+ return keys, nil
+}
+
+func (g *Gen) structGenUnmarshalIdent(field *ast.Field, i *ast.Ident, keys int, ptr bool) (int, error) {
+ var keyV = getStructFieldJSONKey(field)
+
+ switch i.String() {
+ case "string":
+ g.structUnmarshalString(field, keyV, ptr)
+ keys++
+ case "bool":
+ g.structUnmarshalBool(field, keyV, ptr)
+ keys++
+ case "int":
+ g.structUnmarshalInt(field, keyV, "", ptr)
+ keys++
+ case "int64":
+ g.structUnmarshalInt(field, keyV, "64", ptr)
+ keys++
+ case "int32":
+ g.structUnmarshalInt(field, keyV, "32", ptr)
+ keys++
+ case "int16":
+ g.structUnmarshalInt(field, keyV, "16", ptr)
+ keys++
+ case "int8":
+ g.structUnmarshalInt(field, keyV, "8", ptr)
+ keys++
+ case "uint64":
+ g.structUnmarshalUint(field, keyV, "64", ptr)
+ keys++
+ case "uint32":
+ g.structUnmarshalUint(field, keyV, "32", ptr)
+ keys++
+ case "uint16":
+ g.structUnmarshalUint(field, keyV, "16", ptr)
+ keys++
+ case "uint8":
+ g.structUnmarshalUint(field, keyV, "8", ptr)
+ keys++
+ case "float64":
+ g.structUnmarshalFloat(field, keyV, "64", ptr)
+ keys++
+ case "float32":
+ g.structUnmarshalFloat(field, keyV, "32", ptr)
+ keys++
+ default:
+ // if ident is already in our spec list
+ if sp, ok := g.genTypes[i.Name]; ok {
+ err := g.structUnmarshalNonPrim(field, keyV, sp, ptr)
+ if err != nil {
+ return 0, err
+ }
+ keys++
+ } else if i.Obj != nil {
+ // else check the obj infos
+ switch t := i.Obj.Decl.(type) {
+ case *ast.TypeSpec:
+ err := g.structUnmarshalNonPrim(field, keyV, t, ptr)
+ if err != nil {
+ return 0, err
+ }
+ keys++
+ default:
+ g.structUnmarshalAny(field, keyV, sp, ptr)
+ keys++
+ }
+ } else {
+ g.structUnmarshalAny(field, keyV, sp, ptr)
+ keys++
+ }
+ }
+ return keys, nil
+}
+
+func (g *Gen) structUnmarshalNonPrim(field *ast.Field, keyV string, sp *ast.TypeSpec, ptr bool) error {
+ switch sp.Type.(type) {
+ case *ast.StructType:
+ g.structUnmarshalStruct(field, keyV, sp, ptr)
+ return nil
+ case *ast.ArrayType:
+ g.structUnmarshalArr(field, keyV, sp, ptr)
+ return nil
+ default:
+ g.structUnmarshalAny(field, keyV, sp, ptr)
+ return nil
+ }
+}
+
+func (g *Gen) structUnmarshalString(field *ast.Field, keyV string, ptr bool) {
+ key := field.Names[0].String()
+ err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct {
+ Key string
+ }{keyV})
+ if err != nil {
+ log.Fatal(err)
+ }
+ if ptr {
+ err = structUnmarshalTpl["string"].tpl.Execute(g.b, struct {
+ Field string
+ Ptr string
+ }{key, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err = structUnmarshalTpl["string"].tpl.Execute(g.b, struct {
+ Field string
+ Ptr string
+ }{key, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) structUnmarshalBool(field *ast.Field, keyV string, ptr bool) {
+ key := field.Names[0].String()
+ err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct {
+ Key string
+ }{keyV})
+ if err != nil {
+ log.Fatal(err)
+ }
+ if ptr {
+ err = structUnmarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Field string
+ Ptr string
+ }{key, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err = structUnmarshalTpl["bool"].tpl.Execute(g.b, struct {
+ Field string
+ Ptr string
+ }{key, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) structUnmarshalInt(field *ast.Field, keyV string, intLen string, ptr bool) {
+ key := field.Names[0].String()
+ err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct {
+ Key string
+ }{keyV})
+ if err != nil {
+ log.Fatal(err)
+ }
+ if ptr {
+ err = structUnmarshalTpl["int"].tpl.Execute(g.b, struct {
+ Field string
+ IntLen string
+ Ptr string
+ }{key, intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err = structUnmarshalTpl["int"].tpl.Execute(g.b, struct {
+ Field string
+ IntLen string
+ Ptr string
+ }{key, intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) structUnmarshalUint(field *ast.Field, keyV string, intLen string, ptr bool) {
+ key := field.Names[0].String()
+ err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct {
+ Key string
+ }{keyV})
+ if err != nil {
+ log.Fatal(err)
+ }
+ if ptr {
+ err = structUnmarshalTpl["uint"].tpl.Execute(g.b, struct {
+ Field string
+ IntLen string
+ Ptr string
+ }{key, intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err = structUnmarshalTpl["uint"].tpl.Execute(g.b, struct {
+ Field string
+ IntLen string
+ Ptr string
+ }{key, intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) structUnmarshalFloat(field *ast.Field, keyV string, intLen string, ptr bool) {
+ key := field.Names[0].String()
+ err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct {
+ Key string
+ }{keyV})
+ if err != nil {
+ log.Fatal(err)
+ }
+ if ptr {
+ err = structUnmarshalTpl["float"].tpl.Execute(g.b, struct {
+ Field string
+ IntLen string
+ Ptr string
+ }{key, intLen, ""})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err = structUnmarshalTpl["float"].tpl.Execute(g.b, struct {
+ Field string
+ IntLen string
+ Ptr string
+ }{key, intLen, "&"})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) structUnmarshalStruct(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) {
+ key := field.Names[0].String()
+ err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct {
+ Key string
+ }{keyV})
+ if err != nil {
+ log.Fatal(err)
+ }
+ if ptr {
+ err = structUnmarshalTpl["structPtr"].tpl.Execute(g.b, struct {
+ Field string
+ StructName string
+ }{key, st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err = structUnmarshalTpl["struct"].tpl.Execute(g.b, struct {
+ Field string
+ StructName string
+ }{key, st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) structUnmarshalArr(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) {
+ key := field.Names[0].String()
+ err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct {
+ Key string
+ }{keyV})
+ if err != nil {
+ log.Fatal(err)
+ }
+ if ptr {
+ err = structUnmarshalTpl["arrPtr"].tpl.Execute(g.b, struct {
+ Field string
+ TypeName string
+ }{key, st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err = structUnmarshalTpl["arr"].tpl.Execute(g.b, struct {
+ Field string
+ TypeName string
+ }{key, st.Name.String()})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func (g *Gen) structUnmarshalAny(field *ast.Field, keyV string, st *ast.TypeSpec, ptr bool) {
+ key := field.Names[0].String()
+ err := structUnmarshalTpl["case"].tpl.Execute(g.b, struct {
+ Key string
+ }{keyV})
+ if err != nil {
+ log.Fatal(err)
+ }
+ if ptr {
+ err = structUnmarshalTpl["anyPtr"].tpl.Execute(g.b, struct {
+ Field string
+ }{key})
+ if err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ err = structUnmarshalTpl["any"].tpl.Execute(g.b, struct {
+ Field string
+ }{key})
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
diff --git a/gojay/gen_struct_unmarshal_tpl.go b/gojay/gen_struct_unmarshal_tpl.go
@@ -0,0 +1,72 @@
+package main
+
+var structUnmarshalTpl = templateList{
+ "def": &genTpl{
+ strTpl: "\n// UnmarshalJSONObject implements gojay's UnmarshalerJSONObject" +
+ "\nfunc (v *{{.StructName}}) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {\n",
+ },
+ "nKeys": &genTpl{
+ strTpl: `
+// NKeys returns the number of keys to unmarshal
+func (v *{{.StructName}}) NKeys() int { return {{.NKeys}} }
+`,
+ },
+ "case": &genTpl{
+ strTpl: "\tcase \"{{.Key}}\":\n",
+ },
+ "string": &genTpl{
+ strTpl: "\t\treturn dec.String({{.Ptr}}v.{{.Field}})\n",
+ },
+ "int": &genTpl{
+ strTpl: "\t\treturn dec.Int{{.IntLen}}({{.Ptr}}v.{{.Field}})\n",
+ },
+ "uint": &genTpl{
+ strTpl: "\t\treturn dec.Uint{{.IntLen}}({{.Ptr}}v.{{.Field}})\n",
+ },
+ "float": &genTpl{
+ strTpl: "\t\treturn dec.Float{{.IntLen}}({{.Ptr}}v.{{.Field}})\n",
+ },
+ "bool": &genTpl{
+ strTpl: "\t\treturn dec.Bool({{.Ptr}}v.{{.Field}})\n",
+ },
+ "struct": &genTpl{
+ strTpl: ` if v.{{.Field}} == nil {
+ v.{{.Field}} = {{.StructName}}{}
+ }
+ return dec.Object(v.{{.Field}})
+`,
+ },
+ "structPtr": &genTpl{
+ strTpl: ` if v.{{.Field}} == nil {
+ v.{{.Field}} = &{{.StructName}}{}
+ }
+ return dec.Object(v.{{.Field}})
+`,
+ },
+ "arr": &genTpl{
+ strTpl: ` if v.{{.Field}} == nil {
+ arr := make({{.TypeName}}, 0)
+ v.{{.Field}} = arr
+ }
+ return dec.Array(&v.{{.Field}})
+`,
+ },
+ "arrPtr": &genTpl{
+ strTpl: ` if v.{{.Field}} == nil {
+ arr := make({{.TypeName}}, 0)
+ v.{{.Field}} = &arr
+ }
+ return dec.Array(v.{{.Field}})
+`,
+ },
+ "any": &genTpl{
+ strTpl: "\t\treturn dec.Any(&v.{{.Field}})\n",
+ },
+ "anyPtr": &genTpl{
+ strTpl: "\t\treturn dec.Any(v.{{.Field}})\n",
+ },
+}
+
+func init() {
+ parseTemplates(structUnmarshalTpl, "structUnmarshal")
+}
diff --git a/gojay/gen_stuct.go b/gojay/gen_stuct.go
@@ -0,0 +1,30 @@
+package main
+
+import (
+ "go/ast"
+ "strings"
+)
+
+func getStructFieldJSONKey(field *ast.Field) string {
+ var keyV string
+ if field.Tag != nil {
+ keyV = tagKeyName(field.Tag)
+ }
+ if keyV == "" {
+ keyV = strings.ToLower(field.Names[0].String()[:1]) + field.Names[0].String()[1:]
+ }
+ return keyV
+}
+
+func (g *Gen) genStruct(n string, s *ast.StructType) error {
+ keys, err := g.structGenUnmarshalObj(n, s)
+ if err != nil {
+ return err
+ }
+ err = g.structGenNKeys(n, keys)
+ keys, err = g.structGenMarshalObj(n, s)
+ if err != nil {
+ return err
+ }
+ return g.structGenIsNil(n)
+}
diff --git a/gojay/gen_tag.go b/gojay/gen_tag.go
@@ -0,0 +1,65 @@
+package main
+
+import (
+ "go/ast"
+ "log"
+
+ "github.com/fatih/structtag"
+)
+
+const gojayTag = "gojay"
+const hideTag = "-"
+const unmarshalHideTag = "-u"
+const marshalHideTag = "-m"
+const omitEmptyTag = "omitempty"
+
+func getGojayTagValue(tags *ast.BasicLit) (*structtag.Tag, error) {
+ t, err := structtag.Parse(tags.Value[1 : len(tags.Value)-1])
+ if err != nil {
+ return nil, err
+ }
+ v, err := t.Get(gojayTag)
+ if err != nil {
+ return nil, err
+ }
+ return v, nil
+}
+
+func hasTagUnmarshalHide(tags *ast.BasicLit) bool {
+ v, err := getGojayTagValue(tags)
+ if err != nil {
+ log.Print(err)
+ return false
+ }
+ return (v.Name == unmarshalHideTag || v.Name == hideTag) || v.HasOption(unmarshalHideTag)
+}
+
+func hasTagMarshalHide(tags *ast.BasicLit) bool {
+ v, err := getGojayTagValue(tags)
+ if err != nil {
+ log.Print(err)
+ return false
+ }
+ return (v.Name == marshalHideTag || v.Name == hideTag) || v.HasOption(marshalHideTag)
+}
+
+func hasTagOmitEmpty(tags *ast.BasicLit) bool {
+ v, err := getGojayTagValue(tags)
+ if err != nil {
+ log.Print(err)
+ return false
+ }
+ return v.Name == omitEmptyTag || v.HasOption(omitEmptyTag)
+}
+
+func tagKeyName(tags *ast.BasicLit) string {
+ v, err := getGojayTagValue(tags)
+ if err != nil {
+ log.Print(err)
+ return ""
+ }
+ if v.Name == hideTag || v.Name == unmarshalHideTag || v.Name == marshalHideTag {
+ return ""
+ }
+ return v.Name
+}
diff --git a/gojay/gen_test.go b/gojay/gen_test.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "io"
+)
+
+func MakeGenFromReader(input io.Reader) (*Gen, error) {
+ g := NewGen("", []string{})
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "", input, parser.ParseComments)
+ if err != nil {
+ return nil, err
+ }
+ v := newVisitor(g, g.pkg)
+ ast.Walk(v, f)
+ if err != nil {
+ return nil, err
+ }
+ g.vis = v
+ return g, nil
+}
diff --git a/gojay/main.go b/gojay/main.go
@@ -0,0 +1,116 @@
+//+build !test
+
+package main
+
+import (
+ "errors"
+ "flag"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+var dst = flag.String("o", "", "destination file to output generated implementations")
+var src = flag.String("s", "", "source dir or file (absolute or relative path)")
+var pkg = flag.String("p", "", "go package")
+var types = flag.String("t", "", "types to generate")
+
+var ErrNoPathProvided = errors.New("You must provide a path or a package name")
+
+type stringWriter interface {
+ WriteString(string) (int, error)
+}
+
+func hasAnnotation(fP string) bool {
+ b, err := ioutil.ReadFile(fP)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return strings.Contains(string(b), gojayAnnotation)
+}
+
+func resolvePath(p string) (string, error) {
+ if fullPath, err := filepath.Abs(p); err != nil {
+ return "", err
+ } else if _, err := os.Stat(fullPath); err != nil {
+ return "", err
+ } else {
+ return fullPath, nil
+ }
+}
+
+// getPath returns either the path given as argument or current working directory
+func getPath() (string, error) {
+ // if pkg is set, resolve pkg path
+ if *pkg != "" {
+ return resolvePath(os.Getenv("GOPATH") + "/src/" + *pkg)
+ } else if *src != "" { // if src is present parse from src
+ return resolvePath(*src)
+ } else if len(os.Args) > 1 { // else if there is a command line arg, use it as path to a package $GOPATH/src/os.Args[1]
+ return resolvePath(os.Getenv("GOPATH") + "/src/" + os.Args[1])
+ }
+ return "", ErrNoPathProvided
+}
+
+// getTypes returns the types to be parsed
+func getTypes() (t []string) {
+ if *types != "" { // if src is present parse from src
+ return strings.Split(*types, ",")
+ } else if *src == "." && *dst == "" && len(os.Args) > 2 { // else if there is a command line arg, use it as path to a package $GOPATH/src/os.Args[1]
+ return strings.Split(os.Args[2], ",")
+ }
+ return t
+}
+
+// getOutput returns the output
+func getOutput() (stringWriter, error) {
+ if *dst != "" {
+ p, err := filepath.Abs(*dst)
+ if err != nil {
+ return nil, err
+ }
+ return os.OpenFile(p, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
+ } else if len(os.Args) > 3 && *src == "" && *types == "" {
+ p, err := filepath.Abs(os.Args[3])
+ if err != nil {
+ return nil, err
+ }
+ return os.OpenFile(p, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
+ }
+ return os.Stdout, nil
+}
+
+func parseArgs() (p string, t []string, o stringWriter, err error) {
+ flag.Parse()
+ p, err = getPath()
+ if err != nil {
+ return p, t, o, err
+ }
+ t = getTypes()
+ o, err = getOutput()
+ return p, t, o, err
+}
+
+func main() {
+ p, t, o, err := parseArgs()
+ if err != nil {
+ log.Fatal(err)
+ }
+ // parse source files
+ g := NewGen(p, t)
+ err = g.parse()
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
+ // generate output
+ err = g.Gen()
+ if err != nil {
+ log.Fatal(err)
+ return
+ }
+ // write content to output
+ o.WriteString(g.b.String())
+}
diff --git a/gojay/tests/maps.go b/gojay/tests/maps.go
@@ -0,0 +1,4 @@
+package tests
+
+//gojay:json
+type MapStringInt map[string]int
diff --git a/gojay/tests/slices.go b/gojay/tests/slices.go
@@ -0,0 +1,16 @@
+package tests
+
+//gojay:json
+type StrSlice []string
+
+//gojay:json
+type IntSlice []int
+
+//gojay:json
+type BoolSlice []bool
+
+//gojay:json
+type StructSlice []*A
+
+//gojay:json
+type SliceSlice []*StrSlice
diff --git a/gojay/tests/structs.go b/gojay/tests/structs.go
@@ -0,0 +1,44 @@
+package tests
+
+//gojay:json
+type A struct {
+ Str string `gojay:"string"`
+ Bool bool
+ Int int
+ Int64 int64
+ Int32 int32
+ Int16 int16
+ Int8 int8
+ Uint64 uint64
+ Uint32 uint32
+ Uint16 uint16
+ Uint8 uint8
+ Bval *B
+ Arrval *StrSlice
+}
+
+//gojay:json
+type B struct {
+ Str string
+ Bool bool
+ Int int
+ Int64 int64
+ Int32 int32
+ Int16 int16
+ Int8 int8
+ Uint64 uint64
+ Uint32 uint32
+ Uint16 uint16
+ Uint8 uint8
+ StrPtr *string
+ BoolPtr *bool
+ IntPtr *int
+ Int64Ptr *int64
+ Int32Ptr *int32
+ Int16Ptr *int16
+ Int8Ptr *int8
+ Uint64Ptr *uint64
+ Uint32Ptr *uint32
+ Uint16Ptr *uint16
+ Uint8PTr *uint8
+}
diff --git a/gojay/vendor/github.com/fatih/structtag/.travis.yml b/gojay/vendor/github.com/fatih/structtag/.travis.yml
@@ -0,0 +1,4 @@
+language: go
+go:
+ - 1.7.x
+ - tip
diff --git a/gojay/vendor/github.com/fatih/structtag/LICENSE b/gojay/vendor/github.com/fatih/structtag/LICENSE
@@ -0,0 +1,60 @@
+Copyright (c) 2017, Fatih Arslan
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of structtag nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+This software includes some portions from Go. Go is used under the terms of the
+BSD like license.
+
+Copyright (c) 2012 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The Go gopher was designed by Renee French. http://reneefrench.blogspot.com/ The design is licensed under the Creative Commons 3.0 Attributions license. Read this article for more details: https://blog.golang.org/gopher
diff --git a/gojay/vendor/github.com/fatih/structtag/README.md b/gojay/vendor/github.com/fatih/structtag/README.md
@@ -0,0 +1,73 @@
+# structtag [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/structtag) [![Build Status](https://travis-ci.org/fatih/structtag.svg?branch=master)](https://travis-ci.org/fatih/structtag)
+
+structtag provides an easy way of parsing and manipulating struct tag fields.
+Please vendor the library as it might change in future versions.
+
+# Install
+
+```bash
+go get github.com/fatih/structtag
+```
+
+# Example
+
+```go
+package main
+
+import (
+ "fmt"
+ "reflect"
+ "sort"
+
+ "github.com/fatih/structtag"
+)
+
+func main() {
+ type t struct {
+ t string `json:"foo,omitempty,string" xml:"foo"`
+ }
+
+ // get field tag
+ tag := reflect.TypeOf(t{}).Field(0).Tag
+
+ // ... and start using structtag by parsing the tag
+ tags, err := structtag.Parse(string(tag))
+ if err != nil {
+ panic(err)
+ }
+
+ // iterate over all tags
+ for _, t := range tags.Tags() {
+ fmt.Printf("tag: %+v\n", t)
+ }
+
+ // get a single tag
+ jsonTag, err := tags.Get("json")
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println(jsonTag) // Output: json:"foo,omitempty,string"
+ fmt.Println(jsonTag.Key) // Output: json
+ fmt.Println(jsonTag.Name) // Output: foo
+ fmt.Println(jsonTag.Options) // Output: [omitempty string]
+
+ // change existing tag
+ jsonTag.Name = "foo_bar"
+ jsonTag.Options = nil
+ tags.Set(jsonTag)
+
+ // add new tag
+ tags.Set(&structtag.Tag{
+ Key: "hcl",
+ Name: "foo",
+ Options: []string{"squash"},
+ })
+
+ // print the tags
+ fmt.Println(tags) // Output: json:"foo_bar" xml:"foo" hcl:"foo,squash"
+
+ // sort tags according to keys
+ sort.Sort(tags)
+ fmt.Println(tags) // Output: hcl:"foo,squash" json:"foo_bar" xml:"foo"
+}
+```
diff --git a/gojay/vendor/github.com/fatih/structtag/tags.go b/gojay/vendor/github.com/fatih/structtag/tags.go
@@ -0,0 +1,303 @@
+package structtag
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+var (
+ errTagSyntax = errors.New("bad syntax for struct tag pair")
+ errTagKeySyntax = errors.New("bad syntax for struct tag key")
+ errTagValueSyntax = errors.New("bad syntax for struct tag value")
+
+ errKeyNotSet = errors.New("tag key does not exist")
+ errTagNotExist = errors.New("tag does not exist")
+ errTagKeyMismatch = errors.New("mismatch between key and tag.key")
+)
+
+// Tags represent a set of tags from a single struct field
+type Tags struct {
+ tags []*Tag
+}
+
+// Tag defines a single struct's string literal tag
+type Tag struct {
+ // Key is the tag key, such as json, xml, etc..
+ // i.e: `json:"foo,omitempty". Here key is: "json"
+ Key string
+
+ // Name is a part of the value
+ // i.e: `json:"foo,omitempty". Here name is: "foo"
+ Name string
+
+ // Options is a part of the value. It contains a slice of tag options i.e:
+ // `json:"foo,omitempty". Here options is: ["omitempty"]
+ Options []string
+}
+
+// Parse parses a single struct field tag and returns the set of tags.
+func Parse(tag string) (*Tags, error) {
+ var tags []*Tag
+
+ // NOTE(arslan) following code is from reflect and vet package with some
+ // modifications to collect all necessary information and extend it with
+ // usable methods
+ for tag != "" {
+ // Skip leading space.
+ i := 0
+ for i < len(tag) && tag[i] == ' ' {
+ i++
+ }
+ tag = tag[i:]
+ if tag == "" {
+ return nil, nil
+ }
+
+ // Scan to colon. A space, a quote or a control character is a syntax
+ // error. Strictly speaking, control chars include the range [0x7f,
+ // 0x9f], not just [0x00, 0x1f], but in practice, we ignore the
+ // multi-byte control characters as it is simpler to inspect the tag's
+ // bytes than the tag's runes.
+ i = 0
+ for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
+ i++
+ }
+
+ if i == 0 {
+ return nil, errTagKeySyntax
+ }
+ if i+1 >= len(tag) || tag[i] != ':' {
+ return nil, errTagSyntax
+ }
+ if tag[i+1] != '"' {
+ return nil, errTagValueSyntax
+ }
+
+ key := string(tag[:i])
+ tag = tag[i+1:]
+
+ // Scan quoted string to find value.
+ i = 1
+ for i < len(tag) && tag[i] != '"' {
+ if tag[i] == '\\' {
+ i++
+ }
+ i++
+ }
+ if i >= len(tag) {
+ return nil, errTagValueSyntax
+ }
+
+ qvalue := string(tag[:i+1])
+ tag = tag[i+1:]
+
+ value, err := strconv.Unquote(qvalue)
+ if err != nil {
+ return nil, errTagValueSyntax
+ }
+
+ res := strings.Split(value, ",")
+ name := res[0]
+ options := res[1:]
+ if len(options) == 0 {
+ options = nil
+ }
+
+ tags = append(tags, &Tag{
+ Key: key,
+ Name: name,
+ Options: options,
+ })
+ }
+
+ return &Tags{
+ tags: tags,
+ }, nil
+}
+
+// Get returns the tag associated with the given key. If the key is present
+// in the tag the value (which may be empty) is returned. Otherwise the
+// returned value will be the empty string. The ok return value reports whether
+// the tag exists or not (which the return value is nil).
+func (t *Tags) Get(key string) (*Tag, error) {
+ for _, tag := range t.tags {
+ if tag.Key == key {
+ return tag, nil
+ }
+ }
+
+ return nil, errTagNotExist
+}
+
+// Set sets the given tag. If the tag key already exists it'll override it
+func (t *Tags) Set(tag *Tag) error {
+ if tag.Key == "" {
+ return errKeyNotSet
+ }
+
+ added := false
+ for i, tg := range t.tags {
+ if tg.Key == tag.Key {
+ added = true
+ t.tags[i] = tag
+ }
+ }
+
+ if !added {
+ // this means this is a new tag, add it
+ t.tags = append(t.tags, tag)
+ }
+
+ return nil
+}
+
+// AddOptions adds the given option for the given key. If the option already
+// exists it doesn't add it again.
+func (t *Tags) AddOptions(key string, options ...string) {
+ for i, tag := range t.tags {
+ if tag.Key != key {
+ continue
+ }
+
+ for _, opt := range options {
+ if !tag.HasOption(opt) {
+ tag.Options = append(tag.Options, opt)
+ }
+ }
+
+ t.tags[i] = tag
+ }
+}
+
+// DeleteOptions deletes the given options for the given key
+func (t *Tags) DeleteOptions(key string, options ...string) {
+ hasOption := func(option string) bool {
+ for _, opt := range options {
+ if opt == option {
+ return true
+ }
+ }
+ return false
+ }
+
+ for i, tag := range t.tags {
+ if tag.Key != key {
+ continue
+ }
+
+ var updated []string
+ for _, opt := range tag.Options {
+ if !hasOption(opt) {
+ updated = append(updated, opt)
+ }
+ }
+
+ tag.Options = updated
+ t.tags[i] = tag
+ }
+}
+
+// Delete deletes the tag for the given keys
+func (t *Tags) Delete(keys ...string) {
+ hasKey := func(key string) bool {
+ for _, k := range keys {
+ if k == key {
+ return true
+ }
+ }
+ return false
+ }
+
+ var updated []*Tag
+ for _, tag := range t.tags {
+ if !hasKey(tag.Key) {
+ updated = append(updated, tag)
+ }
+ }
+
+ t.tags = updated
+}
+
+// Tags returns a slice of tags. The order is the original tag order unless it
+// was changed.
+func (t *Tags) Tags() []*Tag {
+ return t.tags
+}
+
+// Tags returns a slice of tags. The order is the original tag order unless it
+// was changed.
+func (t *Tags) Keys() []string {
+ var keys []string
+ for _, tag := range t.tags {
+ keys = append(keys, tag.Key)
+ }
+ return keys
+}
+
+// String reassembles the tags into a valid literal tag field representation
+func (t *Tags) String() string {
+ tags := t.Tags()
+ if len(tags) == 0 {
+ return ""
+ }
+
+ var buf bytes.Buffer
+ for i, tag := range t.Tags() {
+ buf.WriteString(tag.String())
+ if i != len(tags)-1 {
+ buf.WriteString(" ")
+ }
+ }
+ return buf.String()
+}
+
+// HasOption returns true if the given option is available in options
+func (t *Tag) HasOption(opt string) bool {
+ for _, tagOpt := range t.Options {
+ if tagOpt == opt {
+ return true
+ }
+ }
+
+ return false
+}
+
+// String reassembles the tag into a valid tag field representation
+func (t *Tag) String() string {
+ options := strings.Join(t.Options, ",")
+ if options != "" {
+ return fmt.Sprintf(`%s:"%s,%s"`, t.Key, t.Name, options)
+ }
+ return fmt.Sprintf(`%s:"%s"`, t.Key, t.Name)
+}
+
+// GoString implements the fmt.GoStringer interface
+func (t *Tag) GoString() string {
+ template := `{
+ Key: '%s',
+ Name: '%s',
+ Option: '%s',
+ }`
+
+ if t.Options == nil {
+ return fmt.Sprintf(template, t.Key, t.Name, "nil")
+ }
+
+ options := strings.Join(t.Options, ",")
+ return fmt.Sprintf(template, t.Key, t.Name, options)
+}
+
+func (t *Tags) Len() int {
+ return len(t.tags)
+}
+
+func (t *Tags) Less(i int, j int) bool {
+ return t.tags[i].Key < t.tags[j].Key
+}
+
+func (t *Tags) Swap(i int, j int) {
+ t.tags[i], t.tags[j] = t.tags[j], t.tags[i]
+}
diff --git a/gojay/vendor/github.com/fatih/structtag/tags_test.go b/gojay/vendor/github.com/fatih/structtag/tags_test.go
@@ -0,0 +1,390 @@
+package structtag
+
+import (
+ "reflect"
+ "sort"
+ "testing"
+)
+
+func TestParse(t *testing.T) {
+ test := []struct {
+ name string
+ tag string
+ exp []*Tag
+ invalid bool
+ }{
+ {
+ name: "empty tag",
+ tag: "",
+ },
+ {
+ name: "tag with one key (invalid)",
+ tag: "json",
+ invalid: true,
+ },
+ {
+ name: "tag with one key (valid)",
+ tag: `json:""`,
+ exp: []*Tag{
+ {
+ Key: "json",
+ },
+ },
+ },
+ {
+ name: "tag with one key and dash name",
+ tag: `json:"-"`,
+ exp: []*Tag{
+ {
+ Key: "json",
+ Name: "-",
+ },
+ },
+ },
+ {
+ name: "tag with key and name",
+ tag: `json:"foo"`,
+ exp: []*Tag{
+ {
+ Key: "json",
+ Name: "foo",
+ },
+ },
+ },
+ {
+ name: "tag with key, name and option",
+ tag: `json:"foo,omitempty"`,
+ exp: []*Tag{
+ {
+ Key: "json",
+ Name: "foo",
+ Options: []string{"omitempty"},
+ },
+ },
+ },
+ {
+ name: "tag with multiple keys",
+ tag: `json:"" hcl:""`,
+ exp: []*Tag{
+ {
+ Key: "json",
+ },
+ {
+ Key: "hcl",
+ },
+ },
+ },
+ {
+ name: "tag with multiple keys and names",
+ tag: `json:"foo" hcl:"foo"`,
+ exp: []*Tag{
+ {
+ Key: "json",
+ Name: "foo",
+ },
+ {
+ Key: "hcl",
+ Name: "foo",
+ },
+ },
+ },
+ {
+ name: "tag with multiple keys and names",
+ tag: `json:"foo" hcl:"foo"`,
+ exp: []*Tag{
+ {
+ Key: "json",
+ Name: "foo",
+ },
+ {
+ Key: "hcl",
+ Name: "foo",
+ },
+ },
+ },
+ {
+ name: "tag with multiple keys and different names",
+ tag: `json:"foo" hcl:"bar"`,
+ exp: []*Tag{
+ {
+ Key: "json",
+ Name: "foo",
+ },
+ {
+ Key: "hcl",
+ Name: "bar",
+ },
+ },
+ },
+ {
+ name: "tag with multiple keys, different names and options",
+ tag: `json:"foo,omitempty" structs:"bar,omitnested"`,
+ exp: []*Tag{
+ {
+ Key: "json",
+ Name: "foo",
+ Options: []string{"omitempty"},
+ },
+ {
+ Key: "structs",
+ Name: "bar",
+ Options: []string{"omitnested"},
+ },
+ },
+ },
+ {
+ name: "tag with multiple keys, different names and options",
+ tag: `json:"foo" structs:"bar,omitnested" hcl:"-"`,
+ exp: []*Tag{
+ {
+ Key: "json",
+ Name: "foo",
+ },
+ {
+ Key: "structs",
+ Name: "bar",
+ Options: []string{"omitnested"},
+ },
+ {
+ Key: "hcl",
+ Name: "-",
+ },
+ },
+ },
+ }
+
+ for _, ts := range test {
+ t.Run(ts.name, func(t *testing.T) {
+ tags, err := Parse(ts.tag)
+ invalid := err != nil
+
+ if invalid != ts.invalid {
+ t.Errorf("invalid case\n\twant: %+v\n\tgot : %+v\n\terr : %s", ts.invalid, invalid, err)
+ }
+
+ if invalid {
+ return
+ }
+
+ got := tags.Tags()
+
+ if !reflect.DeepEqual(ts.exp, got) {
+ t.Errorf("parse\n\twant: %#v\n\tgot : %#v", ts.exp, got)
+ }
+ })
+ }
+}
+
+func TestTags_Get(t *testing.T) {
+ tag := `json:"foo,omitempty" structs:"bar,omitnested"`
+
+ tags, err := Parse(tag)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ found, err := tags.Get("json")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want := `json:"foo,omitempty"`
+
+ if found.String() != want {
+ t.Errorf("get\n\twant: %#v\n\tgot : %#v", want, found.String())
+ }
+}
+
+func TestTags_Set(t *testing.T) {
+ tag := `json:"foo,omitempty" structs:"bar,omitnested"`
+
+ tags, err := Parse(tag)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = tags.Set(&Tag{
+ Key: "json",
+ Name: "bar",
+ Options: []string{},
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ found, err := tags.Get("json")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want := `json:"bar"`
+ if found.String() != want {
+ t.Errorf("set\n\twant: %#v\n\tgot : %#v", want, found.String())
+ }
+}
+
+func TestTags_Set_Append(t *testing.T) {
+ tag := `json:"foo,omitempty"`
+
+ tags, err := Parse(tag)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = tags.Set(&Tag{
+ Key: "structs",
+ Name: "bar",
+ Options: []string{"omitnested"},
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ found, err := tags.Get("structs")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want := `structs:"bar,omitnested"`
+ if found.String() != want {
+ t.Errorf("set append\n\twant: %#v\n\tgot : %#v", want, found.String())
+ }
+
+ wantFull := `json:"foo,omitempty" structs:"bar,omitnested"`
+ if tags.String() != wantFull {
+ t.Errorf("set append\n\twant: %#v\n\tgot : %#v", wantFull, tags.String())
+ }
+}
+
+func TestTags_Set_KeyDoesNotExist(t *testing.T) {
+ tag := `json:"foo,omitempty" structs:"bar,omitnested"`
+
+ tags, err := Parse(tag)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = tags.Set(&Tag{
+ Key: "",
+ Name: "bar",
+ Options: []string{},
+ })
+ if err == nil {
+ t.Fatal("setting tag with a nonexisting key should error")
+ }
+
+ if err != errKeyNotSet {
+ t.Errorf("set\n\twant: %#v\n\tgot : %#v", errTagKeyMismatch, err)
+ }
+}
+
+func TestTags_Delete(t *testing.T) {
+ tag := `json:"foo,omitempty" structs:"bar,omitnested" hcl:"-"`
+
+ tags, err := Parse(tag)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tags.Delete("structs")
+ if tags.Len() != 2 {
+ t.Fatalf("tag length should be 2, have %d", tags.Len())
+ }
+
+ found, err := tags.Get("json")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want := `json:"foo,omitempty"`
+ if found.String() != want {
+ t.Errorf("delete\n\twant: %#v\n\tgot : %#v", want, found.String())
+ }
+
+ wantFull := `json:"foo,omitempty" hcl:"-"`
+ if tags.String() != wantFull {
+ t.Errorf("delete\n\twant: %#v\n\tgot : %#v", wantFull, tags.String())
+ }
+}
+
+func TestTags_DeleteOptions(t *testing.T) {
+ tag := `json:"foo,omitempty" structs:"bar,omitnested,omitempty" hcl:"-"`
+
+ tags, err := Parse(tag)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tags.DeleteOptions("json", "omitempty")
+
+ want := `json:"foo" structs:"bar,omitnested,omitempty" hcl:"-"`
+ if tags.String() != want {
+ t.Errorf("delete option\n\twant: %#v\n\tgot : %#v", want, tags.String())
+ }
+
+ tags.DeleteOptions("structs", "omitnested")
+ want = `json:"foo" structs:"bar,omitempty" hcl:"-"`
+ if tags.String() != want {
+ t.Errorf("delete option\n\twant: %#v\n\tgot : %#v", want, tags.String())
+ }
+}
+
+func TestTags_AddOption(t *testing.T) {
+ tag := `json:"foo" structs:"bar,omitempty" hcl:"-"`
+
+ tags, err := Parse(tag)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tags.AddOptions("json", "omitempty")
+
+ want := `json:"foo,omitempty" structs:"bar,omitempty" hcl:"-"`
+ if tags.String() != want {
+ t.Errorf("add options\n\twant: %#v\n\tgot : %#v", want, tags.String())
+ }
+
+ // this shouldn't change anything
+ tags.AddOptions("structs", "omitempty")
+
+ want = `json:"foo,omitempty" structs:"bar,omitempty" hcl:"-"`
+ if tags.String() != want {
+ t.Errorf("add options\n\twant: %#v\n\tgot : %#v", want, tags.String())
+ }
+
+ // this should append to the existing
+ tags.AddOptions("structs", "omitnested", "flatten")
+ want = `json:"foo,omitempty" structs:"bar,omitempty,omitnested,flatten" hcl:"-"`
+ if tags.String() != want {
+ t.Errorf("add options\n\twant: %#v\n\tgot : %#v", want, tags.String())
+ }
+}
+
+func TestTags_String(t *testing.T) {
+ tag := `json:"foo" structs:"bar,omitnested" hcl:"-"`
+
+ tags, err := Parse(tag)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if tags.String() != tag {
+ t.Errorf("string\n\twant: %#v\n\tgot : %#v", tag, tags.String())
+ }
+}
+
+func TestTags_Sort(t *testing.T) {
+ tag := `json:"foo" structs:"bar,omitnested" hcl:"-"`
+
+ tags, err := Parse(tag)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ sort.Sort(tags)
+
+ want := `hcl:"-" json:"foo" structs:"bar,omitnested"`
+ if tags.String() != want {
+ t.Errorf("string\n\twant: %#v\n\tgot : %#v", want, tags.String())
+ }
+}
diff --git a/gojay/visitor.go b/gojay/visitor.go
@@ -0,0 +1,54 @@
+package main
+
+import (
+ "go/ast"
+ "strings"
+)
+
+func docContains(n *ast.CommentGroup, s string) bool {
+ for _, d := range n.List {
+ if strings.Contains(d.Text, s) {
+ return true
+ }
+ }
+ return false
+}
+
+type vis struct {
+ pkg string
+ g *Gen
+ commentFound bool
+}
+
+func (v *vis) Visit(n ast.Node) (w ast.Visitor) {
+ switch n := n.(type) {
+ case *ast.Package:
+ v.commentFound = false
+ return v
+ case *ast.File:
+ v.commentFound = false
+ return v
+ case *ast.GenDecl:
+ if len(v.g.types) == 0 && n.Doc != nil {
+ v.commentFound = docContains(n.Doc, gojayAnnotation)
+ }
+ return v
+ case *ast.TypeSpec:
+ if v.commentFound || v.g.isGenType(n.Name.Name) {
+ v.g.genTypes[n.Name.Name] = n
+ }
+ v.commentFound = false
+ return v
+ case *ast.StructType:
+ v.commentFound = false
+ return v
+ }
+ return v
+}
+
+func newVisitor(g *Gen, pkgName string) *vis {
+ return &vis{
+ g: g,
+ pkg: pkgName,
+ }
+}