diff --git a/go.mod b/go.mod index 1d925d27..88c4ce8f 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.21 require ( entgo.io/ent v0.12.5 + github.com/BurntSushi/toml v0.3.1 github.com/PuerkitoBio/goquery v1.8.1 github.com/ThreeDotsLabs/watermill v1.3.4 github.com/ThreeDotsLabs/watermill-sql/v2 v2.0.0 diff --git a/go.sum b/go.sum index b8d9371f..d899a047 100644 --- a/go.sum +++ b/go.sum @@ -46,6 +46,7 @@ entgo.io/ent v0.12.5 h1:KREM5E4CSoej4zeGa88Ou/gfturAnpUv0mzAjch1sj4= entgo.io/ent v0.12.5/go.mod h1:Y3JVAjtlIk8xVZYSn3t3mf8xlZIn5SAOXZQxD6kKI+Q= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= diff --git a/internal/lib/libcodec/codec.go b/internal/lib/libcodec/codec.go index 2bf56121..597b861e 100644 --- a/internal/lib/libcodec/codec.go +++ b/internal/lib/libcodec/codec.go @@ -12,12 +12,15 @@ type ContentType string const ( JSON ContentType = "json" + TOML ContentType = "toml" ) func checkContentType(contentType ContentType) (string, error) { switch contentType { case JSON: return string(JSON), nil + case TOML: + return string(TOML), nil default: return "", errors.New("unsupported content type") } diff --git a/internal/lib/libcodec/toml.go b/internal/lib/libcodec/toml.go new file mode 100644 index 00000000..bf36c8a7 --- /dev/null +++ b/internal/lib/libcodec/toml.go @@ -0,0 +1,37 @@ +// https://github.com/go-kratos/kratos/pull/2811 + +package libcodec + +import ( + "bytes" + + "github.com/BurntSushi/toml" + "github.com/go-kratos/kratos/v2/encoding" +) + +func init() { //nolint: gochecknoinits // required by the encoding package + encoding.RegisterCodec(tomlCodec{}) +} + +// tomlCodec is a Codec implementation with toml. +type tomlCodec struct{} + +func (c tomlCodec) 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 tomlCodec) Unmarshal(data []byte, v interface{}) error { + return toml.Unmarshal(data, v) +} + +func (c tomlCodec) Name() string { + return string(TOML) +} diff --git a/internal/lib/libcodec/toml_test.go b/internal/lib/libcodec/toml_test.go new file mode 100644 index 00000000..894c207e --- /dev/null +++ b/internal/lib/libcodec/toml_test.go @@ -0,0 +1,95 @@ +// https://github.com/go-kratos/kratos/pull/2811 + +package libcodec_test + +import ( + "reflect" + "testing" + "time" + + "github.com/tuihub/librarian/internal/lib/libcodec" +) + +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 := libcodec.Unmarshal(libcodec.TOML, []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 := libcodec.Marshal(libcodec.TOML, 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)) + } +}