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..1adb4f87c 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 @@ -297,43 +294,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/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/selector.go b/internal/tagexpr/selector.go index 044380cca..ba0fe343e 100644 --- a/internal/tagexpr/selector.go +++ b/internal/tagexpr/selector.go @@ -18,11 +18,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 +54,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..586e4c7e4 100644 --- a/internal/tagexpr/selector_test.go +++ b/internal/tagexpr/selector_test.go @@ -2,13 +2,15 @@ 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..0b2a9ca23 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) diff --git a/internal/tagexpr/spec_func_test.go b/internal/tagexpr/spec_func_test.go index d6872f251..e353b7d96 100644 --- a/internal/tagexpr/spec_func_test.go +++ b/internal/tagexpr/spec_func_test.go @@ -15,11 +15,11 @@ 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) { @@ -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..8ae356a68 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,7 +212,6 @@ func readVariableExprNode(expr *string) ExprNode { } } - func getBoolAndSignOpposite(expr *string) (last string, boolOpposite *bool, signOpposite *bool) { last = strings.TrimLeft(last, "+") last, boolOpposite = getOpposite(expr, "!") @@ -241,7 +238,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 } @@ -287,7 +284,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 +343,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_test.go b/internal/tagexpr/spec_range_test.go index 3e209df79..79962dc0e 100644 --- a/internal/tagexpr/spec_range_test.go +++ b/internal/tagexpr/spec_range_test.go @@ -3,8 +3,7 @@ package tagexpr_test import ( "testing" - "github.com/bytedance/go-tagexpr/v2" - "github.com/stretchr/testify/assert" + "github.com/cloudwego/hertz/internal/tagexpr" ) func TestIssue12(t *testing.T) { @@ -25,10 +24,10 @@ 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{}{">m0:6", ">m1:7"}, r.Eval("M")) + assertEqual(t, []interface{}{[]interface{}{[]interface{}{">0:6", ">1:7"}}}, r.Eval("MFs")) + assertEqual(t, []interface{}{}, r.Eval("MFs2")) + assertEqual(t, true, r.EvalBool("MFs2")) } diff --git a/internal/tagexpr/tagexpr.go b/internal/tagexpr/tagexpr.go index 288f3510b..dc98aff50 100644 --- a/internal/tagexpr/tagexpr.go +++ b/internal/tagexpr/tagexpr.go @@ -23,8 +23,6 @@ import ( "strings" "sync" "unsafe" - - "github.com/andeya/ameda" ) // Internally unified data types @@ -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 } - tid := u.RuntimeTypeID() + tid := rvType(v) var err error vm.rw.RLock() s, ok := vm.structJar[tid] @@ -175,12 +172,12 @@ func checkStructMapAddr(v reflect.Value) error { } 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(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 @@ -813,7 +809,7 @@ func FakeBool(v interface{}) bool { } 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..1eb8abf5a 100644 --- a/internal/tagexpr/tagexpr_test.go +++ b/internal/tagexpr/tagexpr_test.go @@ -12,22 +12,31 @@ // 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() @@ -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,7 +440,7 @@ 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) } } @@ -531,7 +542,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 +554,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()] @@ -571,13 +582,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 +612,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")) @@ -667,17 +678,19 @@ func TestStruct3(t *testing.T) { 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 +700,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,10 +718,11 @@ 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()) + if !ok || !r { + t.Fatal(eh.Path()) + } return nil }) return @@ -737,14 +753,16 @@ func TestNilField(t *testing.T) { SI: []interface{}{&M{X: "nn"}}, } var cnt int - vm.MustRun(n).Range(func(eh *ExprHandler) error { + vm.MustRun(n).Range(func(eh *tagexpr.ExprHandler) error { r := eh.EvalBool() - assert.True(t, r, eh.Path()) + if !r { + t.Fatal(eh.Path()) + } t.Log("path:", eh.Path(), "es:", eh.ExprSelector(), "val:", r) cnt++ return nil }) - assert.Equal(t, 3, cnt) + assertEqual(t, 3, cnt) } func TestDeepNested(t *testing.T) { @@ -780,15 +798,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 +826,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 +856,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 +877,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_test.go b/internal/tagexpr/tagparser_test.go index aa7f4cc55..fbd382229 100644 --- a/internal/tagexpr/tagparser_test.go +++ b/internal/tagexpr/tagparser_test.go @@ -3,8 +3,6 @@ package tagexpr import ( "reflect" "testing" - - "github.com/stretchr/testify/assert" ) func TestTagparser(t *testing.T) { @@ -68,9 +66,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..0685d545b --- /dev/null +++ b/internal/tagexpr/utils.go @@ -0,0 +1,85 @@ +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{} + p1 := &T1{} + p2 := &T2{} + + 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/example_test.go b/internal/tagexpr/validator/example_test.go index 9318566dc..da999ca1b 100644 --- a/internal/tagexpr/validator/example_test.go +++ b/internal/tagexpr/validator/example_test.go @@ -3,7 +3,7 @@ package validator_test import ( "fmt" - vd "github.com/bytedance/go-tagexpr/v2/validator" + vd "github.com/cloudwego/hertz/internal/tagexpr/validator" ) func Example() { diff --git a/internal/tagexpr/validator/func.go b/internal/tagexpr/validator/func.go index fedcd36ed..fe965610b 100644 --- a/internal/tagexpr/validator/func.go +++ b/internal/tagexpr/validator/func.go @@ -6,7 +6,7 @@ import ( "github.com/nyaruka/phonenumbers" - "github.com/bytedance/go-tagexpr/v2" + "github.com/cloudwego/hertz/internal/tagexpr" ) // ErrInvalidWithoutMsg verification error without error message. diff --git a/internal/tagexpr/validator/validator.go b/internal/tagexpr/validator/validator.go index 577bffc3a..5adc19d24 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,7 +55,8 @@ 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 { @@ -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..1c1fd3647 100644 --- a/internal/tagexpr/validator/validator_test.go +++ b/internal/tagexpr/validator/validator_test.go @@ -5,18 +5,30 @@ 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"` } } - assert.EqualError(t, vd.Validate((*F)(nil)), "unsupport data: nil") + assertEqualError(t, vd.Validate((*F)(nil)), "unsupport data: nil") } func TestAll(t *testing.T) { @@ -26,7 +38,7 @@ func TestAll(t *testing.T) { 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) { @@ -68,7 +80,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 +94,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 +113,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 +130,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") + 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,7 +158,7 @@ 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) { @@ -165,11 +177,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,27 +199,27 @@ 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 ( @@ -220,7 +236,7 @@ type ( func TestIssue23(t *testing.T) { var data = &Issue23B{a: &Issue23A{b: new(Issue23B)}} err := vd.Validate(data, true) - assert.NoError(t, err) + assertNoError(t, err) } func TestIssue24(t *testing.T) { @@ -253,7 +269,7 @@ func TestIssue24(t *testing.T) { } var 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 +292,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 +303,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 +333,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"