add JsonBox type in pkg/types

pull/230/head
Michael Li 2 years ago
parent 81e022f626
commit 0b68e4bb9d
No known key found for this signature in database

@ -0,0 +1,82 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package types
import (
"database/sql"
"database/sql/driver"
stdjson "encoding/json"
"errors"
"fmt"
"github.com/rocboss/paopao-ce/pkg/json"
)
var (
_ stdjson.Marshaler = (*JsonBox[any])(nil)
_ stdjson.Unmarshaler = (*JsonBox[any])(nil)
_ driver.Valuer = (*JsonBox[any])(nil)
_ sql.Scanner = (*JsonBox[any])(nil)
_ Boxes[any] = (*JsonBox[any])(nil)
)
// JsonBox Json box for process database/sql json data
type JsonBox[T any] struct {
data T
}
func (j *JsonBox[T]) Box(t T) {
j.data = t
}
func (j *JsonBox[T]) Unbox() T {
return j.data
}
func (j *JsonBox[T]) MarshalJSON() ([]byte, error) {
if j == nil {
return []byte(`null`), nil
}
return json.Marshal(j.data)
}
func (j *JsonBox[T]) UnmarshalJSON(data []byte) error {
if j == nil {
return errors.New("JsonBox.UnmarshalJSON: on nil pointer")
}
return json.Unmarshal(data, &j.data)
}
func (j *JsonBox[T]) Value() (driver.Value, error) {
if j == nil {
return nil, nil
}
return j.MarshalJSON()
}
func (j *JsonBox[T]) Scan(value any) error {
if value == nil {
return nil
}
var b []byte
switch v := value.(type) {
case []byte:
b = v
case string:
b = []byte(v)
default:
return fmt.Errorf("JsonBox.Scan: expected []byte or string, got %T (%q)", value, value)
}
return j.UnmarshalJSON(b)
}
// NewJsonBox create a new JsonBox instance
func NewJsonBox[T any](t ...T) *JsonBox[T] {
if len(t) > 0 {
return &JsonBox[T]{data: t[0]}
}
return &JsonBox[T]{}
}

@ -0,0 +1,85 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package types_test
import (
"encoding/json"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rocboss/paopao-ce/pkg/types"
)
var _ = Describe("JsonBox", Ordered, func() {
type jsonCases []struct {
j *types.JsonBox[json.RawMessage]
b []byte
}
var samples jsonCases
BeforeAll(func() {
samples = jsonCases{
{
j: types.NewJsonBox(json.RawMessage(`null`)),
b: []byte(`null`),
},
{
j: types.NewJsonBox(json.RawMessage(`{}`)),
b: []byte(`{}`),
},
{
j: types.NewJsonBox(json.RawMessage(`[]`)),
b: []byte(`[]`),
},
{
j: types.NewJsonBox(json.RawMessage(`[{"b":true,"n":123},{"s":"foo","obj":{"f1":456,"f2":false}},[null]]`)),
b: []byte(`[{"b":true,"n":123},{"s":"foo","obj":{"f1":456,"f2":false}},[null]]`),
},
}
})
It("boxes Box and Unbox", func() {
for _, t := range samples {
jv := types.NewJsonBox[json.RawMessage]()
jv.Box(json.RawMessage(t.b))
Expect(jv.Unbox()).To(Equal(t.j.Unbox()))
}
})
It("json marshaler", func() {
for _, t := range samples {
mv, err := t.j.MarshalJSON()
Expect(err).To(BeNil())
Expect(mv).To(Equal(t.b))
}
})
It("json unmarshaler", func() {
for _, t := range samples {
jv := types.NewJsonBox[json.RawMessage]()
err := jv.UnmarshalJSON(t.b)
Expect(err).To(BeNil())
Expect(t.j.Unbox()).To(Equal(jv.Unbox()))
}
})
It("driver valuer", func() {
for _, t := range samples {
v, err := t.j.Value()
Expect(err).To(BeNil())
Expect(v).To(Equal(t.b))
}
})
It("sql scaner", func() {
for _, t := range samples {
jv := types.NewJsonBox[json.RawMessage]()
err := jv.Scan(t.b)
Expect(err).To(BeNil())
Expect(jv.Unbox()).To(Equal(t.j.Unbox()))
}
})
})

@ -9,3 +9,9 @@ type Empty = struct{}
// Fn empty argument func alias type
type Fn = func()
// Boxes Box/Unbox interface
type Boxes[T any] interface {
Box(t T)
Unbox() T
}

@ -0,0 +1,17 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package types_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestTypes(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Types Suite")
}
Loading…
Cancel
Save