From 39ceb82987b93641da5d981f4a5bdc878893e522 Mon Sep 17 00:00:00 2001 From: Kyle Xiao Date: Wed, 20 Nov 2024 16:51:26 +0800 Subject: [PATCH] refactor(binding): use internal tagexpr * remove unused code in github.com/bytedance/go-tagexpr * remove personal repos github.com/henrylee2cn/* --- go.mod | 5 +- go.sum | 14 -- internal/tagexpr/README.md | 204 +------------------ internal/tagexpr/example_test.go | 4 +- internal/tagexpr/expr.go | 46 +---- internal/tagexpr/expr_test.go | 10 +- internal/tagexpr/go.mod | 14 -- internal/tagexpr/go.sum | 35 ---- internal/tagexpr/handler.go | 29 ++- internal/tagexpr/selector.go | 28 +-- internal/tagexpr/selector_test.go | 24 ++- internal/tagexpr/spec_func.go | 6 +- internal/tagexpr/spec_func_test.go | 20 +- internal/tagexpr/spec_operand.go | 12 +- internal/tagexpr/spec_range.go | 14 ++ internal/tagexpr/spec_range_test.go | 38 +++- internal/tagexpr/spec_test.go | 16 +- internal/tagexpr/tagexpr.go | 70 +++---- internal/tagexpr/tagexpr_test.go | 186 ++++++++--------- internal/tagexpr/tagparser.go | 29 ++- internal/tagexpr/tagparser_test.go | 22 +- internal/tagexpr/utils.go | 101 +++++++++ internal/tagexpr/validator/README.md | 2 +- internal/tagexpr/validator/default.go | 25 ++- internal/tagexpr/validator/example_test.go | 18 +- internal/tagexpr/validator/func.go | 18 +- internal/tagexpr/validator/validator.go | 12 +- internal/tagexpr/validator/validator_test.go | 133 +++++++----- pkg/app/server/binding/config.go | 2 +- pkg/app/server/binding/default.go | 2 +- 30 files changed, 535 insertions(+), 604 deletions(-) delete mode 100644 internal/tagexpr/go.mod delete mode 100644 internal/tagexpr/go.sum create mode 100644 internal/tagexpr/utils.go diff --git a/go.mod b/go.mod index 65cd910d6..d2950316f 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module github.com/cloudwego/hertz go 1.17 require ( - github.com/bytedance/go-tagexpr/v2 v2.9.2 github.com/bytedance/gopkg v0.1.0 github.com/bytedance/mockey v1.2.12 github.com/bytedance/sonic v1.12.0 github.com/cloudwego/netpoll v0.6.4 github.com/fsnotify/fsnotify v1.5.4 + github.com/nyaruka/phonenumbers v1.0.55 github.com/tidwall/gjson v1.14.4 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.24.0 @@ -21,11 +21,8 @@ require ( github.com/cloudwego/iasm v0.2.0 // indirect github.com/golang/protobuf v1.5.0 // indirect github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect - github.com/henrylee2cn/ameda v1.4.10 // indirect - github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect - github.com/nyaruka/phonenumbers v1.0.55 // indirect github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect github.com/smartystreets/goconvey v1.6.4 // indirect github.com/tidwall/match v1.1.1 // indirect diff --git a/go.sum b/go.sum index 98999c738..7e920d459 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,3 @@ -github.com/bytedance/go-tagexpr/v2 v2.9.2 h1:QySJaAIQgOEDQBLS3x9BxOWrnhqu5sQ+f6HaZIxD39I= -github.com/bytedance/go-tagexpr/v2 v2.9.2/go.mod h1:5qsx05dYOiUXOUgnQ7w3Oz8BYs2qtM/bJokdLb79wRM= -github.com/bytedance/gopkg v0.0.0-20240507064146-197ded923ae3/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= github.com/bytedance/gopkg v0.1.0 h1:aAxB7mm1qms4Wz4sp8e1AtKDOeFLtdqvGiUe7aonRJs= github.com/bytedance/gopkg v0.1.0/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= github.com/bytedance/mockey v1.2.12 h1:aeszOmGw8CPX8CRx1DZ/Glzb1yXvhjDh6jdFBNZjsU4= @@ -14,8 +11,6 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/ github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/cloudwego/netpoll v0.6.2 h1:+KdILv5ATJU+222wNNXpHapYaBeRvvL8qhJyhcxRxrQ= -github.com/cloudwego/netpoll v0.6.2/go.mod h1:kaqvfZ70qd4T2WtIIpCOi5Cxyob8viEpzLhCrTrz3HM= github.com/cloudwego/netpoll v0.6.4 h1:z/dA4sOTUQof6zZIO4QNnLBXsDFFFEos9OOGloR6kno= github.com/cloudwego/netpoll v0.6.4/go.mod h1:BtM+GjKTdwKoC8IOzD08/+8eEn2gYoiNLipFca6BVXQ= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -30,11 +25,6 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/henrylee2cn/ameda v1.4.8/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= -github.com/henrylee2cn/ameda v1.4.10 h1:JdvI2Ekq7tapdPsuhrc4CaFiqw6QXFvZIULWJgQyCAk= -github.com/henrylee2cn/ameda v1.4.10/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= -github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 h1:yE9ULgp02BhYIrO6sdV/FPe0xQM6fNHkVQW2IAymfM0= -github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8/go.mod h1:Nhe/DM3671a5udlv2AdV2ni/MZzgfv2qrPL5nIi3EGQ= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= @@ -51,14 +41,11 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -92,7 +79,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/tagexpr/README.md b/internal/tagexpr/README.md index d3ea44972..b248ac3b4 100644 --- a/internal/tagexpr/README.md +++ b/internal/tagexpr/README.md @@ -1,203 +1,3 @@ -# go-tagexpr [![report card](https://goreportcard.com/badge/github.com/bytedance/go-tagexpr?style=flat-square)](http://goreportcard.com/report/bytedance/go-tagexpr) [![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](http://godoc.org/github.com/bytedance/go-tagexpr) +# go-tagexpr -An interesting go struct tag expression syntax for field validation, etc. - -## Usage - -- **[Validator](https://github.com/bytedance/go-tagexpr/tree/master/validator)**: A powerful validator that supports struct tag expression - -- **[Binding](https://github.com/bytedance/go-tagexpr/tree/master/binding)**: A powerful HTTP request parameters binder that supports struct tag expression - -## Feature - -- Support for a variety of common operator -- Support for accessing arrays, slices, members of the dictionary -- Support access to any field in the current structure -- Support access to nested fields, non-exported fields, etc. -- Support variable -- Support registers function expression -- Built-in len, sprintf, regexp functions -- Support single mode and multiple mode to define expression -- Parameter check subpackage -- Use offset pointers to directly take values, better performance -- Required go version ≥1.9 - -## Example - -```go -package tagexpr_test - -import ( - "fmt" - - tagexpr "github.com/bytedance/go-tagexpr/v2" -) - -func Example() { - type T struct { - A int `tagexpr:"$<0||$>=100"` - B string `tagexpr:"len($)>1 && regexp('^\\w*$')"` - C bool `tagexpr:"expr1:(f.g)$>0 && $; expr2:'C must be true when T.f.g>0'"` - d []string `tagexpr:"@:len($)>0 && $[0]=='D'; msg:sprintf('invalid d: %v',$)"` - e map[string]int `tagexpr:"len($)==$['len']"` - e2 map[string]*int `tagexpr:"len($)==$['len']"` - f struct { - g int `tagexpr:"$"` - } - h int `tagexpr:"$>minVal"` - } - - vm := tagexpr.New("tagexpr") - t := &T{ - A: 107, - B: "abc", - C: true, - d: []string{"x", "y"}, - e: map[string]int{"len": 1}, - e2: map[string]*int{"len": new(int)}, - f: struct { - g int `tagexpr:"$"` - }{1}, - h: 10, - } - - tagExpr, err := vm.Run(t) - if err != nil { - panic(err) - } - - fmt.Println(tagExpr.Eval("A")) - fmt.Println(tagExpr.Eval("B")) - fmt.Println(tagExpr.Eval("C@expr1")) - fmt.Println(tagExpr.Eval("C@expr2")) - if !tagExpr.Eval("d").(bool) { - fmt.Println(tagExpr.Eval("d@msg")) - } - fmt.Println(tagExpr.Eval("e")) - fmt.Println(tagExpr.Eval("e2")) - fmt.Println(tagExpr.Eval("f.g")) - fmt.Println(tagExpr.EvalWithEnv("h", map[string]interface{}{"minVal": 9})) - fmt.Println(tagExpr.EvalWithEnv("h", map[string]interface{}{"minVal": 11})) - - // Output: - // true - // true - // true - // C must be true when T.f.g>0 - // invalid d: [x y] - // true - // false - // 1 - // true - // false -} -``` - -## Syntax - -Struct tag syntax spec: - -``` -type T struct { - // Single model - Field1 T1 `tagName:"expression"` - // Multiple model - Field2 T2 `tagName:"exprName:expression; [exprName2:expression2;]..."` - // Omit it - Field3 T3 `tagName:"-"` - // Omit it when it is nil - Field4 T4 `tagName:"?"` - ... -} -``` - -NOTE: **The `exprName` under the same struct field cannot be the same!** - -|Operator or Operand|Explain| -|-----|---------| -|`true` `false`|boolean| -|`0` `0.0`|float64 "0"| -|`''`|String| -|`\\'`| Escape `'` delims in string| -|`\"`| Escape `"` delims in string| -|`nil`|nil, undefined| -|`!`|not| -|`+`|Digital addition or string splicing| -|`-`|Digital subtraction or negative| -|`*`|Digital multiplication| -|`/`|Digital division| -|`%`|division remainder, as: `float64(int64(a)%int64(b))`| -|`==`|`eq`| -|`!=`|`ne`| -|`>`|`gt`| -|`>=`|`ge`| -|`<`|`lt`| -|`<=`|`le`| -|`&&`|Logic `and`| -|`\|\|`|Logic `or`| -|`()`|Expression group| -|`(X)$`|Struct field value named X| -|`(X.Y)$`|Struct field value named X.Y| -|`$`|Shorthand for `(X)$`, omit `(X)` to indicate current struct field value| -|`(X)$['A']`|Map value with key A or struct A sub-field in the struct field X| -|`(X)$[0]`|The 0th element or sub-field of the struct field X(type: map, slice, array, struct)| -|`len((X)$)`|Built-in function `len`, the length of struct field X| -|`mblen((X)$)`|the length of string field X (character number)| -|`regexp('^\\w*$', (X)$)`|Regular match the struct field X, return boolean| -|`regexp('^\\w*$')`|Regular match the current struct field, return boolean| -|`sprintf('X value: %v', (X)$)`|`fmt.Sprintf`, format the value of struct field X| -|`range(KvExpr, forEachExpr)`|Iterate over an array, slice, or dictionary
- `#k` is the element key var
- `#v` is the element value var
- `##` is the number of elements
- e.g. [example](spec_range_test.go)| -|`in((X)$, enum_1, ...enum_n)`|Check if the first parameter is one of the enumerated parameters| - - - - - -Operator priority(high -> low): - -* `()` `!` `bool` `float64` `string` `nil` -* `*` `/` `%` -* `+` `-` -* `<` `<=` `>` `>=` -* `==` `!=` -* `&&` -* `||` - -## Field Selector - -``` -field_lv1.field_lv2...field_lvn -``` - -## Expression Selector - -- If expression is **single model** or exprName is `@`: - -``` -field_lv1.field_lv2...field_lvn -``` - -- If expression is **multiple model** and exprName is not `@`: - -``` -field_lv1.field_lv2...field_lvn@exprName -``` - -## Benchmark - -``` -goos: darwin -goarch: amd64 -pkg: github.com/bytedance/go-tagexpr -BenchmarkTagExpr-4 10000000 148 ns/op 32 B/op 3 allocs/op -BenchmarkReflect-4 10000000 182 ns/op 16 B/op 2 allocs/op -PASS -``` - -[Go to test code](https://github.com/bytedance/go-tagexpr/blob/master/tagexpr_test.go#L9-L56) +originally from https://github.com/bytedance/go-tagexpr diff --git a/internal/tagexpr/example_test.go b/internal/tagexpr/example_test.go index 1a16bc82a..fab5e87a4 100644 --- a/internal/tagexpr/example_test.go +++ b/internal/tagexpr/example_test.go @@ -17,7 +17,7 @@ package tagexpr_test import ( "fmt" - tagexpr "github.com/bytedance/go-tagexpr/v2" + "github.com/cloudwego/hertz/internal/tagexpr" ) func Example() { @@ -31,7 +31,7 @@ func Example() { f struct { g int `tagexpr:"$"` } - h int `tagexpr:"$>minVal"` + h int `tagexpr:"$>minVal"` } vm := tagexpr.New("tagexpr") diff --git a/internal/tagexpr/expr.go b/internal/tagexpr/expr.go index 73765da08..776a6795d 100644 --- a/internal/tagexpr/expr.go +++ b/internal/tagexpr/expr.go @@ -15,15 +15,12 @@ package tagexpr import ( - "bytes" "context" "fmt" - "os" - - "github.com/andeya/goutil" ) type variableKeyType string + const variableKey variableKeyType = "__ENV_KEY__" // Expr expression @@ -45,6 +42,7 @@ func parseExpr(expr string) (*Expr, error) { sortPriority(e) return p, nil } + func (p *Expr) parseExprNode(expr *string, e ExprNode) error { trimLeftSpace(expr) if *expr == "" { @@ -297,43 +295,3 @@ func (eb *exprBackground) SetRightOperand(right ExprNode) { } func (*exprBackground) Run(context.Context, string, *TagExpr) interface{} { return nil } - -var debugSwitch = goutil.IsGoTest() - -func printf(format string, a ...interface{}) { - if debugSwitch { - fmt.Fprintf(os.Stderr, format, a...) - } -} - -func printExprNode(node ExprNode) { - if node == nil { - return - } - tail := true - if node.Parent() != nil { - tail = node == node.Parent().RightOperand() - } - printf("%s\n\n", formatExprNode(node, 0, tail)) -} - -func formatExprNode(node ExprNode, level int, tail bool) []byte { - var b bytes.Buffer - if node == nil { - } else { - b.Write(formatExprNode(node.LeftOperand(), level+1, false)) - - b.Write(bytes.Repeat([]byte(" "), level)) - if tail { - b.Write([]byte("└── ")) - } else { - b.Write([]byte("┌── ")) - } - - b.Write([]byte(node.String())) - b.Write([]byte("\n")) - - b.Write(formatExprNode(node.RightOperand(), level+1, true)) - } - return b.Bytes() -} diff --git a/internal/tagexpr/expr_test.go b/internal/tagexpr/expr_test.go index 76456b199..bec82411c 100644 --- a/internal/tagexpr/expr_test.go +++ b/internal/tagexpr/expr_test.go @@ -21,7 +21,7 @@ import ( ) func TestExpr(t *testing.T) { - var cases = []struct { + cases := []struct { expr string val interface{} }{ @@ -132,7 +132,7 @@ func TestExpr(t *testing.T) { } func TestExprWithEnv(t *testing.T) { - var cases = []struct { + cases := []struct { expr string val interface{} }{ @@ -162,7 +162,7 @@ func TestExprWithEnv(t *testing.T) { } func TestPriority(t *testing.T) { - var cases = []struct { + cases := []struct { expr string val interface{} }{ @@ -190,7 +190,7 @@ func TestPriority(t *testing.T) { } func TestBuiltInFunc(t *testing.T) { - var cases = []struct { + cases := []struct { expr string val interface{} }{ @@ -226,7 +226,7 @@ func TestBuiltInFunc(t *testing.T) { } func TestSyntaxIncorrect(t *testing.T) { - var cases = []struct { + cases := []struct { incorrectExpr string }{ {incorrectExpr: "1 + + 'a'"}, diff --git a/internal/tagexpr/go.mod b/internal/tagexpr/go.mod deleted file mode 100644 index 1c2552325..000000000 --- a/internal/tagexpr/go.mod +++ /dev/null @@ -1,14 +0,0 @@ -module github.com/bytedance/go-tagexpr/v2 - -go 1.14 - -require ( - github.com/andeya/ameda v1.5.3 - github.com/andeya/goutil v1.0.1 - github.com/davecgh/go-spew v1.1.1 - github.com/nyaruka/phonenumbers v1.0.55 - github.com/stretchr/testify v1.7.5 - github.com/tidwall/match v1.1.1 - github.com/tidwall/pretty v1.2.0 - google.golang.org/protobuf v1.27.1 -) diff --git a/internal/tagexpr/go.sum b/internal/tagexpr/go.sum deleted file mode 100644 index 79105e431..000000000 --- a/internal/tagexpr/go.sum +++ /dev/null @@ -1,35 +0,0 @@ -github.com/andeya/ameda v1.5.3 h1:SvqnhQPZwwabS8HQTRGfJwWPl2w9ZIPInHAw9aE1Wlk= -github.com/andeya/ameda v1.5.3/go.mod h1:FQDHRe1I995v6GG+8aJ7UIUToEmbdTJn/U26NCPIgXQ= -github.com/andeya/goutil v1.0.1 h1:eiYwVyAnnK0dXU5FJsNjExkJW4exUGn/xefPt3k4eXg= -github.com/andeya/goutil v1.0.1/go.mod h1:jEG5/QnnhG7yGxwFUX6Q+JGMif7sjdHmmNVjn7nhJDo= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg= -github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q= -github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/tagexpr/handler.go b/internal/tagexpr/handler.go index e77ececcb..a64d6825e 100644 --- a/internal/tagexpr/handler.go +++ b/internal/tagexpr/handler.go @@ -1,3 +1,17 @@ +// Copyright 2019 Bytedance Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tagexpr import "reflect" @@ -29,7 +43,8 @@ func (f *FieldHandler) FieldSelector() FieldSelector { // Value returns the field value. // NOTE: -// If initZero==true, initialize nil pointer to zero value +// +// If initZero==true, initialize nil pointer to zero value func (f *FieldHandler) Value(initZero bool) reflect.Value { return f.field.reflectValueGetter(f.expr.ptr, initZero) } @@ -100,14 +115,16 @@ func (e *ExprHandler) Path() string { // Eval evaluate the value of the struct tag expression. // NOTE: -// result types: float64, string, bool, nil +// +// result types: float64, string, bool, nil func (e *ExprHandler) Eval() interface{} { return e.expr.s.exprs[e.selector].run(e.base, e.targetExpr) } // EvalFloat evaluates the value of the struct tag expression. // NOTE: -// If the expression value type is not float64, return 0. +// +// If the expression value type is not float64, return 0. func (e *ExprHandler) EvalFloat() float64 { r, _ := e.Eval().(float64) return r @@ -115,7 +132,8 @@ func (e *ExprHandler) EvalFloat() float64 { // EvalString evaluates the value of the struct tag expression. // NOTE: -// If the expression value type is not string, return "". +// +// If the expression value type is not string, return "". func (e *ExprHandler) EvalString() string { r, _ := e.Eval().(string) return r @@ -123,7 +141,8 @@ func (e *ExprHandler) EvalString() string { // EvalBool evaluates the value of the struct tag expression. // NOTE: -// If the expression value is not 0, '' or nil, return true. +// +// If the expression value is not 0, '' or nil, return true. func (e *ExprHandler) EvalBool() bool { return FakeBool(e.Eval()) } diff --git a/internal/tagexpr/selector.go b/internal/tagexpr/selector.go index 044380cca..7d9e737b2 100644 --- a/internal/tagexpr/selector.go +++ b/internal/tagexpr/selector.go @@ -1,3 +1,17 @@ +// Copyright 2019 Bytedance Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tagexpr import ( @@ -18,11 +32,6 @@ const ( // FieldSelector expression selector type FieldSelector string -// JoinFieldSelector creates a field selector. -func JoinFieldSelector(path ...string) string { - return strings.Join(path, FieldSeparator) -} - // Name returns the current field name. func (f FieldSelector) Name() string { s := string(f) @@ -59,15 +68,6 @@ func (f FieldSelector) String() string { return string(f) } -// JoinExprSelector creates a expression selector. -func JoinExprSelector(pathFields []string, exprName string) string { - p := strings.Join(pathFields, FieldSeparator) - if p == "" || exprName == "" { - return p - } - return p + ExprNameSeparator + exprName -} - // ExprSelector expression selector type ExprSelector string diff --git a/internal/tagexpr/selector_test.go b/internal/tagexpr/selector_test.go index 393383253..bfb5dd4f7 100644 --- a/internal/tagexpr/selector_test.go +++ b/internal/tagexpr/selector_test.go @@ -1,14 +1,30 @@ +// Copyright 2019 Bytedance Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tagexpr import ( "testing" - - "github.com/stretchr/testify/assert" ) func TestExprSelector(t *testing.T) { es := ExprSelector("F1.Index") field, ok := es.ParentField() - assert.True(t, ok) - assert.Equal(t, "F1", field) + if !ok { + t.Fatal("not ok") + } + if "F1" != field { + t.Fatal(field) + } } diff --git a/internal/tagexpr/spec_func.go b/internal/tagexpr/spec_func.go index 610fc8841..8e6291da8 100644 --- a/internal/tagexpr/spec_func.go +++ b/internal/tagexpr/spec_func.go @@ -20,8 +20,6 @@ import ( "reflect" "regexp" "strings" - - "github.com/andeya/goutil/errors" ) // --------------------------- Custom function --------------------------- @@ -54,7 +52,7 @@ func RegFunc(funcName string, fn func(...interface{}) interface{}, force ...bool if len(force) == 0 || !force[0] { _, ok := funcList[funcName] if ok { - return errors.Errorf("duplicate registration expression function: %s", funcName) + return fmt.Errorf("duplicate registration expression function: %s", funcName) } } funcList[funcName] = newFunc(funcName, fn) @@ -241,7 +239,7 @@ func readRegexpFuncExprNode(p *Expr, expr *string) ExprNode { return nil } } else { - var currFieldVal = "$" + currFieldVal := "$" p.parseExprNode(&currFieldVal, operand) } trimLeftSpace(subExprNode) diff --git a/internal/tagexpr/spec_func_test.go b/internal/tagexpr/spec_func_test.go index d6872f251..3d66a49bc 100644 --- a/internal/tagexpr/spec_func_test.go +++ b/internal/tagexpr/spec_func_test.go @@ -15,15 +15,15 @@ package tagexpr_test import ( + "reflect" "regexp" "testing" - "github.com/bytedance/go-tagexpr/v2" - "github.com/stretchr/testify/assert" + "github.com/cloudwego/hertz/internal/tagexpr" ) func TestFunc(t *testing.T) { - var emailRegexp = regexp.MustCompile( + emailRegexp := regexp.MustCompile( "^([A-Za-z0-9_\\-\\.\u4e00-\u9fa5])+\\@([A-Za-z0-9_\\-\\.])+\\.([A-Za-z]{2,8})$", ) tagexpr.RegFunc("email", func(args ...interface{}) interface{} { @@ -38,12 +38,12 @@ func TestFunc(t *testing.T) { return emailRegexp.MatchString(s) }) - var vm = tagexpr.New("te") + vm := tagexpr.New("te") type T struct { Email string `te:"email($)"` } - var cases = []struct { + cases := []struct { email string expect bool }{ @@ -65,7 +65,7 @@ func TestFunc(t *testing.T) { type R struct { Str string `vd:"mblen($)<6"` } - var lenCases = []struct { + lenCases := []struct { str string expect bool }{ @@ -87,7 +87,7 @@ func TestFunc(t *testing.T) { } func TestRangeIn(t *testing.T) { - var vm = tagexpr.New("te") + vm := tagexpr.New("te") type S struct { F []string `te:"range($, in(#v, '', 'ttp', 'euttp'))"` } @@ -96,5 +96,9 @@ func TestRangeIn(t *testing.T) { F: a, // F: b, }) - assert.Equal(t, []interface{}{true, true, true}, r.Eval("F")) + expect := []interface{}{true, true, true} + actual := r.Eval("F") + if !reflect.DeepEqual(expect, actual) { + t.Fatal("not equal", expect, actual) + } } diff --git a/internal/tagexpr/spec_operand.go b/internal/tagexpr/spec_operand.go index b18e6ce7f..b14b05515 100644 --- a/internal/tagexpr/spec_operand.go +++ b/internal/tagexpr/spec_operand.go @@ -21,8 +21,6 @@ import ( "regexp" "strconv" "strings" - - "github.com/andeya/ameda" ) // --------------------------- Operand --------------------------- @@ -214,9 +212,7 @@ func readVariableExprNode(expr *string) ExprNode { } } - func getBoolAndSignOpposite(expr *string) (last string, boolOpposite *bool, signOpposite *bool) { - last = strings.TrimLeft(last, "+") last, boolOpposite = getOpposite(expr, "!") last = strings.TrimLeft(last, "+") last, signOpposite = getOpposite(&last, "-") @@ -241,7 +237,7 @@ func toString(i interface{}, enforce bool) (string, bool) { case nil: return "", false default: - rv := ameda.DereferenceValue(reflect.ValueOf(i)) + rv := dereferenceValue(reflect.ValueOf(i)) if rv.Kind() == reflect.String { return rv.String(), true } @@ -258,7 +254,7 @@ func toString(i interface{}, enforce bool) (string, bool) { func toFloat64(i interface{}, tryParse bool) (float64, bool) { var v float64 - var ok = true + ok := true switch t := i.(type) { case float64: v = t @@ -287,7 +283,7 @@ func toFloat64(i interface{}, tryParse bool) (float64, bool) { case nil: ok = false default: - rv := ameda.DereferenceValue(reflect.ValueOf(t)) + rv := dereferenceValue(reflect.ValueOf(t)) switch rv.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: v = float64(rv.Int()) @@ -346,7 +342,7 @@ func realValue(v interface{}, boolOpposite *bool, signOpposite *bool) interface{ t[k] = realValue(v, boolOpposite, signOpposite) } default: - rv := ameda.DereferenceValue(reflect.ValueOf(v)) + rv := dereferenceValue(reflect.ValueOf(v)) switch rv.Kind() { case reflect.String: v = rv.String() diff --git a/internal/tagexpr/spec_range.go b/internal/tagexpr/spec_range.go index fecccad60..dcb88722a 100644 --- a/internal/tagexpr/spec_range.go +++ b/internal/tagexpr/spec_range.go @@ -1,3 +1,17 @@ +// Copyright 2019 Bytedance Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tagexpr import ( diff --git a/internal/tagexpr/spec_range_test.go b/internal/tagexpr/spec_range_test.go index 3e209df79..bc4f73a36 100644 --- a/internal/tagexpr/spec_range_test.go +++ b/internal/tagexpr/spec_range_test.go @@ -1,14 +1,28 @@ +// Copyright 2019 Bytedance Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tagexpr_test import ( + "reflect" "testing" - "github.com/bytedance/go-tagexpr/v2" - "github.com/stretchr/testify/assert" + "github.com/cloudwego/hertz/internal/tagexpr" ) func TestIssue12(t *testing.T) { - var vm = tagexpr.New("te") + vm := tagexpr.New("te") type I int type S struct { F []I `te:"range($, '>'+sprintf('%v:%v', #k, #v+2+len($)))"` @@ -25,10 +39,16 @@ func TestIssue12(t *testing.T) { MFs: []map[string][]I{{"m": a}}, MFs2: []map[string][]I{}, }) - assert.Equal(t, []interface{}{">0:6", ">1:7"}, r.Eval("F")) - assert.Equal(t, []interface{}{[]interface{}{">0:6", ">1:7"}}, r.Eval("Fs")) - assert.Equal(t, []interface{}{">m0:6", ">m1:7"}, r.Eval("M")) - assert.Equal(t, []interface{}{[]interface{}{[]interface{}{">0:6", ">1:7"}}}, r.Eval("MFs")) - assert.Equal(t, []interface{}{}, r.Eval("MFs2")) - assert.Equal(t, true, r.EvalBool("MFs2")) + assertEqual(t, []interface{}{">0:6", ">1:7"}, r.Eval("F")) + assertEqual(t, []interface{}{[]interface{}{">0:6", ">1:7"}}, r.Eval("Fs")) + assertEqual(t, []interface{}{[]interface{}{[]interface{}{">0:6", ">1:7"}}}, r.Eval("MFs")) + assertEqual(t, []interface{}{}, r.Eval("MFs2")) + assertEqual(t, true, r.EvalBool("MFs2")) + + // result may not stable for map + got := r.Eval("M") + if !reflect.DeepEqual([]interface{}{">m0:6", ">m1:7"}, got) && + !reflect.DeepEqual([]interface{}{">m1:7", ">m0:6"}, got) { + t.Fatal(got) + } } diff --git a/internal/tagexpr/spec_test.go b/internal/tagexpr/spec_test.go index 6bc7f857f..07f93d1ec 100644 --- a/internal/tagexpr/spec_test.go +++ b/internal/tagexpr/spec_test.go @@ -16,13 +16,12 @@ package tagexpr import ( "context" - "fmt" "reflect" "testing" ) func TestReadPairedSymbol(t *testing.T) { - var cases = []struct { + cases := []struct { left, right rune expr, val, lastExprNode string }{ @@ -45,7 +44,7 @@ func TestReadPairedSymbol(t *testing.T) { } func TestReadBoolExprNode(t *testing.T) { - var cases = []struct { + cases := []struct { expr string val bool lastExprNode string @@ -69,7 +68,7 @@ func TestReadBoolExprNode(t *testing.T) { } func TestReadDigitalExprNode(t *testing.T) { - var cases = []struct { + cases := []struct { expr string val float64 lastExprNode string @@ -98,7 +97,7 @@ func TestReadDigitalExprNode(t *testing.T) { } func TestFindSelector(t *testing.T) { - var cases = []struct { + cases := []struct { expr string field string name string @@ -161,10 +160,3 @@ func TestFindSelector(t *testing.T) { } } } -func printBoolPtr(b *bool) string { - var v interface{} = b - if b != nil { - v = *b - } - return fmt.Sprint(v) -} diff --git a/internal/tagexpr/tagexpr.go b/internal/tagexpr/tagexpr.go index 288f3510b..a41e435d0 100644 --- a/internal/tagexpr/tagexpr.go +++ b/internal/tagexpr/tagexpr.go @@ -1,5 +1,3 @@ -// Package tagexpr is an interesting go struct tag expression syntax for field validation, etc. -// // Copyright 2019 Bytedance Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,6 +11,8 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + +// Package tagexpr is an interesting go struct tag expression syntax for field validation, etc. package tagexpr import ( @@ -23,8 +23,6 @@ import ( "strings" "sync" "unsafe" - - "github.com/andeya/ameda" ) // Internally unified data types @@ -98,8 +96,8 @@ func (vm *VM) MustRun(structOrStructPtrOrReflectValue interface{}) *TagExpr { } var ( - unsupportNil = errors.New("unsupport data: nil") - unsupportCannotAddr = errors.New("unsupport data: can not addr") + unsupportedNil = errors.New("unsupported data: nil") + unsupportedCannotAddr = errors.New("unsupported data: can not addr") ) // Run returns the tag expression handler of the @structPtrOrReflectValue. @@ -115,21 +113,20 @@ func (vm *VM) Run(structPtrOrReflectValue interface{}) (*TagExpr, error) { var v reflect.Value switch t := structPtrOrReflectValue.(type) { case reflect.Value: - v = ameda.DereferenceValue(t) + v = dereferenceValue(t) default: - v = ameda.DereferenceValue(reflect.ValueOf(t)) + v = dereferenceValue(reflect.ValueOf(t)) } if err := checkStructMapAddr(v); err != nil { return nil, err } - u := ameda.ValueFrom2(&v) - ptr := unsafe.Pointer(u.Pointer()) + ptr := rvPtr(v) if ptr == nil { - return nil, unsupportNil + return nil, unsupportedNil } - tid := u.RuntimeTypeID() + tid := rvType(v) var err error vm.rw.RLock() s, ok := vm.structJar[tid] @@ -171,16 +168,16 @@ func checkStructMapAddr(v reflect.Value) error { if !v.IsValid() || v.CanAddr() || v.NumField() != 1 || v.Field(0).Kind() != reflect.Map { return nil } - return unsupportCannotAddr + return unsupportedCannotAddr } func (vm *VM) subRunAll(omitNil bool, tePath string, value reflect.Value, fn func(*TagExpr, error) error) error { - rv := ameda.DereferenceInterfaceValue(value) + rv := dereferenceInterfaceValue(value) if !rv.IsValid() { return nil } - rt := ameda.DereferenceType(rv.Type()) - rv = ameda.DereferenceValue(rv) + rt := dereferenceType(rv.Type()) + rv = dereferenceValue(rv) switch rt.Kind() { case reflect.Struct: if len(tePath) == 0 { @@ -188,22 +185,21 @@ func (vm *VM) subRunAll(omitNil bool, tePath string, value reflect.Value, fn fun return err } } - u := ameda.ValueFrom2(&rv) - ptr := unsafe.Pointer(u.Pointer()) + ptr := rvPtr(rv) if ptr == nil { if omitNil { return nil } - return fn(nil, unsupportNil) + return fn(nil, unsupportedNil) } - return fn(vm.subRun(tePath, rt, u.RuntimeTypeID(), ptr)) + return fn(vm.subRun(tePath, rt, rvType(rv), ptr)) case reflect.Slice, reflect.Array: count := rv.Len() if count == 0 { return nil } - switch ameda.DereferenceType(rv.Type().Elem()).Kind() { + switch dereferenceType(rv.Type().Elem()).Kind() { case reflect.Struct, reflect.Interface, reflect.Slice, reflect.Array, reflect.Map: for i := count - 1; i >= 0; i-- { err := vm.subRunAll(omitNil, tePath+"["+strconv.Itoa(i)+"]", rv.Index(i), fn) @@ -221,11 +217,11 @@ func (vm *VM) subRunAll(omitNil bool, tePath string, value reflect.Value, fn fun } var canKey, canValue bool rt := rv.Type() - switch ameda.DereferenceType(rt.Key()).Kind() { + switch dereferenceType(rt.Key()).Kind() { case reflect.Struct, reflect.Interface, reflect.Slice, reflect.Array, reflect.Map: canKey = true } - switch ameda.DereferenceType(rt.Elem()).Kind() { + switch dereferenceType(rt.Elem()).Kind() { case reflect.Struct, reflect.Interface, reflect.Slice, reflect.Array, reflect.Map: canValue = true } @@ -278,7 +274,7 @@ func (vm *VM) registerStructLocked(structType reflect.Type) (*structVM, error) { if err != nil { return nil, err } - tid := ameda.RuntimeTypeID(structType) + tid := rtType(structType) s, had := vm.structJar[tid] if had { return s, s.err @@ -286,7 +282,7 @@ func (vm *VM) registerStructLocked(structType reflect.Type) (*structVM, error) { s = vm.newStructVM() s.name = structType.String() vm.structJar[tid] = s - var numField = structType.NumField() + numField := structType.NumField() var structField reflect.StructField var sub *structVM for i := 0; i < numField; i++ { @@ -302,7 +298,7 @@ func (vm *VM) registerStructLocked(structType reflect.Type) (*structVM, error) { } switch field.elemKind { default: - field.setUnsupportGetter() + field.setUnsupportedGetter() switch field.elemKind { case reflect.Struct: sub, err = vm.registerStructLocked(field.structField.Type) @@ -420,7 +416,7 @@ func (vm *VM) newStructVM() *structVM { } func (s *structVM) newFieldVM(structField reflect.StructField) (*fieldVM, bool, error) { - var tag = structField.Tag.Get(s.vm.tagName) + tag := structField.Tag.Get(s.vm.tagName) if tag == tagOmit { return nil, false, nil } @@ -437,7 +433,7 @@ func (s *structVM) newFieldVM(structField reflect.StructField) (*fieldVM, bool, s.fields[f.fieldSelector] = f s.fieldSelectorList = append(s.fieldSelectorList, f.fieldSelector) - var t = structField.Type + t := structField.Type var ptrDeep int for t.Kind() == reflect.Ptr { t = t.Elem() @@ -445,7 +441,7 @@ func (s *structVM) newFieldVM(structField reflect.StructField) (*fieldVM, bool, } f.ptrDeep = ptrDeep - var offset = structField.Offset + offset := structField.Offset f.getPtr = func(ptr unsafe.Pointer) unsafe.Pointer { if ptr == nil { return nil @@ -701,7 +697,7 @@ func (f *fieldVM) setLengthGetter() { } } -func (f *fieldVM) setUnsupportGetter() { +func (f *fieldVM) setUnsupportedGetter() { f.valueGetter = func(ptr unsafe.Pointer) interface{} { raw := f.packRawFrom(ptr) if safeIsNil(raw) { @@ -724,7 +720,7 @@ func (vm *VM) getStructType(t reflect.Type) (reflect.Type, error) { structType = structType.Elem() } if structType.Kind() != reflect.Struct { - return nil, fmt.Errorf("unsupport type: %s", t.String()) + return nil, fmt.Errorf("unsupported type: %s", t.String()) } return structType, nil } @@ -807,13 +803,13 @@ func FakeBool(v interface{}) bool { case nil, error: return false case []interface{}: - var bol = true + bol := true for _, v := range r { bol = bol && FakeBool(v) } return bol default: - vv := ameda.DereferenceValue(reflect.ValueOf(v)) + vv := dereferenceValue(reflect.ValueOf(v)) if vv.IsValid() || vv.IsZero() { return false } @@ -877,7 +873,7 @@ func (t *TagExpr) Eval(exprSelector string) interface{} { // // format: fieldName, fieldName.exprName, fieldName1.fieldName2.exprName1 // result types: float64, string, bool, nil -func (t *TagExpr) EvalWithEnv(exprSelector string, env map[string]interface{})interface{} { +func (t *TagExpr) EvalWithEnv(exprSelector string, env map[string]interface{}) interface{} { expr, ok := t.s.exprs[exprSelector] if !ok { // Compatible with single mode or the expression with the name @ @@ -940,7 +936,7 @@ func (t *TagExpr) Range(fn func(*ExprHandler) error) error { keyPath := f.fieldSelector + "{k}" for _, key := range v.MapKeys() { if mapKeyStructVM != nil { - p := unsafe.Pointer(ameda.ValueFrom(derefValue(key)).Pointer()) + p := rvPtr(derefValue(key)) if omitNil && p == nil { continue } @@ -955,7 +951,7 @@ func (t *TagExpr) Range(fn func(*ExprHandler) error) error { } } if mapOrSliceElemStructVM != nil { - p := unsafe.Pointer(ameda.ValueFrom(derefValue(v.MapIndex(key))).Pointer()) + p := rvPtr(derefValue(v.MapIndex(key))) if omitNil && p == nil { continue } @@ -975,7 +971,7 @@ func (t *TagExpr) Range(fn func(*ExprHandler) error) error { // slice or array for i := v.Len() - 1; i >= 0; i-- { if mapOrSliceElemStructVM != nil { - p := unsafe.Pointer(ameda.ValueFrom(derefValue(v.Index(i))).Pointer()) + p := rvPtr(derefValue(v.Index(i))) if omitNil && p == nil { continue } diff --git a/internal/tagexpr/tagexpr_test.go b/internal/tagexpr/tagexpr_test.go index 4c316f763..d571c4106 100644 --- a/internal/tagexpr/tagexpr_test.go +++ b/internal/tagexpr/tagexpr_test.go @@ -12,26 +12,35 @@ // See the License for the specific language governing permissions and // limitations under the License. -package tagexpr +package tagexpr_test import ( + "fmt" "reflect" "strconv" "testing" "time" - "github.com/stretchr/testify/assert" + "github.com/cloudwego/hertz/internal/tagexpr" ) +func assertEqual(t *testing.T, v1, v2 interface{}, msgs ...interface{}) { + t.Helper() + if reflect.DeepEqual(v1, v2) { + return + } + t.Fatal(fmt.Sprintf("not equal %v %v", v1, v2) + "\n" + fmt.Sprint(msgs...)) +} + func BenchmarkTagExpr(b *testing.B) { type T struct { a int `bench:"$%3"` } - vm := New("bench") + vm := tagexpr.New("bench") vm.MustRun(new(T)) // warm up b.ReportAllocs() b.ResetTimer() - var t = &T{10} + t := &T{10} for i := 0; i < b.N; i++ { tagExpr, err := vm.Run(t) if err != nil { @@ -49,7 +58,7 @@ func BenchmarkReflect(b *testing.B) { } b.ReportAllocs() b.ResetTimer() - var t = &T{1} + t := &T{1} for i := 0; i < b.N; i++ { v := reflect.ValueOf(t).Elem() ft, ok := v.Type().FieldByName("a") @@ -82,7 +91,7 @@ func Test(t *testing.T) { e := new(int) *e = 3 type iface interface{} - var cases = []struct { + cases := []struct { tagName string structure interface{} tests map[string]interface{} @@ -214,7 +223,7 @@ func Test(t *testing.T) { }, } for i, c := range cases { - vm := New(c.tagName) + vm := tagexpr.New(c.tagName) // vm.WarmUp(c.structure) tagExpr, err := vm.Run(c.structure) if err != nil { @@ -226,7 +235,7 @@ func Test(t *testing.T) { t.Fatalf("Eval Serial: %d, selector: %q, got: %v, expect: %v", i, selector, val, value) } } - tagExpr.Range(func(eh *ExprHandler) error { + tagExpr.Range(func(eh *tagexpr.ExprHandler) error { es := eh.ExprSelector() t.Logf("Range selector: %s, field: %q exprName: %q", es, es.Field(), es.Name()) value := c.tests[es.String()] @@ -283,7 +292,7 @@ func TestFieldNotInit(t *testing.T) { g: &g, i: "12", } - vm := New("expr") + vm := tagexpr.New("expr") e, err := vm.Run(structure) if err != nil { t.Fatal(err) @@ -315,15 +324,17 @@ func TestFieldNotInit(t *testing.T) { for _, c := range cases { fh, _ := e.Field(c.fieldSelector) val := fh.Value(false).Interface() - assert.Equal(t, c.value, val, c.fieldSelector) + assertEqual(t, c.value, val, c.fieldSelector) } var i int - e.RangeFields(func(fh *FieldHandler) bool { + e.RangeFields(func(fh *tagexpr.FieldHandler) bool { val := fh.Value(false).Interface() if fh.StringSelector() == "c.d" { - assert.NotNil(t, fh.EvalFuncs()["c.d@test"]) + if fh.EvalFuncs()["c.d@test"] == nil { + t.Fatal("nil") + } } - assert.Equal(t, cases[i].value, val, fh.StringSelector()) + assertEqual(t, cases[i].value, val, fh.StringSelector()) i++ return true }) @@ -392,7 +403,7 @@ func TestFieldInitZero(t *testing.T) { i: "12", } - vm := New("") + vm := tagexpr.New("") e, err := vm.Run(structure) if err != nil { t.Fatal(err) @@ -429,15 +440,14 @@ func TestFieldInitZero(t *testing.T) { for _, c := range cases { fh, _ := e.Field(c.fieldSelector) val := fh.Value(true).Interface() - assert.Equal(t, c.value, val, c.fieldSelector) + assertEqual(t, c.value, val, c.fieldSelector) } } func TestOperator(t *testing.T) { - type Tmp1 struct { - A string `tagexpr:$=="1"||$=="2"||$="3"` - B []int `tagexpr:len($)>=10&&$[0]<10` + A string `tagexpr:$=="1"||$=="2"||$="3"` //nolint:govet + B []int `tagexpr:len($)>=10&&$[0]<10` //nolint:govet C interface{} } @@ -468,7 +478,7 @@ func TestOperator(t *testing.T) { k := "123456" n := float64(-12.5) o := [3]float64{15, 9, 9} - var cases = []struct { + cases := []struct { tagName string structure interface{} tests map[string]interface{} @@ -531,7 +541,7 @@ func TestOperator(t *testing.T) { } for i, c := range cases { - vm := New(c.tagName) + vm := tagexpr.New(c.tagName) // vm.WarmUp(c.structure) tagExpr, err := vm.Run(c.structure) if err != nil { @@ -543,7 +553,7 @@ func TestOperator(t *testing.T) { t.Fatalf("Eval NO: %d, selector: %q, got: %v, expect: %v", i, selector, val, value) } } - tagExpr.Range(func(eh *ExprHandler) error { + tagExpr.Range(func(eh *tagexpr.ExprHandler) error { es := eh.ExprSelector() t.Logf("Range selector: %s, field: %q exprName: %q", es, es.Field(), es.Name()) value := c.tests[es.String()] @@ -554,7 +564,6 @@ func TestOperator(t *testing.T) { return nil }) } - } func TestStruct(t *testing.T) { @@ -571,13 +580,13 @@ func TestStruct(t *testing.T) { } a := new(A) a.B.C.D.X = "xxx" - vm := New("vd") + vm := tagexpr.New("vd") expr := vm.MustRun(a) - assert.Equal(t, "xxx", expr.EvalString("B.C2")) - assert.Equal(t, "xxx", expr.EvalString("B.C3")) - assert.Equal(t, "xxx", expr.EvalString("B.C")) - assert.Equal(t, "xxx", expr.EvalString("B.C.D.X")) - expr.Range(func(eh *ExprHandler) error { + assertEqual(t, "xxx", expr.EvalString("B.C2")) + assertEqual(t, "xxx", expr.EvalString("B.C3")) + assertEqual(t, "xxx", expr.EvalString("B.C")) + assertEqual(t, "xxx", expr.EvalString("B.C.D.X")) + expr.Range(func(eh *tagexpr.ExprHandler) error { es := eh.ExprSelector() t.Logf("Range selector: %s, field: %q exprName: %q", es, es.Field(), es.Name()) if eh.Eval().(string) != "xxx" { @@ -601,7 +610,7 @@ func TestStruct2(t *testing.T) { b := new(IframeBlock) b.XBlock.BlockType = "BlockType" b.Props.Data.DataType = "DataType" - vm := New("vd") + vm := tagexpr.New("vd") expr := vm.MustRun(b) if expr.EvalString("XBlock.BlockType") != "BlockType" { t.Fatal(expr.EvalString("XBlock.BlockType")) @@ -617,8 +626,8 @@ func TestStruct3(t *testing.T) { } type Prop struct { PropType string `vd:"$"` - Datas []*Data `vd:"$"` - Datas2 []*Data `vd:"$"` + DD []*Data `vd:"$"` + DD2 []*Data `vd:"$"` DataMap map[int]Data `vd:"$"` DataMap2 map[int]Data `vd:"$"` } @@ -637,7 +646,7 @@ func TestStruct3(t *testing.T) { b.XBlock.BlockType = "BlockType" p1 := Prop{ PropType: "p1", - Datas: []*Data{ + DD: []*Data{ {"p1s1"}, {"p1s2"}, nil, @@ -645,13 +654,13 @@ func TestStruct3(t *testing.T) { DataMap: map[int]Data{ 1: {"p1m1"}, 2: {"p1m2"}, - 0: Data{}, + 0: {}, }, } b.Props = []Prop{p1} p2 := &Prop{ PropType: "p2", - Datas: []*Data{ + DD: []*Data{ {"p2s1"}, {"p2s2"}, nil, @@ -659,25 +668,27 @@ func TestStruct3(t *testing.T) { DataMap: map[int]Data{ 1: {"p2m1"}, 2: {"p2m2"}, - 0: Data{}, + 0: {}, }, } - b.Props1 = [2]Prop{p1, Prop{}} + b.Props1 = [2]Prop{p1, {}} b.PropMap = map[int]*Prop{ 9: p2, } - vm := New("vd") + vm := tagexpr.New("vd") expr := vm.MustRun(b) if expr.EvalString("XBlock.BlockType") != "BlockType" { t.Fatal(expr.EvalString("XBlock.BlockType")) } - err := expr.Range(func(eh *ExprHandler) error { + err := expr.Range(func(eh *tagexpr.ExprHandler) error { es := eh.ExprSelector() t.Logf("Range selector: %s, field: %q exprName: %q, eval: %v", eh.Path(), es.Field(), es.Name(), eh.Eval()) return nil }) - assert.NoError(t, err) + if err != nil { + t.Fatal(err) + } } func TestNilField(t *testing.T) { @@ -687,11 +698,13 @@ func TestNilField(t *testing.T) { } `tagexpr:"$"` Y **struct{} `tagexpr:"$"` } - vm := New("tagexpr") + vm := tagexpr.New("tagexpr") te := vm.MustRun(&P{}) - te.Range(func(eh *ExprHandler) error { + te.Range(func(eh *tagexpr.ExprHandler) error { r := eh.Eval() - assert.Nil(t, r, eh.Path()) + if r != nil { + t.Fatal(eh.Path()) + } return nil }) @@ -703,48 +716,13 @@ func TestNilField(t *testing.T) { // Nil1: new(int), Nil2: new(int), } - vm.MustRun(g).Range(func(eh *ExprHandler) error { + vm.MustRun(g).Range(func(eh *tagexpr.ExprHandler) error { r, ok := eh.Eval().(bool) - assert.True(t, ok, eh.Path()) - assert.True(t, r, eh.Path()) - return nil - }) - return - - type ( - N struct { - X string `tagexpr:"len($)>0"` - S []*N `tagexpr:"?"` - M map[string]*N `tagexpr:"?"` - M2 map[string]*N `tagexpr:"?"` - I interface{} `tagexpr:"-"` - MI map[string]interface{} `tagexpr:"?"` - SI []interface{} - *N `tagexpr:"?"` - N2 *N `tagexpr:"?"` - } - M struct { - X string `tagexpr:"len($)>0"` + if !ok || !r { + t.Fatal(eh.Path()) } - ) - n := &N{ - X: "n", - S: []*N{nil}, - M: map[string]*N{"": nil}, - M2: map[string]*N{"": {X: "nn"}}, - I: new(N), - MI: map[string]interface{}{"": (*M)(nil)}, - SI: []interface{}{&M{X: "nn"}}, - } - var cnt int - vm.MustRun(n).Range(func(eh *ExprHandler) error { - r := eh.EvalBool() - assert.True(t, r, eh.Path()) - t.Log("path:", eh.Path(), "es:", eh.ExprSelector(), "val:", r) - cnt++ return nil }) - assert.Equal(t, 3, cnt) } func TestDeepNested(t *testing.T) { @@ -780,15 +758,15 @@ func TestDeepNested(t *testing.T) { expectKey := [...]interface{}{"S1.S.I.Address@name", "S2.S.I.Address@name", "S1.S.A[0].Address@name", "S2.S.A[0].Address@name", "S1.S.X[0].Address@name"} expectValue := [...]interface{}{"I:address", nil, "A:address", nil, "X:address"} var i int - vm := New("tagexpr") - vm.MustRun(data).Range(func(eh *ExprHandler) error { - assert.Equal(t, expectKey[i], eh.Path()) - assert.Equal(t, expectValue[i], eh.Eval()) + vm := tagexpr.New("tagexpr") + vm.MustRun(data).Range(func(eh *tagexpr.ExprHandler) error { + assertEqual(t, expectKey[i], eh.Path()) + assertEqual(t, expectValue[i], eh.Eval()) i++ t.Log(eh.Path(), eh.ExprSelector(), eh.Eval()) return nil }) - assert.Equal(t, 5, i) + assertEqual(t, 5, i) } func TestIssue3(t *testing.T) { @@ -808,21 +786,23 @@ func TestIssue3(t *testing.T) { P: new(int), }, } - vm := New("vd") - err := vm.MustRun(a).Range(func(eh *ExprHandler) error { + vm := tagexpr.New("vd") + err := vm.MustRun(a).Range(func(eh *tagexpr.ExprHandler) error { switch eh.Path() { case "F1.Index": - assert.Equal(t, float64(1), eh.Eval(), eh.Path()) + assertEqual(t, float64(1), eh.Eval(), eh.Path()) case "F2.Index": - assert.Equal(t, nil, eh.Eval(), eh.Path()) + assertEqual(t, nil, eh.Eval(), eh.Path()) case "F1.P": - assert.Equal(t, true, eh.Eval(), eh.Path()) + assertEqual(t, true, eh.Eval(), eh.Path()) case "F2.P": - assert.Equal(t, false, eh.Eval(), eh.Path()) + assertEqual(t, false, eh.Eval(), eh.Path()) } return nil }) - assert.NoError(t, err) + if err != nil { + t.Fatal(err) + } } func TestIssue4(t *testing.T) { @@ -836,12 +816,14 @@ func TestIssue4(t *testing.T) { B: new(string), C: &c, } - vm := New("te") - err := vm.MustRun(v).Range(func(eh *ExprHandler) error { + vm := tagexpr.New("te") + err := vm.MustRun(v).Range(func(eh *tagexpr.ExprHandler) error { t.Logf("eval:%v, path:%s", eh.EvalFloat(), eh.Path()) return nil }) - assert.NoError(t, err) + if err != nil { + t.Fatal(err) + } } func TestIssue5(t *testing.T) { @@ -855,17 +837,19 @@ func TestIssue5(t *testing.T) { F2: 1500, F3: 1500, } - vm := New("vd") - err := vm.MustRun(a).Range(func(eh *ExprHandler) error { + vm := tagexpr.New("vd") + err := vm.MustRun(a).Range(func(eh *tagexpr.ExprHandler) error { switch eh.Path() { case "F1": - assert.Equal(t, true, eh.Eval(), eh.Path()) + assertEqual(t, true, eh.Eval(), eh.Path()) case "F2": - assert.Equal(t, true, eh.Eval(), eh.Path()) + assertEqual(t, true, eh.Eval(), eh.Path()) case "F3": - assert.Equal(t, true, eh.Eval(), eh.Path()) + assertEqual(t, true, eh.Eval(), eh.Path()) } return nil }) - assert.NoError(t, err) + if err != nil { + t.Fatal(err) + } } diff --git a/internal/tagexpr/tagparser.go b/internal/tagexpr/tagparser.go index 864d85a4f..fd9547845 100644 --- a/internal/tagexpr/tagparser.go +++ b/internal/tagexpr/tagparser.go @@ -1,3 +1,17 @@ +// Copyright 2019 Bytedance Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tagexpr import ( @@ -6,11 +20,6 @@ import ( "unicode" ) -type namedTagExpr struct { - exprSelector string - expr *Expr -} - const ( tagOmit = "-" tagOmitNil = "?" @@ -99,7 +108,7 @@ func splitExpr(one string) (key, val string) { } func readOneExpr(tag *string) (string, error) { - var s = *(trimRightSpace(trimLeftSpace(tag))) + s := *(trimRightSpace(trimLeftSpace(tag))) s = strings.TrimLeft(s, ";") if s == "" { return "", nil @@ -108,7 +117,7 @@ func readOneExpr(tag *string) (string, error) { s += ";" } a := strings.SplitAfter(strings.Replace(s, "\\'", "##", -1), ";") - var idx = -1 + idx := -1 var patch int for _, v := range a { idx += len(v) @@ -140,16 +149,16 @@ func readPairedSymbol(p *string, left, right rune) *string { return nil } s = s[1:] - var last1 = left + last1 := left var last2 rune var leftLevel, rightLevel int - var escapeIndexes = make(map[int]bool) + escapeIndexes := make(map[int]bool) var realEqual, escapeEqual bool for i, r := range s { if realEqual, escapeEqual = equalRune(right, r, last1, last2); realEqual { if leftLevel == rightLevel { *p = s[i+1:] - var sub = make([]rune, 0, i) + sub := make([]rune, 0, i) for k, v := range s[:i] { if !escapeIndexes[k] { sub = append(sub, v) diff --git a/internal/tagexpr/tagparser_test.go b/internal/tagexpr/tagparser_test.go index aa7f4cc55..5f36fb80e 100644 --- a/internal/tagexpr/tagparser_test.go +++ b/internal/tagexpr/tagparser_test.go @@ -1,10 +1,22 @@ +// Copyright 2019 Bytedance Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package tagexpr import ( "reflect" "testing" - - "github.com/stretchr/testify/assert" ) func TestTagparser(t *testing.T) { @@ -68,9 +80,11 @@ func TestTagparser(t *testing.T) { for _, c := range cases { r, e := parseTag(c.tag.Get("tagexpr")) if e != nil == c.fail { - assert.Equal(t, c.expect, r, c.tag) + if !reflect.DeepEqual(c.expect, r) { + t.Fatal(c.expect, r, c.tag) + } } else { - assert.Failf(t, string(c.tag), "kvs:%v, err:%v", r, e) + t.Fatalf("tag:%s kvs:%v, err:%v", c.tag, r, e) } if e != nil { t.Logf("tag:%q, errMsg:%v", c.tag, e) diff --git a/internal/tagexpr/utils.go b/internal/tagexpr/utils.go new file mode 100644 index 000000000..11b3ca622 --- /dev/null +++ b/internal/tagexpr/utils.go @@ -0,0 +1,101 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tagexpr + +import ( + "reflect" + "unsafe" +) + +func init() { + testhack() +} + +func dereferenceValue(v reflect.Value) reflect.Value { + for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + v = v.Elem() + } + return v +} + +func dereferenceType(t reflect.Type) reflect.Type { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t +} + +func dereferenceInterfaceValue(v reflect.Value) reflect.Value { + for v.Kind() == reflect.Interface { + v = v.Elem() + } + return v +} + +type rvtype struct { // reflect.Value + abiType uintptr + ptr unsafe.Pointer // data pointer +} + +func rvPtr(rv reflect.Value) unsafe.Pointer { + return (*rvtype)(unsafe.Pointer(&rv)).ptr +} + +func rvType(rv reflect.Value) uintptr { + return (*rvtype)(unsafe.Pointer(&rv)).abiType +} + +func rtType(rt reflect.Type) uintptr { + type iface struct { + tab uintptr + data uintptr + } + return (*iface)(unsafe.Pointer(&rt)).data +} + +// quick test make sure the hack above works +func testhack() { + type T1 struct { + a int + } + type T2 struct { + a int + } + p0 := &T1{1} + p1 := &T1{2} + p2 := &T2{3} + + if rvPtr(reflect.ValueOf(p0)) != unsafe.Pointer(p0) || + rvPtr(reflect.ValueOf(p0).Elem()) != unsafe.Pointer(p0) || + rvPtr(reflect.ValueOf(p0)) == rvPtr(reflect.ValueOf(p1)) { + panic("rvPtr() compatibility issue found") + } + + if rvType(reflect.ValueOf(p0)) != rvType(reflect.ValueOf(p1)) || + rvType(reflect.ValueOf(p0)) == rvType(reflect.ValueOf(p2)) || + rvType(reflect.ValueOf(p0).Elem()) != rvType(reflect.ValueOf(p1).Elem()) || + rvType(reflect.ValueOf(p0).Elem()) == rvType(reflect.ValueOf(p2).Elem()) { + panic("rvType() compatibility issue found") + } + + if rtType(reflect.TypeOf(p0)) != rtType(reflect.TypeOf(p1)) || + rtType(reflect.TypeOf(p0)) == rtType(reflect.TypeOf(p2)) || + rtType(reflect.TypeOf(p0).Elem()) != rtType(reflect.TypeOf(p1).Elem()) || + rtType(reflect.TypeOf(p0).Elem()) == rtType(reflect.TypeOf(p2).Elem()) { + panic("rtType() compatibility issue found") + } +} diff --git a/internal/tagexpr/validator/README.md b/internal/tagexpr/validator/README.md index b35df4d22..b3321a671 100644 --- a/internal/tagexpr/validator/README.md +++ b/internal/tagexpr/validator/README.md @@ -119,7 +119,7 @@ func Example() { // {"succ":false, "error":"validation failed: A[0]{v for k=x}.f.g"} // {"succ":false, "error":"validation failed: {k}.f.g"} // {"succ":false, "error":"validation failed: [0][0].f.g"} - // unsupport data: nil + // unsupported data: nil // // // diff --git a/internal/tagexpr/validator/default.go b/internal/tagexpr/validator/default.go index 0b34f0421..667b5f5cf 100644 --- a/internal/tagexpr/validator/default.go +++ b/internal/tagexpr/validator/default.go @@ -1,25 +1,42 @@ +// Copyright 2019 Bytedance Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package validator var defaultValidator = New("vd").SetErrorFactory(defaultErrorFactory) // Default returns the default validator. // NOTE: -// The tag name is 'vd' +// +// The tag name is 'vd' func Default() *Validator { return defaultValidator } // Validate uses the default validator to validate whether the fields of value is valid. // NOTE: -// The tag name is 'vd' -// If checkAll=true, validate all the error. +// +// The tag name is 'vd' +// If checkAll=true, validate all the error. func Validate(value interface{}, checkAll ...bool) error { return defaultValidator.Validate(value, checkAll...) } // SetErrorFactory customizes the factory of validation error for the default validator. // NOTE: -// The tag name is 'vd' +// +// The tag name is 'vd' func SetErrorFactory(errFactory func(fieldSelector, msg string) error) { defaultValidator.SetErrorFactory(errFactory) } diff --git a/internal/tagexpr/validator/example_test.go b/internal/tagexpr/validator/example_test.go index 9318566dc..0c1788787 100644 --- a/internal/tagexpr/validator/example_test.go +++ b/internal/tagexpr/validator/example_test.go @@ -1,9 +1,23 @@ +// Copyright 2019 Bytedance Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package validator_test import ( "fmt" - vd "github.com/bytedance/go-tagexpr/v2/validator" + vd "github.com/cloudwego/hertz/internal/tagexpr/validator" ) func Example() { @@ -100,7 +114,7 @@ func Example() { // {"succ":false, "error":"validation failed: A[0]{v for k=x}.f.g"} // {"succ":false, "error":"validation failed: {k}.f.g"} // {"succ":false, "error":"validation failed: [0][0].f.g"} - // unsupport data: nil + // unsupported data: nil // // // diff --git a/internal/tagexpr/validator/func.go b/internal/tagexpr/validator/func.go index fedcd36ed..17800cc65 100644 --- a/internal/tagexpr/validator/func.go +++ b/internal/tagexpr/validator/func.go @@ -1,3 +1,17 @@ +// Copyright 2019 Bytedance Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package validator import ( @@ -6,7 +20,7 @@ import ( "github.com/nyaruka/phonenumbers" - "github.com/bytedance/go-tagexpr/v2" + "github.com/cloudwego/hertz/internal/tagexpr" ) // ErrInvalidWithoutMsg verification error without error message. @@ -46,7 +60,7 @@ func RegFunc(funcName string, fn func(args ...interface{}) error, force ...bool) } func init() { - var pattern = "^([A-Za-z0-9_\\-\\.\u4e00-\u9fa5])+\\@([A-Za-z0-9_\\-\\.])+\\.([A-Za-z]{2,8})$" + pattern := "^([A-Za-z0-9_\\-\\.\u4e00-\u9fa5])+\\@([A-Za-z0-9_\\-\\.])+\\.([A-Za-z]{2,8})$" emailRegexp := regexp.MustCompile(pattern) MustRegFunc("email", func(args ...interface{}) error { if len(args) != 1 { diff --git a/internal/tagexpr/validator/validator.go b/internal/tagexpr/validator/validator.go index 577bffc3a..2c57010b5 100644 --- a/internal/tagexpr/validator/validator.go +++ b/internal/tagexpr/validator/validator.go @@ -6,7 +6,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -22,7 +22,7 @@ import ( "strings" _ "unsafe" - tagexpr "github.com/bytedance/go-tagexpr/v2" + "github.com/cloudwego/hertz/internal/tagexpr" ) const ( @@ -55,13 +55,14 @@ func (v *Validator) VM() *tagexpr.VM { // Validate validates whether the fields of value is valid. // NOTE: -// If checkAll=true, validate all the error. +// +// If checkAll=true, validate all the error. func (v *Validator) Validate(value interface{}, checkAll ...bool) error { var all bool if len(checkAll) > 0 { all = checkAll[0] } - var errs = make([]error, 0, 8) + errs := make([]error, 0, 8) err := v.vm.RunAny(value, func(te *tagexpr.TagExpr, err error) error { if err != nil { errs = append(errs, err) @@ -130,7 +131,8 @@ func (v *Validator) Validate(value interface{}, checkAll ...bool) error { // SetErrorFactory customizes the factory of validation error. // NOTE: -// If errFactory==nil, the default is used +// +// If errFactory==nil, the default is used func (v *Validator) SetErrorFactory(errFactory func(failPath, msg string) error) *Validator { if errFactory == nil { errFactory = defaultErrorFactory diff --git a/internal/tagexpr/validator/validator_test.go b/internal/tagexpr/validator/validator_test.go index ef1bbec74..5cc2d7fb1 100644 --- a/internal/tagexpr/validator/validator_test.go +++ b/internal/tagexpr/validator/validator_test.go @@ -1,3 +1,17 @@ +// Copyright 2019 Bytedance Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package validator_test import ( @@ -5,28 +19,40 @@ import ( "errors" "testing" - "github.com/stretchr/testify/assert" - - vd "github.com/bytedance/go-tagexpr/v2/validator" + vd "github.com/cloudwego/hertz/internal/tagexpr/validator" ) +func assertEqualError(t *testing.T, err error, s string) { + t.Helper() + if err.Error() != s { + t.Fatal("not equal", err, s) + } +} + +func assertNoError(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatal(err) + } +} + func TestNil(t *testing.T) { type F struct { - f struct { - g int `vd:"$%3==1"` + F struct { + G int `vd:"$%3==1"` } } - assert.EqualError(t, vd.Validate((*F)(nil)), "unsupport data: nil") + assertEqualError(t, vd.Validate((*F)(nil)), "unsupported data: nil") } func TestAll(t *testing.T) { type T struct { - a string `vd:"email($)"` - f struct { - g int `vd:"$%3==1"` + A string `vd:"email($)"` + F struct { + G int `vd:"$%3==1"` } } - assert.EqualError(t, vd.Validate(new(T), true), "email format is incorrect\tinvalid parameter: f.g") + assertEqualError(t, vd.Validate(new(T), true), "email format is incorrect\tinvalid parameter: F.G") } func TestIssue1(t *testing.T) { @@ -54,7 +80,7 @@ func TestIssue1(t *testing.T) { type BatchCreateEmailTaskRequest struct { InfoList []*EmailTaskInfo } - var invalid = "invalid email" + invalid := "invalid email" req := &BatchCreateEmailTaskRequest{ InfoList: []*EmailTaskInfo{ { @@ -68,7 +94,7 @@ func TestIssue1(t *testing.T) { }, }, } - assert.EqualError(t, vd.Validate(req, false), "email format is incorrect") + assertEqualError(t, vd.Validate(req, false), "email format is incorrect") } func TestIssue2(t *testing.T) { @@ -82,7 +108,7 @@ func TestIssue2(t *testing.T) { }, } v := vd.New("vd") - assert.NoError(t, v.Validate(A)) + assertNoError(t, v.Validate(A)) } func TestIssue3(t *testing.T) { @@ -101,7 +127,7 @@ func TestIssue3(t *testing.T) { }, } v := vd.New("vd") - assert.NoError(t, v.Validate(a)) + assertNoError(t, v.Validate(a)) } func TestIssue4(t *testing.T) { @@ -118,23 +144,23 @@ func TestIssue4(t *testing.T) { v := vd.New("vd") a := &A{} - assert.NoError(t, v.Validate(a)) + assertNoError(t, v.Validate(a)) a = &A{F1: new(C)} - assert.EqualError(t, v.Validate(a), "index is nil") + assertEqualError(t, v.Validate(a), "index is nil") - a = &A{F2: map[string]*C{"x": &C{Index: new(int32)}}} - assert.EqualError(t, v.Validate(a), "invalid parameter: F2{v for k=x}.Index2") + a = &A{F2: map[string]*C{"x": {Index: new(int32)}}} + assertEqualError(t, v.Validate(a), "invalid parameter: F2{v for k=x}.Index2") a = &A{F3: []*C{{Index: new(int32)}}} - assert.EqualError(t, v.Validate(a), "invalid parameter: F3[0].Index2") + assertEqualError(t, v.Validate(a), "invalid parameter: F3[0].Index2") type B struct { F1 *C `vd:"$!=nil"` F2 *C } b := &B{} - assert.EqualError(t, v.Validate(b), "invalid parameter: F1") + assertEqualError(t, v.Validate(b), "invalid parameter: F1") type D struct { F1 *C @@ -146,12 +172,11 @@ func TestIssue4(t *testing.T) { } b.F1 = new(C) e := &E{D: []*D{nil}} - assert.NoError(t, v.Validate(e)) + assertNoError(t, v.Validate(e)) } func TestIssue5(t *testing.T) { - type SubSheet struct { - } + type SubSheet struct{} type CopySheet struct { Source *SubSheet `json:"source" vd:"$!=nil"` Destination *SubSheet `json:"destination" vd:"$!=nil"` @@ -165,11 +190,15 @@ func TestIssue5(t *testing.T) { b := `{"requests": [{}]}` var data BatchUpdateSheetRequestArg err := json.Unmarshal([]byte(b), &data) - assert.NoError(t, err) - assert.Equal(t, 1, len(data.Requests)) - assert.Nil(t, data.Requests[0].CopySheet) + assertNoError(t, err) + if len(data.Requests) != 1 { + t.Fatal(len(data.Requests)) + } + if data.Requests[0].CopySheet != nil { + t.Fatal(data.Requests[0].CopySheet) + } v := vd.New("vd") - assert.NoError(t, v.Validate(&data)) + assertNoError(t, v.Validate(&data)) } func TestIn(t *testing.T) { @@ -183,44 +212,44 @@ func TestIn(t *testing.T) { v := vd.New("vd") data := &T{} err := v.Validate(data) - assert.EqualError(t, err, "invalid parameter: A") + assertEqualError(t, err, "invalid parameter: A") data.A = "b" err = v.Validate(data) - assert.EqualError(t, err, "invalid parameter: B") + assertEqualError(t, err, "invalid parameter: B") data.B = 2 err = v.Validate(data) - assert.NoError(t, err) + assertNoError(t, err) type T2 struct { C string `vd:"in($)"` } data2 := &T2{} err = v.Validate(data2) - assert.EqualError(t, err, "invalid parameter: C") + assertEqualError(t, err, "invalid parameter: C") type T3 struct { C string `vd:"in($,1)"` } data3 := &T3{} err = v.Validate(data3) - assert.EqualError(t, err, "invalid parameter: C") + assertEqualError(t, err, "invalid parameter: C") } type ( Issue23A struct { - b *Issue23B - v int64 `vd:"$==0"` + B *Issue23B + V int64 `vd:"$==0"` } Issue23B struct { - a *Issue23A - v int64 `vd:"$==0"` + A *Issue23A + V int64 `vd:"$==0"` } ) func TestIssue23(t *testing.T) { - var data = &Issue23B{a: &Issue23A{b: new(Issue23B)}} + data := &Issue23B{A: &Issue23A{B: new(Issue23B)}} err := vd.Validate(data, true) - assert.NoError(t, err) + assertNoError(t, err) } func TestIssue24(t *testing.T) { @@ -251,9 +280,9 @@ func TestIssue24(t *testing.T) { type SubmitDoctorImportRequest struct { SubmitDoctorImport []*SubmitDoctorImportItem `form:"submit_doctor_import,required" json:"submit_doctor_import,required"` } - var data = &SubmitDoctorImportRequest{SubmitDoctorImport: []*SubmitDoctorImportItem{{}}} + data := &SubmitDoctorImportRequest{SubmitDoctorImport: []*SubmitDoctorImportItem{{}}} err := vd.Validate(data, true) - assert.EqualError(t, err, "invalid parameter: SubmitDoctorImport[0].Idcard\tinvalid parameter: SubmitDoctorImport[0].PracCertNo\temail format is incorrect\tthe phone number supplied is not a number") + assertEqualError(t, err, "invalid parameter: SubmitDoctorImport[0].Idcard\tinvalid parameter: SubmitDoctorImport[0].PracCertNo\temail format is incorrect\tthe phone number supplied is not a number") } func TestStructSliceMap(t *testing.T) { @@ -276,7 +305,7 @@ func TestStructSliceMap(t *testing.T) { C: map[string][]map[string]F{"z": {{"zz": *f}}}, } err := vd.Validate(s, true) - assert.EqualError(t, err, "invalid parameter: A{v for k=x}.f.g\tinvalid parameter: B[0]{v for k=y}.f.g\tinvalid parameter: C{v for k=z}[0]{v for k=zz}.f.g") + assertEqualError(t, err, "invalid parameter: A{v for k=x}.f.g\tinvalid parameter: B[0]{v for k=y}.f.g\tinvalid parameter: C{v for k=z}[0]{v for k=zz}.f.g") } func TestIssue30(t *testing.T) { @@ -287,27 +316,27 @@ func TestIssue30(t *testing.T) { vd.RegFunc("gt", func(args ...interface{}) error { return errors.New("force error") }) - assert.EqualError(t, vd.Validate(&TStruct{TOk: "1"}), "invalid parameter: TOk") - // assert.NoError(t, vd.Validate(&TStruct{TOk: "1", TFail: "1"})) + assertEqualError(t, vd.Validate(&TStruct{TOk: "1"}), "invalid parameter: TOk") + // assertNoError(t, vd.Validate(&TStruct{TOk: "1", TFail: "1"})) } func TestIssue31(t *testing.T) { type TStruct struct { A []int32 `vd:"$ == nil || ($ != nil && range($, in(#v, 1, 2, 3))"` } - assert.EqualError(t, vd.Validate(&TStruct{A: []int32{1}}), "syntax error: \"($ != nil && range($, in(#v, 1, 2, 3))\"") - assert.EqualError(t, vd.Validate(&TStruct{A: []int32{1}}), "syntax error: \"($ != nil && range($, in(#v, 1, 2, 3))\"") - assert.EqualError(t, vd.Validate(&TStruct{A: []int32{1}}), "syntax error: \"($ != nil && range($, in(#v, 1, 2, 3))\"") + assertEqualError(t, vd.Validate(&TStruct{A: []int32{1}}), "syntax error: \"($ != nil && range($, in(#v, 1, 2, 3))\"") + assertEqualError(t, vd.Validate(&TStruct{A: []int32{1}}), "syntax error: \"($ != nil && range($, in(#v, 1, 2, 3))\"") + assertEqualError(t, vd.Validate(&TStruct{A: []int32{1}}), "syntax error: \"($ != nil && range($, in(#v, 1, 2, 3))\"") } func TestRegexp(t *testing.T) { type TStruct struct { A string `vd:"regexp('(\\d+\\.){3}\\d+')"` } - assert.NoError(t, vd.Validate(&TStruct{A: "0.0.0.0"})) - assert.EqualError(t, vd.Validate(&TStruct{A: "0...0"}), "invalid parameter: A") - assert.EqualError(t, vd.Validate(&TStruct{A: "abc1"}), "invalid parameter: A") - assert.EqualError(t, vd.Validate(&TStruct{A: "0?0?0?0"}), "invalid parameter: A") + assertNoError(t, vd.Validate(&TStruct{A: "0.0.0.0"})) + assertEqualError(t, vd.Validate(&TStruct{A: "0...0"}), "invalid parameter: A") + assertEqualError(t, vd.Validate(&TStruct{A: "abc1"}), "invalid parameter: A") + assertEqualError(t, vd.Validate(&TStruct{A: "0?0?0?0"}), "invalid parameter: A") } func TestRangeIn(t *testing.T) { @@ -317,9 +346,9 @@ func TestRangeIn(t *testing.T) { err := vd.Validate(S{ F: []string{"ttp", "", "euttp"}, }) - assert.NoError(t, err) + assertNoError(t, err) err = vd.Validate(S{ F: []string{"ttp", "?", "euttp"}, }) - assert.EqualError(t, err, "invalid parameter: F") + assertEqualError(t, err, "invalid parameter: F") } diff --git a/pkg/app/server/binding/config.go b/pkg/app/server/binding/config.go index 81cf30e56..4ee349e93 100644 --- a/pkg/app/server/binding/config.go +++ b/pkg/app/server/binding/config.go @@ -22,7 +22,7 @@ import ( "reflect" "time" - exprValidator "github.com/bytedance/go-tagexpr/v2/validator" + exprValidator "github.com/cloudwego/hertz/internal/tagexpr/validator" inDecoder "github.com/cloudwego/hertz/pkg/app/server/binding/internal/decoder" hJson "github.com/cloudwego/hertz/pkg/common/json" "github.com/cloudwego/hertz/pkg/protocol" diff --git a/pkg/app/server/binding/default.go b/pkg/app/server/binding/default.go index 7e09ac9bb..42f55ee87 100644 --- a/pkg/app/server/binding/default.go +++ b/pkg/app/server/binding/default.go @@ -70,8 +70,8 @@ import ( "strings" "sync" - exprValidator "github.com/bytedance/go-tagexpr/v2/validator" "github.com/cloudwego/hertz/internal/bytesconv" + exprValidator "github.com/cloudwego/hertz/internal/tagexpr/validator" inDecoder "github.com/cloudwego/hertz/pkg/app/server/binding/internal/decoder" hJson "github.com/cloudwego/hertz/pkg/common/json" "github.com/cloudwego/hertz/pkg/common/utils"