From b277fac8eacf69733b2078d8b92161b6b9ff808f Mon Sep 17 00:00:00 2001 From: tdakkota Date: Sat, 4 Dec 2021 22:32:37 +0300 Subject: [PATCH] feat(tljson): add JSONValue helper --- telegram/tljson/convert.go | 101 ++++++++++++++++++++++++++++++++ telegram/tljson/convert_test.go | 67 +++++++++++++++++++++ telegram/tljson/tljson.go | 2 + 3 files changed, 170 insertions(+) create mode 100644 telegram/tljson/convert.go create mode 100644 telegram/tljson/convert_test.go create mode 100644 telegram/tljson/tljson.go diff --git a/telegram/tljson/convert.go b/telegram/tljson/convert.go new file mode 100644 index 0000000000..4274953c35 --- /dev/null +++ b/telegram/tljson/convert.go @@ -0,0 +1,101 @@ +package tljson + +import ( + "github.com/go-faster/errors" + "github.com/go-faster/jx" + + "github.com/gotd/td/tg" +) + +// Decode decodes JSON and converts it to tg.JSONValueClass. +func Decode(d *jx.Decoder) (tg.JSONValueClass, error) { + switch tt := d.Next(); tt { + case jx.String: + s, err := d.Str() + if err != nil { + return nil, err + } + return &tg.JSONString{Value: s}, nil + case jx.Number: + f, err := d.Float64() + if err != nil { + return nil, err + } + return &tg.JSONNumber{Value: f}, nil + case jx.Null: + if err := d.Null(); err != nil { + return nil, err + } + return &tg.JSONNull{}, nil + case jx.Bool: + b, err := d.Bool() + if err != nil { + return nil, err + } + return &tg.JSONBool{Value: b}, nil + case jx.Array: + var r []tg.JSONValueClass + if err := d.Arr(func(d *jx.Decoder) error { + obj, err := Decode(d) + if err != nil { + return errors.Wrapf(err, "decode %d element", len(r)) + } + + r = append(r, obj) + return nil + }); err != nil { + return nil, err + } + return &tg.JSONArray{Value: r}, nil + case jx.Object: + var r []tg.JSONObjectValue + if err := d.Obj(func(d *jx.Decoder, key string) error { + obj, err := Decode(d) + if err != nil { + return errors.Wrapf(err, "decode %q", key) + } + + r = append(r, tg.JSONObjectValue{ + Key: key, + Value: obj, + }) + return nil + }); err != nil { + return nil, err + } + return &tg.JSONObject{Value: r}, nil + default: + return nil, errors.Errorf("unexpected type %v", tt) + } +} + +// Encode writes given value to Encoder. +func Encode(obj tg.JSONValueClass, e *jx.Encoder) { + switch obj := obj.(type) { + case *tg.JSONNull: + e.Null() + case *tg.JSONBool: + e.Bool(obj.Value) + case *tg.JSONNumber: + if v := int64(obj.Value); float64(v) == obj.Value { + e.Int64(v) + } else { + e.Float64(obj.Value) + } + case *tg.JSONString: + e.Str(obj.Value) + case *tg.JSONArray: + e.ArrStart() + for _, v := range obj.Value { + Encode(v, e) + } + e.ArrEnd() + case *tg.JSONObject: + e.ObjStart() + for _, pair := range obj.Value { + e.FieldStart(pair.Key) + Encode(pair.Value, e) + } + e.ObjEnd() + } +} diff --git a/telegram/tljson/convert_test.go b/telegram/tljson/convert_test.go new file mode 100644 index 0000000000..19500b0769 --- /dev/null +++ b/telegram/tljson/convert_test.go @@ -0,0 +1,67 @@ +package tljson + +import ( + "testing" + + "github.com/go-faster/jx" + "github.com/stretchr/testify/require" + + "github.com/gotd/td/tg" +) + +func TestDecodeEncodeDecode(t *testing.T) { + tests := []struct { + name string + input string + wantErr bool + expect tg.JSONValueClass + }{ + {"Empty", "", true, nil}, + {"InvalidNull", "nul", true, nil}, + {"InvalidTrue", "tru", true, nil}, + {"InvalidFalse", "falsy", true, nil}, + {"InvalidInt", `[1a]"`, true, nil}, + {"InvalidFloat", "1.", true, nil}, + {"InvalidString", `"hello`, true, nil}, + {"InvalidArray", "[1, 2, 3.]", true, nil}, + {"InvalidObject", `{"abc":"def}`, true, nil}, + + {"Null", "null", false, nil}, + {"True", "true", false, nil}, + {"False", "false", false, nil}, + {"Int", "10", false, nil}, + {"Float", "1.1", false, nil}, + {"String", `"hello"`, false, nil}, + {"EmptyArray", "[]", false, nil}, + {"Array", "[1, 2, 3]", false, nil}, + {"EmptyObject", `{}`, false, nil}, + {"Object", `{"abc":"def"}`, false, nil}, + {"Tree", `{"a":1,"b":true,"c":null,"sub":{"abc":"def"}}`, false, nil}, + } + for _, tt := range tests { + tt := tt + + t.Run(tt.name, func(t *testing.T) { + a := require.New(t) + + // Decode. + d := jx.DecodeStr(tt.input) + obj, err := Decode(d) + if tt.wantErr { + a.Error(err) + return + } + a.NoError(err) + + // Encode. + e := jx.GetEncoder() + Encode(obj, e) + + // Decode. + d.ResetBytes(e.Bytes()) + obj2, err := Decode(d) + a.NoError(err) + a.Equal(obj, obj2) + }) + } +} diff --git a/telegram/tljson/tljson.go b/telegram/tljson/tljson.go new file mode 100644 index 0000000000..737e09242e --- /dev/null +++ b/telegram/tljson/tljson.go @@ -0,0 +1,2 @@ +// Package tljson contains some helpers to work with JSONValue class. +package tljson