diff --git a/pkg/types/json.go b/pkg/types/json.go deleted file mode 100644 index 94bb39f3..00000000 --- a/pkg/types/json.go +++ /dev/null @@ -1,64 +0,0 @@ -// 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 = (*JsonType[any])(nil) - _ stdjson.Unmarshaler = (*JsonType[any])(nil) - _ driver.Valuer = (*JsonType[any])(nil) - _ sql.Scanner = (*JsonType[any])(nil) -) - -type JsonType[T any] struct { - Data T -} - -func (j *JsonType[T]) MarshalJSON() ([]byte, error) { - if j == nil { - return []byte(`null`), nil - } - return json.Marshal(j.Data) -} - -func (j *JsonType[T]) UnmarshalJSON(data []byte) error { - if j == nil { - return errors.New("JSONText.UnmarshalJSON: on nil pointer") - } - return json.Unmarshal(data, &j.Data) - -} - -func (j *JsonType[T]) Value() (driver.Value, error) { - if j == nil { - return nil, nil - } - return j.MarshalJSON() -} - -func (j *JsonType[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("JSONText.Scan: expected []byte or string, got %T (%q)", value, value) - } - return j.UnmarshalJSON(b) -} diff --git a/pkg/types/json_box.go b/pkg/types/json_box.go new file mode 100644 index 00000000..971b3407 --- /dev/null +++ b/pkg/types/json_box.go @@ -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]) MarshalJSON() ([]byte, error) { + if j == nil { + return []byte(`null`), nil + } + return json.Marshal(j.data) +} + +func (j *JsonBox[T]) Box(t T) { + j.data = t +} + +func (j *JsonBox[T]) Unbox() T { + return 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]{} +} diff --git a/pkg/types/json_box_test.go b/pkg/types/json_box_test.go new file mode 100644 index 00000000..b45c5a4f --- /dev/null +++ b/pkg/types/json_box_test.go @@ -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())) + } + }) +}) diff --git a/pkg/types/json_test.go b/pkg/types/json_test.go deleted file mode 100644 index 84e861ef..00000000 --- a/pkg/types/json_test.go +++ /dev/null @@ -1,60 +0,0 @@ -// 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("Json", Ordered, func() { - type jsonCases []struct { - j types.JsonType[json.RawMessage] - b []byte - } - var samples jsonCases - - BeforeAll(func() { - samples = jsonCases{ - { - j: types.JsonType[json.RawMessage]{json.RawMessage(`null`)}, - b: []byte(`null`), - }, - { - j: types.JsonType[json.RawMessage]{json.RawMessage(`{}`)}, - b: []byte(`{}`), - }, - { - j: types.JsonType[json.RawMessage]{json.RawMessage(`[]`)}, - b: []byte(`[]`), - }, - { - j: types.JsonType[json.RawMessage]{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("driver valuer ", func() { - for _, t := range samples { - v, err := t.j.Value() - Expect(err).To(BeNil()) - Expect(v).To(Equal(t.b)) - } - }) - - It("sql scan ", func() { - for _, t := range samples { - var jv types.JsonType[json.RawMessage] - err := jv.Scan(t.b) - Expect(err).To(BeNil()) - Expect(jv.Data).To(Equal(t.j.Data)) - } - }) -}) diff --git a/pkg/types/types.go b/pkg/types/types.go index 830c56c6..e2bb9331 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -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 +}