From 7e2f7a6f86a53385724cdda3d253d2e3594028b3 Mon Sep 17 00:00:00 2001 From: yeebing Date: Thu, 4 May 2023 21:07:51 +0800 Subject: [PATCH] feat(contrib/encoding): support toml codec --- contrib/encoding/toml/go.mod | 8 +++ contrib/encoding/toml/toml.go | 37 ++++++++++++ contrib/encoding/toml/toml_test.go | 97 ++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 contrib/encoding/toml/go.mod create mode 100644 contrib/encoding/toml/toml.go create mode 100644 contrib/encoding/toml/toml_test.go diff --git a/contrib/encoding/toml/go.mod b/contrib/encoding/toml/go.mod new file mode 100644 index 000000000..8909e8df3 --- /dev/null +++ b/contrib/encoding/toml/go.mod @@ -0,0 +1,8 @@ +module github.com/go-kratos/kratos/contrib/encoding/toml/v2 + +go 1.20 + +require ( + github.com/BurntSushi/toml v1.2.1 + github.com/go-kratos/kratos/v2 v2.6.1 +) diff --git a/contrib/encoding/toml/toml.go b/contrib/encoding/toml/toml.go new file mode 100644 index 000000000..f948ac3ee --- /dev/null +++ b/contrib/encoding/toml/toml.go @@ -0,0 +1,37 @@ +package toml + +import ( + "bytes" + "github.com/BurntSushi/toml" + "github.com/go-kratos/kratos/v2/encoding" +) + +// Name is the name registered for the toml compressor. +const Name = "toml" + +func init() { + encoding.RegisterCodec(codec{}) +} + +// codec is a Codec implementation with toml. +type codec struct{} + +func (c codec) Marshal(v interface{}) ([]byte, error) { + buf := &bytes.Buffer{} + encoder := toml.NewEncoder(buf) + + if err := encoder.Encode(v); err != nil { + return nil, err + } + + data := buf.Bytes() + return data, nil +} + +func (c codec) Unmarshal(data []byte, v interface{}) error { + return toml.Unmarshal(data, v) +} + +func (c codec) Name() string { + return Name +} diff --git a/contrib/encoding/toml/toml_test.go b/contrib/encoding/toml/toml_test.go new file mode 100644 index 000000000..2b8f383fa --- /dev/null +++ b/contrib/encoding/toml/toml_test.go @@ -0,0 +1,97 @@ +package toml + +import ( + "reflect" + "testing" + "time" +) + +func TestCodec_Name(t *testing.T) { + if (codec{}).Name() != Name { + t.Fatal("(codec{}).Name() should be consistent with Name") + } +} + +func TestCodec_Unmarshal(t *testing.T) { + type User struct { + Name string `toml:"name"` + Email string `toml:"email"` + } + tests := []struct { + data string + value interface{} + }{ + { + data: "v = \"John Doe\"", + value: map[string]interface{}{"v": "John Doe"}, + }, + { + data: "v = 100", + value: map[string]interface{}{"v": 100}, + }, + { + data: "v = 100_100", + value: map[string]interface{}{"v": 100100}, + }, + { + data: "v = 1.1", + value: map[string]interface{}{"v": 1.1}, + }, + { + data: "v = true", + value: map[string]interface{}{"v": true}, + }, + { + data: "v = [\"apple\", \"banana\", \"cherry\"]", + value: map[string]interface{}{"": []string{"apple", "banana", "cherry"}}, + }, + { + data: "v = 1.618e+01", + value: map[string]interface{}{"v": 16.18}, + }, + { + data: "v = 0xDEADBEEF", + value: map[string]interface{}{"v": 3735928559}, + }, + { + data: "v = 0b1101_0101", + value: map[string]interface{}{"v": 213}, + }, + { + data: "v = 0o755", + value: map[string]interface{}{"v": 493}, + }, + { + data: "v = 2022-04-16T12:13:14Z", + value: map[string]interface{}{"v": time.Date(2022, time.April, 16, 12, 13, 14, 0, time.UTC)}, + }, + { + data: "v = { name = \"John\", email = \"john.doe@example.com\"}", + value: map[string]interface{}{"v": User{ + Name: "John", + Email: "john.doe@example.com", + }}, + }, + } + + for _, tt := range tests { + v := reflect.ValueOf(tt.value).Type() + value := reflect.New(v) + err := (codec{}).Unmarshal([]byte(tt.data), value.Interface()) + if err != nil { + t.Fatalf("(codec{}).Unmarshal should not return err: %v", err) + } + } +} + +func TestCodec_Marshal(t *testing.T) { + value := map[string]string{"v": "hi"} + got, err := (codec{}).Marshal(value) + if err != nil { + t.Fatalf("should not return err") + } + //t.Logf("got: %v", string(got)) + if string(got) != "v = \"hi\"\n" { + t.Fatalf("want v = \"hi\", return %s", string(got)) + } +}