From 2e751bf5db288109bc3210b9b3a5ff57654b8f13 Mon Sep 17 00:00:00 2001 From: Yi Duan Date: Tue, 28 Jun 2022 13:27:07 +0800 Subject: [PATCH] feat:(ast) Add more strict casting API (#249) * feat:(ast) Add more strict API * test: add cast test --- ast/encode_test.go | 2 +- ast/node.go | 93 +++++++++++++++++++++++++++++++++++++++++++--- ast/node_test.go | 41 +++++++++++++++++++- 3 files changed, 128 insertions(+), 8 deletions(-) diff --git a/ast/encode_test.go b/ast/encode_test.go index 69051fe..c451834 100644 --- a/ast/encode_test.go +++ b/ast/encode_test.go @@ -85,7 +85,7 @@ func TestEncodeValue(t *testing.T) { {NewBool(false), "false", false}, {NewNumber("0.0"), "0.0", false}, {NewString(""), `""`, false}, - {NewString("\"\""), `"\"\""`, false}, + {NewString(`\"\"`), `"\\\"\\\""`, false}, {NewString(_TwitterJson), string(quote), false}, {NewArray([]Node{}), "[]", false}, {NewArray([]Node{NewBool(true), NewString("true"), NewString("\t")}), `[true,"true","\t"]`, false}, diff --git a/ast/node.go b/ast/node.go index c4a87ad..f08a9c4 100644 --- a/ast/node.go +++ b/ast/node.go @@ -165,7 +165,7 @@ func (self *Node) checkRaw() error { return nil } -// Bool_E returns bool value represented by this node +// Bool returns bool value represented by this node // // If node type is not types.V_TRUE or types.V_FALSE, // V_RAW (must be a bool json value), or V_ANY (must be a bool type) @@ -187,7 +187,7 @@ func (self *Node) Bool() (bool, error) { } } -// Int64 as above. +// Int64 casts the node to int64 value, including V_NUMBER, V_TRUE, V_FALSE, V_ANY func (self *Node) Int64() (int64, error) { if err := self.checkRaw(); err != nil { return 0, err @@ -215,7 +215,33 @@ func (self *Node) Int64() (int64, error) { } } -// Number as above. +// StrictInt64 exports underlying int64 value, including V_NUMBER, V_ANY +func (self *Node) StrictInt64() (int64, error) { + if err := self.checkRaw(); err != nil { + return 0, err + } + switch self.t { + case _V_NUMBER : return numberToInt64(self) + case _V_ANY : + any := self.packAny() + switch v := any.(type) { + case int : return int64(v), nil + case int8 : return int64(v), nil + case int16 : return int64(v), nil + case int32 : return int64(v), nil + case int64 : return int64(v), nil + case uint : return int64(v), nil + case uint8 : return int64(v), nil + case uint16: return int64(v), nil + case uint32: return int64(v), nil + case uint64: return int64(v), nil + default: return 0, ErrUnsupportType + } + default : return 0, ErrUnsupportType + } +} + +// Number casts node to float64, including V_NUMBER, V_TRUE, V_FALSE, V_ANY of json.Number func (self *Node) Number() (json.Number, error) { if err := self.checkRaw(); err != nil { return json.Number(""), err @@ -234,6 +260,23 @@ func (self *Node) Number() (json.Number, error) { } } +// Number exports underlying float64 value, including V_NUMBER, V_ANY of json.Number +func (self *Node) StrictNumber() (json.Number, error) { + if err := self.checkRaw(); err != nil { + return json.Number(""), err + } + switch self.t { + case _V_NUMBER : return toNumber(self) , nil + case _V_ANY : + if v, ok := self.packAny().(json.Number); ok { + return v, nil + } else { + return json.Number(""), ErrUnsupportType + } + default : return json.Number(""), ErrUnsupportType + } +} + // String returns raw string value if node type is V_STRING. // Or return the string representation of other types: // V_NULL => "null", @@ -261,7 +304,26 @@ func (self *Node) String() (string, error) { } } -// Float64 as above. +// StrictString returns string value (unescaped), includeing V_STRING, V_ANY of string. +// In other cases, it will return empty string. +func (self *Node) StrictString() (string, error) { + if err := self.checkRaw(); err != nil { + return "", err + } + switch self.t { + case types.V_NULL : return "", nil + case types.V_STRING : return addr2str(self.p, self.v), nil + case _V_ANY : + if v, ok := self.packAny().(string); ok { + return v, nil + } else { + return "", ErrUnsupportType + } + default : return "", ErrUnsupportType + } +} + +// Float64 casts node to float64, includeing V_NUMBER, V_TRUE, V_FALSE, V_ANY func (self *Node) Float64() (float64, error) { if err := self.checkRaw(); err != nil { return 0.0, err @@ -281,6 +343,24 @@ func (self *Node) Float64() (float64, error) { } } +// Float64 exports underlying float64 value, includeing V_NUMBER, V_ANY +func (self *Node) StrictFloat64() (float64, error) { + if err := self.checkRaw(); err != nil { + return 0.0, err + } + switch self.t { + case _V_NUMBER : return numberToFloat64(self) + case _V_ANY : + any := self.packAny() + switch v := any.(type) { + case float32 : return float64(v), nil + case float64 : return float64(v), nil + default : return 0, ErrUnsupportType + } + default : return 0.0, ErrUnsupportType + } +} + /** Sequencial Value Methods **/ // Len returns children count of a array|object|string node @@ -1410,7 +1490,10 @@ func newBytes(v []byte) Node { } } -// NewString creates a node of type string +// NewString creates a node of type V_STRING. +// v is considered to be a valid UTF-8 string, +// which means it won't be validated and unescaped. +// when the node is encoded to json, v will be escaped. func NewString(v string) Node { return Node{ t: types.V_STRING, diff --git a/ast/node_test.go b/ast/node_test.go index 1e8ad32..7446dad 100644 --- a/ast/node_test.go +++ b/ast/node_test.go @@ -280,6 +280,8 @@ func TestTypeCast(t *testing.T) { {"Bool", NewAny(false), false, nil}, {"Bool", NewRaw("true"), true, nil}, {"Bool", NewRaw("false"), false, nil}, + {"Int64", NewRaw("true"), int64(1), nil}, + {"Int64", NewRaw("false"), int64(0), nil}, {"Int64", NewAny(int(0)), int64(0), nil}, {"Int64", NewAny(int8(0)), int64(0), nil}, {"Int64", NewAny(int16(0)), int64(0), nil}, @@ -291,22 +293,57 @@ func TestTypeCast(t *testing.T) { {"Int64", NewAny(uint64(0)), int64(0), nil}, {"Int64", Node{}, int64(0), ErrUnsupportType}, {"Int64", NewRaw("0"), int64(0), nil}, + {"StrictInt64", NewRaw("true"), int64(0), ErrUnsupportType}, + {"StrictInt64", NewRaw("false"), int64(0), ErrUnsupportType}, + {"StrictInt64", NewAny(int(0)), int64(0), nil}, + {"StrictInt64", NewAny(int8(0)), int64(0), nil}, + {"StrictInt64", NewAny(int16(0)), int64(0), nil}, + {"StrictInt64", NewAny(int32(0)), int64(0), nil}, + {"StrictInt64", NewAny(int64(0)), int64(0), nil}, + {"StrictInt64", NewAny(uint(0)), int64(0), nil}, + {"StrictInt64", NewAny(uint8(0)), int64(0), nil}, + {"StrictInt64", NewAny(uint32(0)), int64(0), nil}, + {"StrictInt64", NewAny(uint64(0)), int64(0), nil}, + {"StrictInt64", Node{}, int64(0), ErrUnsupportType}, + {"StrictInt64", NewRaw("0"), int64(0), nil}, + {"Float64", NewRaw("true"), float64(1), nil}, + {"Float64", NewRaw("false"), float64(0), nil}, {"Float64", Node{}, float64(0), ErrUnsupportType}, {"Float64", NewAny(float32(0)), float64(0), nil}, {"Float64", NewAny(float64(0)), float64(0), nil}, {"Float64", NewRaw("0.0"), float64(0.0), nil}, + {"StrictFloat64", NewRaw("true"), float64(0), ErrUnsupportType}, + {"StrictFloat64", NewRaw("false"), float64(0), ErrUnsupportType}, + {"StrictFloat64", Node{}, float64(0), ErrUnsupportType}, + {"StrictFloat64", NewAny(float32(0)), float64(0), nil}, + {"StrictFloat64", NewAny(float64(0)), float64(0), nil}, + {"StrictFloat64", NewRaw("0.0"), float64(0.0), nil}, {"Number", Node{}, json.Number(""), ErrUnsupportType}, {"Number", NewAny(json.Number("0")), json.Number("0"), nil}, {"Number", NewRaw("0.0"), json.Number("0.0"), nil}, {"Number", NewRaw("true"), json.Number("1"), nil}, {"Number", NewRaw("false"), json.Number("0"), nil}, + {"StrictNumber", NewRaw("true"), json.Number(""), ErrUnsupportType}, + {"StrictNumber", NewRaw("false"), json.Number(""), ErrUnsupportType}, + {"StrictNumber", Node{}, json.Number(""), ErrUnsupportType}, + {"StrictNumber", NewAny(json.Number("0")), json.Number("0"), nil}, + {"StrictNumber", NewRaw("0.0"), json.Number("0.0"), nil}, {"String", Node{}, "", ErrUnsupportType}, - {"String", NewAny(""), "", nil}, - {"String", NewRaw(`""`), ``, nil}, + {"String", NewAny(`\u263a`), `\u263a`, nil}, + {"String", NewRaw(`"\u263a"`), `☺`, nil}, + {"String", NewString(`\u263a`), `\u263a`, nil}, {"String", NewRaw(`0.0`), "0.0", nil}, {"String", NewRaw(`null`), "null", nil}, {"String", NewRaw(`true`), "true", nil}, {"String", NewRaw(`false`), "false", nil}, + {"StrictString", Node{}, "", ErrUnsupportType}, + {"StrictString", NewAny(`\u263a`), `\u263a`, nil}, + {"StrictString", NewRaw(`"\u263a"`), `☺`, nil}, + {"StrictString", NewString(`\u263a`), `\u263a`, nil}, + {"StrictString", NewRaw(`0.0`), "", ErrUnsupportType}, + {"StrictString", NewRaw(`null`), "", nil}, + {"StrictString", NewRaw(`true`), "", ErrUnsupportType}, + {"StrictString", NewRaw(`false`), "", ErrUnsupportType}, {"Len", Node{}, 0, nil}, {"Len", NewAny(0), 0, ErrUnsupportType}, {"Len", NewNull(), 0, nil},