gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit 75dba3be66f280626f447c63e65179417b651429
parent 75fff352c961091226ab3c6876e46631ecd63e53
Author: francoispqt <francois@parquet.ninja>
Date:   Sun, 12 Aug 2018 00:41:03 +0800

sqlnull types

Diffstat:
Mdecode_sqlnull_test.go | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Aencode_sqlnull.go | 269+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aencode_sqlnull_test.go | 1121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1542 insertions(+), 4 deletions(-)

diff --git a/decode_sqlnull_test.go b/decode_sqlnull_test.go @@ -13,21 +13,47 @@ func TestDecodeSQLNullString(t *testing.T) { name string json string expectedNullString sql.NullString + err bool }{ { name: "basic", json: `"test"`, expectedNullString: sql.NullString{String: "test", Valid: true}, }, + { + name: "basic", + json: `"test`, + expectedNullString: sql.NullString{String: "test", Valid: true}, + err: true, + }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { nullString := sql.NullString{} dec := NewDecoder(strings.NewReader(testCase.json)) - dec.DecodeSQLNullString(&nullString) - assert.Equal(t, testCase.expectedNullString, nullString) + err := dec.DecodeSQLNullString(&nullString) + if testCase.err { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + assert.Equal(t, testCase.expectedNullString, nullString) + } }) } + t.Run( + "should panic because decoder is pooled", + func(t *testing.T) { + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnt be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeSQLNullString(&sql.NullString{}) + assert.True(t, false, "should not be called as decoder should have panicked") + }, + ) } func TestDecodeSQLNullInt64(t *testing.T) { @@ -35,19 +61,141 @@ func TestDecodeSQLNullInt64(t *testing.T) { name string json string expectedNullInt64 sql.NullInt64 + err bool }{ { name: "basic", json: `1`, expectedNullInt64: sql.NullInt64{Int64: 1, Valid: true}, }, + { + name: "basic", + json: `"test`, + expectedNullInt64: sql.NullInt64{Int64: 1, Valid: true}, + err: true, + }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { nullInt64 := sql.NullInt64{} dec := NewDecoder(strings.NewReader(testCase.json)) - dec.DecodeSQLNullInt64(&nullInt64) - assert.Equal(t, testCase.expectedNullInt64, nullInt64) + err := dec.DecodeSQLNullInt64(&nullInt64) + if testCase.err { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + assert.Equal(t, testCase.expectedNullInt64, nullInt64) + } + }) + } + t.Run( + "should panic because decoder is pooled", + func(t *testing.T) { + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnt be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeSQLNullInt64(&sql.NullInt64{}) + assert.True(t, false, "should not be called as decoder should have panicked") + }, + ) +} + +func TestDecodeSQLNullFloat64(t *testing.T) { + testCases := []struct { + name string + json string + expectedNullFloat64 sql.NullFloat64 + err bool + }{ + { + name: "basic", + json: `1`, + expectedNullFloat64: sql.NullFloat64{Float64: 1, Valid: true}, + }, + { + name: "basic", + json: `"test`, + expectedNullFloat64: sql.NullFloat64{Float64: 1, Valid: true}, + err: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + nullFloat64 := sql.NullFloat64{} + dec := NewDecoder(strings.NewReader(testCase.json)) + err := dec.DecodeSQLNullFloat64(&nullFloat64) + if testCase.err { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + assert.Equal(t, testCase.expectedNullFloat64, nullFloat64) + } + }) + } + t.Run( + "should panic because decoder is pooled", + func(t *testing.T) { + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnt be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeSQLNullFloat64(&sql.NullFloat64{}) + assert.True(t, false, "should not be called as decoder should have panicked") + }, + ) +} + +func TestDecodeSQLNullBool(t *testing.T) { + testCases := []struct { + name string + json string + expectedNullBool sql.NullBool + err bool + }{ + { + name: "basic", + json: `true`, + expectedNullBool: sql.NullBool{Bool: true, Valid: true}, + }, + { + name: "basic", + json: `"&`, + expectedNullBool: sql.NullBool{Bool: true, Valid: true}, + err: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + nullBool := sql.NullBool{} + dec := NewDecoder(strings.NewReader(testCase.json)) + err := dec.DecodeSQLNullBool(&nullBool) + if testCase.err { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + assert.Equal(t, testCase.expectedNullBool, nullBool) + } }) } + t.Run( + "should panic because decoder is pooled", + func(t *testing.T) { + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnt be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeSQLNullBool(&sql.NullBool{}) + assert.True(t, false, "should not be called as decoder should have panicked") + }, + ) } diff --git a/encode_sqlnull.go b/encode_sqlnull.go @@ -0,0 +1,269 @@ +package gojay + +import "database/sql" + +// EncodeSQLNullString encodes a string to +func (enc *Encoder) EncodeSQLNullString(v *sql.NullString) error { + if enc.isPooled == 1 { + panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) + } + _, _ = enc.encodeString(v.String) + _, err := enc.Write() + if err != nil { + enc.err = err + return err + } + return nil +} + +// AddSQLNullString adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddSQLNullString(v *sql.NullString) { + enc.String(v.String) +} + +// AddSQLNullStringOmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddSQLNullStringOmitEmpty(v *sql.NullString) { + if v != nil && v.Valid && v.String != "" { + enc.StringOmitEmpty(v.String) + } +} + +// AddSQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) AddSQLNullStringKey(key string, v *sql.NullString) { + enc.StringKey(key, v.String) +} + +// AddSQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) AddSQLNullStringKeyOmitEmpty(key string, v *sql.NullString) { + if v != nil && v.Valid && v.String != "" { + enc.StringKeyOmitEmpty(key, v.String) + } +} + +// SQLNullString adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullString(v *sql.NullString) { + enc.String(v.String) +} + +// SQLNullStringOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullStringOmitEmpty(v *sql.NullString) { + if v != nil && v.Valid && v.String != "" { + enc.String(v.String) + } +} + +// SQLNullStringKey adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullStringKey(key string, v *sql.NullString) { + enc.StringKey(key, v.String) +} + +// SQLNullStringKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullStringKeyOmitEmpty(key string, v *sql.NullString) { + if v != nil && v.Valid && v.String != "" { + enc.StringKeyOmitEmpty(key, v.String) + } +} + +// NullInt64 + +// EncodeSQLNullInt64 encodes a string to +func (enc *Encoder) EncodeSQLNullInt64(v *sql.NullInt64) error { + if enc.isPooled == 1 { + panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) + } + _, _ = enc.encodeInt64(v.Int64) + _, err := enc.Write() + if err != nil { + enc.err = err + return err + } + return nil +} + +// AddSQLNullInt64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddSQLNullInt64(v *sql.NullInt64) { + enc.Int64(v.Int64) +} + +// AddSQLNullInt64OmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddSQLNullInt64OmitEmpty(v *sql.NullInt64) { + if v != nil && v.Valid && v.Int64 != 0 { + enc.Int64OmitEmpty(v.Int64) + } +} + +// AddSQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) AddSQLNullInt64Key(key string, v *sql.NullInt64) { + enc.Int64Key(key, v.Int64) +} + +// AddSQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) AddSQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) { + if v != nil && v.Valid && v.Int64 != 0 { + enc.Int64KeyOmitEmpty(key, v.Int64) + } +} + +// SQLNullInt64 adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullInt64(v *sql.NullInt64) { + enc.Int64(v.Int64) +} + +// SQLNullInt64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullInt64OmitEmpty(v *sql.NullInt64) { + if v != nil && v.Valid && v.Int64 != 0 { + enc.Int64(v.Int64) + } +} + +// SQLNullInt64Key adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullInt64Key(key string, v *sql.NullInt64) { + enc.Int64Key(key, v.Int64) +} + +// SQLNullInt64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullInt64KeyOmitEmpty(key string, v *sql.NullInt64) { + if v != nil && v.Valid && v.Int64 != 0 { + enc.Int64KeyOmitEmpty(key, v.Int64) + } +} + +// NullFloat64 + +// EncodeSQLNullFloat64 encodes a string to +func (enc *Encoder) EncodeSQLNullFloat64(v *sql.NullFloat64) error { + if enc.isPooled == 1 { + panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) + } + _, _ = enc.encodeFloat(v.Float64) + _, err := enc.Write() + if err != nil { + enc.err = err + return err + } + return nil +} + +// AddSQLNullFloat64 adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddSQLNullFloat64(v *sql.NullFloat64) { + enc.Float64(v.Float64) +} + +// AddSQLNullFloat64OmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddSQLNullFloat64OmitEmpty(v *sql.NullFloat64) { + if v != nil && v.Valid && v.Float64 != 0 { + enc.Float64OmitEmpty(v.Float64) + } +} + +// AddSQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) AddSQLNullFloat64Key(key string, v *sql.NullFloat64) { + enc.Float64Key(key, v.Float64) +} + +// AddSQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) AddSQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) { + if v != nil && v.Valid && v.Float64 != 0 { + enc.Float64KeyOmitEmpty(key, v.Float64) + } +} + +// SQLNullFloat64 adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullFloat64(v *sql.NullFloat64) { + enc.Float64(v.Float64) +} + +// SQLNullFloat64OmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullFloat64OmitEmpty(v *sql.NullFloat64) { + if v != nil && v.Valid && v.Float64 != 0 { + enc.Float64(v.Float64) + } +} + +// SQLNullFloat64Key adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullFloat64Key(key string, v *sql.NullFloat64) { + enc.Float64Key(key, v.Float64) +} + +// SQLNullFloat64KeyOmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullFloat64KeyOmitEmpty(key string, v *sql.NullFloat64) { + if v != nil && v.Valid && v.Float64 != 0 { + enc.Float64KeyOmitEmpty(key, v.Float64) + } +} + +// NullBool + +// EncodeSQLNullBool encodes a string to +func (enc *Encoder) EncodeSQLNullBool(v *sql.NullBool) error { + if enc.isPooled == 1 { + panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) + } + _, _ = enc.encodeBool(v.Bool) + _, err := enc.Write() + if err != nil { + enc.err = err + return err + } + return nil +} + +// AddSQLNullBool adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddSQLNullBool(v *sql.NullBool) { + enc.Bool(v.Bool) +} + +// AddSQLNullBoolOmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside a slice or array encoding (does not encode a key) +func (enc *Encoder) AddSQLNullBoolOmitEmpty(v *sql.NullBool) { + if v != nil && v.Valid && v.Bool != false { + enc.BoolOmitEmpty(v.Bool) + } +} + +// AddSQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) AddSQLNullBoolKey(key string, v *sql.NullBool) { + enc.BoolKey(key, v.Bool) +} + +// AddSQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) AddSQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) { + if v != nil && v.Valid && v.Bool != false { + enc.BoolKeyOmitEmpty(key, v.Bool) + } +} + +// SQLNullBool adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullBool(v *sql.NullBool) { + enc.Bool(v.Bool) +} + +// SQLNullBoolOmitEmpty adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullBoolOmitEmpty(v *sql.NullBool) { + if v != nil && v.Valid && v.Bool != false { + enc.Bool(v.Bool) + } +} + +// SQLNullBoolKey adds a string to be encoded, must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullBoolKey(key string, v *sql.NullBool) { + enc.BoolKey(key, v.Bool) +} + +// SQLNullBoolKeyOmitEmpty adds a string to be encoded or skips it if it is zero value. +// Must be used inside an object as it will encode a key +func (enc *Encoder) SQLNullBoolKeyOmitEmpty(key string, v *sql.NullBool) { + if v != nil && v.Valid && v.Bool != false { + enc.BoolKeyOmitEmpty(key, v.Bool) + } +} diff --git a/encode_sqlnull_test.go b/encode_sqlnull_test.go @@ -0,0 +1,1121 @@ +package gojay + +import ( + "database/sql" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +// Null String +func TestEncoceSQLNullString(t *testing.T) { + testCases := []struct { + name string + sqlNullString sql.NullString + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullString: sql.NullString{ + String: "foo bar", + }, + expectedResult: `"foo bar"`, + }, + { + name: "it should return an err as the string is invalid", + sqlNullString: sql.NullString{ + String: "foo \t bar", + }, + expectedResult: `"foo \t bar"`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + err := enc.EncodeSQLNullString(&testCase.sqlNullString) + if testCase.err { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + assert.Equal(t, testCase.expectedResult, b.String()) + } + }) + } + + t.Run( + "should panic as the encoder is pooled", + func(t *testing.T) { + builder := &strings.Builder{} + enc := NewEncoder(builder) + enc.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err should not be nil") + assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") + }() + _ = enc.EncodeSQLNullString(&sql.NullString{}) + assert.True(t, false, "should not be called as encoder should have panicked") + }, + ) + + t.Run( + "should return an error as the writer encounters an error", + func(t *testing.T) { + builder := TestWriterError("") + enc := NewEncoder(builder) + err := enc.EncodeSQLNullString(&sql.NullString{}) + assert.NotNil(t, err) + }, + ) +} + +func TestAddSQLNullStringKey(t *testing.T) { + t.Run( + "AddSQLNullStringKey", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullString sql.NullString + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullString: sql.NullString{ + String: "foo bar", + }, + baseJSON: "{", + expectedResult: `{"foo":"foo bar"`, + }, + { + name: "it should encode a null string", + sqlNullString: sql.NullString{ + String: "foo \t bar", + }, + baseJSON: "{", + expectedResult: `{"foo":"foo \t bar"`, + }, + { + name: "it should encode a null string", + sqlNullString: sql.NullString{ + String: "foo \t bar", + }, + baseJSON: "{", + expectedResult: `{"foo":"foo \t bar"`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullStringKey("foo", &testCase.sqlNullString) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullStringKey("foo", &testCase.sqlNullString) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) + t.Run( + "AddSQLNullStringKeyOmitEmpty, is should encode a sql.NullString", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullString sql.NullString + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullString: sql.NullString{ + String: "foo bar", + Valid: true, + }, + baseJSON: "{", + expectedResult: `{"foo":"foo bar"`, + }, + { + name: "it should not encode anything as null string is invalid", + sqlNullString: sql.NullString{ + String: "foo \t bar", + Valid: false, + }, + baseJSON: "{", + expectedResult: `{`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullStringKeyOmitEmpty("foo", &testCase.sqlNullString) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullStringKeyOmitEmpty("foo", &testCase.sqlNullString) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) +} + +func TestAddSQLNullString(t *testing.T) { + t.Run( + "AddSQLNullString", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullString sql.NullString + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullString: sql.NullString{ + String: "foo bar", + }, + baseJSON: "[", + expectedResult: `["foo bar"`, + }, + { + name: "it should encode a null string", + sqlNullString: sql.NullString{ + String: "foo \t bar", + }, + baseJSON: "[", + expectedResult: `["foo \t bar"`, + }, + { + name: "it should encode a null string", + sqlNullString: sql.NullString{ + String: "foo \t bar", + }, + baseJSON: "[", + expectedResult: `["foo \t bar"`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullString(&testCase.sqlNullString) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullString(&testCase.sqlNullString) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) + t.Run( + "AddSQLNullStringKeyOmitEmpty, is should encode a sql.NullString", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullString sql.NullString + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullString: sql.NullString{ + String: "foo bar", + Valid: true, + }, + baseJSON: "[", + expectedResult: `["foo bar"`, + }, + { + name: "it should not encode anything as null string is invalid", + sqlNullString: sql.NullString{ + String: "foo \t bar", + Valid: false, + }, + baseJSON: "[", + expectedResult: `[`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullStringOmitEmpty(&testCase.sqlNullString) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullStringOmitEmpty(&testCase.sqlNullString) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) +} + +// NullInt64 +func TestEncoceSQLNullInt64(t *testing.T) { + testCases := []struct { + name string + sqlNullInt64 sql.NullInt64 + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullInt64: sql.NullInt64{ + Int64: int64(1), + }, + expectedResult: `1`, + }, + { + name: "it should return an err as the string is invalid", + sqlNullInt64: sql.NullInt64{ + Int64: int64(2), + }, + expectedResult: `2`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + err := enc.EncodeSQLNullInt64(&testCase.sqlNullInt64) + if testCase.err { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + assert.Equal(t, testCase.expectedResult, b.String()) + } + }) + } + t.Run( + "should panic as the encoder is pooled", + func(t *testing.T) { + builder := &strings.Builder{} + enc := NewEncoder(builder) + enc.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err should not be nil") + assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") + }() + _ = enc.EncodeSQLNullInt64(&sql.NullInt64{}) + assert.True(t, false, "should not be called as encoder should have panicked") + }, + ) + t.Run( + "should return an error as the writer encounters an error", + func(t *testing.T) { + builder := TestWriterError("") + enc := NewEncoder(builder) + err := enc.EncodeSQLNullInt64(&sql.NullInt64{}) + assert.NotNil(t, err) + }, + ) +} + +func TestAddSQLNullInt64Key(t *testing.T) { + t.Run( + "AddSQLNullInt64Key", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullInt64 sql.NullInt64 + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullInt64: sql.NullInt64{ + Int64: 1, + }, + baseJSON: "{", + expectedResult: `{"foo":1`, + }, + { + name: "it should encode a null string", + sqlNullInt64: sql.NullInt64{ + Int64: 2, + }, + baseJSON: "{", + expectedResult: `{"foo":2`, + }, + { + name: "it should encode a null string", + sqlNullInt64: sql.NullInt64{ + Int64: 2, + }, + baseJSON: "{", + expectedResult: `{"foo":2`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullInt64Key("foo", &testCase.sqlNullInt64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullInt64Key("foo", &testCase.sqlNullInt64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) + t.Run( + "AddSQLNullInt64KeyOmitEmpty, is should encode a sql.NullInt64", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullInt64 sql.NullInt64 + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullInt64: sql.NullInt64{ + Int64: 1, + Valid: true, + }, + baseJSON: "{", + expectedResult: `{"foo":1`, + }, + { + name: "it should not encode anything as null string is invalid", + sqlNullInt64: sql.NullInt64{ + Int64: 2, + Valid: false, + }, + baseJSON: "{", + expectedResult: `{`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullInt64KeyOmitEmpty("foo", &testCase.sqlNullInt64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullInt64KeyOmitEmpty("foo", &testCase.sqlNullInt64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) +} + +func TestAddSQLNullInt64(t *testing.T) { + t.Run( + "AddSQLNullInt64", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullInt64 sql.NullInt64 + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullInt64: sql.NullInt64{ + Int64: 1, + }, + baseJSON: "[", + expectedResult: `[1`, + }, + { + name: "it should encode a null string", + sqlNullInt64: sql.NullInt64{ + Int64: 2, + }, + baseJSON: "[", + expectedResult: `[2`, + }, + { + name: "it should encode a null string", + sqlNullInt64: sql.NullInt64{ + Int64: 2, + }, + baseJSON: "[", + expectedResult: `[2`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullInt64(&testCase.sqlNullInt64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullInt64(&testCase.sqlNullInt64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) + t.Run( + "AddSQLNullInt64KeyOmitEmpty, is should encode a sql.NullInt64", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullInt64 sql.NullInt64 + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullInt64: sql.NullInt64{ + Int64: 2, + Valid: true, + }, + baseJSON: "[", + expectedResult: `[2`, + }, + { + name: "it should not encode anything as null string is invalid", + sqlNullInt64: sql.NullInt64{ + Int64: 2, + Valid: false, + }, + baseJSON: "[", + expectedResult: `[`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullInt64OmitEmpty(&testCase.sqlNullInt64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullInt64OmitEmpty(&testCase.sqlNullInt64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) +} + +// NullFloat64 +func TestEncoceSQLNullFloat64(t *testing.T) { + testCases := []struct { + name string + sqlNullFloat64 sql.NullFloat64 + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullFloat64: sql.NullFloat64{ + Float64: float64(1), + }, + expectedResult: `1`, + }, + { + name: "it should return an err as the string is invalid", + sqlNullFloat64: sql.NullFloat64{ + Float64: float64(2), + }, + expectedResult: `2`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + err := enc.EncodeSQLNullFloat64(&testCase.sqlNullFloat64) + if testCase.err { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + assert.Equal(t, testCase.expectedResult, b.String()) + } + }) + } + t.Run( + "should panic as the encoder is pooled", + func(t *testing.T) { + builder := &strings.Builder{} + enc := NewEncoder(builder) + enc.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err should not be nil") + assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") + }() + _ = enc.EncodeSQLNullFloat64(&sql.NullFloat64{}) + assert.True(t, false, "should not be called as encoder should have panicked") + }, + ) + + t.Run( + "should return an error as the writer encounters an error", + func(t *testing.T) { + builder := TestWriterError("") + enc := NewEncoder(builder) + err := enc.EncodeSQLNullFloat64(&sql.NullFloat64{}) + assert.NotNil(t, err) + }, + ) +} + +func TestAddSQLNullFloat64Key(t *testing.T) { + t.Run( + "AddSQLNullFloat64Key", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullFloat64 sql.NullFloat64 + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullFloat64: sql.NullFloat64{ + Float64: 1, + }, + baseJSON: "{", + expectedResult: `{"foo":1`, + }, + { + name: "it should encode a null string", + sqlNullFloat64: sql.NullFloat64{ + Float64: 2, + }, + baseJSON: "{", + expectedResult: `{"foo":2`, + }, + { + name: "it should encode a null string", + sqlNullFloat64: sql.NullFloat64{ + Float64: 2, + }, + baseJSON: "{", + expectedResult: `{"foo":2`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullFloat64Key("foo", &testCase.sqlNullFloat64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullFloat64Key("foo", &testCase.sqlNullFloat64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) + t.Run( + "AddSQLNullFloat64KeyOmitEmpty, is should encode a sql.NullFloat64", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullFloat64 sql.NullFloat64 + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullFloat64: sql.NullFloat64{ + Float64: 1, + Valid: true, + }, + baseJSON: "{", + expectedResult: `{"foo":1`, + }, + { + name: "it should not encode anything as null string is invalid", + sqlNullFloat64: sql.NullFloat64{ + Float64: 2, + Valid: false, + }, + baseJSON: "{", + expectedResult: `{`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullFloat64KeyOmitEmpty("foo", &testCase.sqlNullFloat64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullFloat64KeyOmitEmpty("foo", &testCase.sqlNullFloat64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) +} + +func TestAddSQLNullFloat64(t *testing.T) { + t.Run( + "AddSQLNullFloat64", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullFloat64 sql.NullFloat64 + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullFloat64: sql.NullFloat64{ + Float64: 1, + }, + baseJSON: "[", + expectedResult: `[1`, + }, + { + name: "it should encode a null string", + sqlNullFloat64: sql.NullFloat64{ + Float64: 2, + }, + baseJSON: "[", + expectedResult: `[2`, + }, + { + name: "it should encode a null string", + sqlNullFloat64: sql.NullFloat64{ + Float64: 2, + }, + baseJSON: "[", + expectedResult: `[2`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullFloat64(&testCase.sqlNullFloat64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullFloat64(&testCase.sqlNullFloat64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) + t.Run( + "AddSQLNullFloat64KeyOmitEmpty, is should encode a sql.NullFloat64", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullFloat64 sql.NullFloat64 + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullFloat64: sql.NullFloat64{ + Float64: 2, + Valid: true, + }, + baseJSON: "[", + expectedResult: `[2`, + }, + { + name: "it should not encode anything as null string is invalid", + sqlNullFloat64: sql.NullFloat64{ + Float64: 2, + Valid: false, + }, + baseJSON: "[", + expectedResult: `[`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullFloat64OmitEmpty(&testCase.sqlNullFloat64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullFloat64OmitEmpty(&testCase.sqlNullFloat64) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) +} + +// NullBool +func TestEncoceSQLNullBool(t *testing.T) { + testCases := []struct { + name string + sqlNullBool sql.NullBool + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullBool: sql.NullBool{ + Bool: true, + }, + expectedResult: `true`, + }, + { + name: "it should return an err as the string is invalid", + sqlNullBool: sql.NullBool{ + Bool: false, + }, + expectedResult: `false`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + err := enc.EncodeSQLNullBool(&testCase.sqlNullBool) + if testCase.err { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + assert.Equal(t, testCase.expectedResult, b.String()) + } + }) + } + t.Run( + "should panic as the encoder is pooled", + func(t *testing.T) { + builder := &strings.Builder{} + enc := NewEncoder(builder) + enc.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err should not be nil") + assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") + }() + _ = enc.EncodeSQLNullBool(&sql.NullBool{}) + assert.True(t, false, "should not be called as encoder should have panicked") + }, + ) + + t.Run( + "should return an error as the writer encounters an error", + func(t *testing.T) { + builder := TestWriterError("") + enc := NewEncoder(builder) + err := enc.EncodeSQLNullBool(&sql.NullBool{}) + assert.NotNil(t, err) + }, + ) +} + +func TestAddSQLNullBoolKey(t *testing.T) { + t.Run( + "AddSQLNullBoolKey", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullBool sql.NullBool + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullBool: sql.NullBool{ + Bool: true, + }, + baseJSON: "{", + expectedResult: `{"foo":true`, + }, + { + name: "it should encode a null string", + sqlNullBool: sql.NullBool{ + Bool: false, + }, + baseJSON: "{", + expectedResult: `{"foo":false`, + }, + { + name: "it should encode a null string", + sqlNullBool: sql.NullBool{ + Bool: true, + }, + baseJSON: "{", + expectedResult: `{"foo":true`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullBoolKey("foo", &testCase.sqlNullBool) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullBoolKey("foo", &testCase.sqlNullBool) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) + t.Run( + "AddSQLNullBoolKeyOmitEmpty, is should encode a sql.NullBool", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullBool sql.NullBool + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullBool: sql.NullBool{ + Bool: true, + Valid: true, + }, + baseJSON: "{", + expectedResult: `{"foo":true`, + }, + { + name: "it should not encode anything as null string is invalid", + sqlNullBool: sql.NullBool{ + Bool: true, + Valid: false, + }, + baseJSON: "{", + expectedResult: `{`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullBoolKeyOmitEmpty("foo", &testCase.sqlNullBool) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullBoolKeyOmitEmpty("foo", &testCase.sqlNullBool) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) +} + +func TestAddSQLNullBool(t *testing.T) { + t.Run( + "AddSQLNullBool", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullBool sql.NullBool + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullBool: sql.NullBool{ + Bool: true, + }, + baseJSON: "[", + expectedResult: `[true`, + }, + { + name: "it should encode a null string", + sqlNullBool: sql.NullBool{ + Bool: true, + }, + baseJSON: "[", + expectedResult: `[true`, + }, + { + name: "it should encode a null string", + sqlNullBool: sql.NullBool{ + Bool: false, + }, + baseJSON: "[", + expectedResult: `[false`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullBool(&testCase.sqlNullBool) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullBool(&testCase.sqlNullBool) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) + t.Run( + "AddSQLNullBoolKeyOmitEmpty, is should encode a sql.NullBool", + func(t *testing.T) { + testCases := []struct { + name string + sqlNullBool sql.NullBool + baseJSON string + expectedResult string + err bool + }{ + { + name: "it should encode a null string", + sqlNullBool: sql.NullBool{ + Bool: true, + Valid: true, + }, + baseJSON: "[", + expectedResult: `[true`, + }, + { + name: "it should not encode anything as null string is invalid", + sqlNullBool: sql.NullBool{ + Bool: true, + Valid: false, + }, + baseJSON: "[", + expectedResult: `[`, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var b strings.Builder + enc := NewEncoder(&b) + enc.writeString(testCase.baseJSON) + enc.AddSQLNullBoolOmitEmpty(&testCase.sqlNullBool) + enc.Write() + assert.Equal(t, testCase.expectedResult, b.String()) + + var b2 strings.Builder + enc = NewEncoder(&b2) + enc.writeString(testCase.baseJSON) + enc.SQLNullBoolOmitEmpty(&testCase.sqlNullBool) + enc.Write() + assert.Equal(t, testCase.expectedResult, b2.String()) + }) + } + }, + ) +}