github.com/bytedance/go-tagexpr@v2.7.5-0.20210114074101-de5b8743ad85+incompatible/README.md (about) 1 # 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) 2 3 An interesting go struct tag expression syntax for field validation, etc. 4 5 ## Usage 6 7 - **[Validator](https://github.com/bytedance/go-tagexpr/tree/master/validator)**: A powerful validator that supports struct tag expression 8 9 - **[Binding](https://github.com/bytedance/go-tagexpr/tree/master/binding)**: A powerful HTTP request parameters binder that supports struct tag expression 10 11 ## Feature 12 13 - Support for a variety of common operator 14 - Support for accessing arrays, slices, members of the dictionary 15 - Support access to any field in the current structure 16 - Support access to nested fields, non-exported fields, etc. 17 - Support registers function expression 18 - Built-in len, sprintf, regexp functions 19 - Support single mode and multiple mode to define expression 20 - Parameter check subpackage 21 - Use offset pointers to directly take values, better performance 22 - Required go version ≥1.9 23 24 ## Example 25 26 ```go 27 package tagexpr_test 28 29 import ( 30 "fmt" 31 32 tagexpr "github.com/bytedance/go-tagexpr" 33 ) 34 35 func Example() { 36 type T struct { 37 A int `tagexpr:"$<0||$>=100"` 38 B string `tagexpr:"len($)>1 && regexp('^\\w*$')"` 39 C bool `tagexpr:"expr1:(f.g)$>0 && $; expr2:'C must be true when T.f.g>0'"` 40 d []string `tagexpr:"@:len($)>0 && $[0]=='D'; msg:sprintf('invalid d: %v',$)"` 41 e map[string]int `tagexpr:"len($)==$['len']"` 42 e2 map[string]*int `tagexpr:"len($)==$['len']"` 43 f struct { 44 g int `tagexpr:"$"` 45 } 46 } 47 48 vm := tagexpr.New("tagexpr") 49 t := &T{ 50 A: 107, 51 B: "abc", 52 C: true, 53 d: []string{"x", "y"}, 54 e: map[string]int{"len": 1}, 55 e2: map[string]*int{"len": new(int)}, 56 f: struct { 57 g int `tagexpr:"$"` 58 }{1}, 59 } 60 61 tagExpr, err := vm.Run(t) 62 if err != nil { 63 panic(err) 64 } 65 66 fmt.Println(tagExpr.Eval("A")) 67 fmt.Println(tagExpr.Eval("B")) 68 fmt.Println(tagExpr.Eval("C@expr1")) 69 fmt.Println(tagExpr.Eval("C@expr2")) 70 if !tagExpr.Eval("d").(bool) { 71 fmt.Println(tagExpr.Eval("d@msg")) 72 } 73 fmt.Println(tagExpr.Eval("e")) 74 fmt.Println(tagExpr.Eval("e2")) 75 fmt.Println(tagExpr.Eval("f.g")) 76 77 // Output: 78 // true 79 // true 80 // true 81 // C must be true when T.f.g>0 82 // invalid d: [x y] 83 // true 84 // false 85 // 1 86 } 87 ``` 88 89 ## Syntax 90 91 Struct tag syntax spec: 92 93 ``` 94 type T struct { 95 // Single model 96 Field1 T1 `tagName:"expression"` 97 // Multiple model 98 Field2 T2 `tagName:"exprName:expression; [exprName2:expression2;]..."` 99 // Omit it 100 Field3 T3 `tagName:"-"` 101 // Omit it when it is nil 102 Field4 T4 `tagName:"?"` 103 ... 104 } 105 ``` 106 107 NOTE: **The `exprName` under the same struct field cannot be the same!** 108 109 |Operator or Operand|Explain| 110 |-----|---------| 111 |`true` `false`|boolean| 112 |`0` `0.0`|float64 "0"| 113 |`''`|String| 114 |`\\'`| Escape `'` delims in string| 115 |`\"`| Escape `"` delims in string| 116 |`nil`|nil, undefined| 117 |`!`|not, suitable for `bool`, `string`, `float64`, `nil`, `$` and `()`| 118 |`+`|Digital addition or string splicing| 119 |`-`|Digital subtraction or negative| 120 |`*`|Digital multiplication| 121 |`/`|Digital division| 122 |`%`|division remainder, as: `float64(int64(a)%int64(b))`| 123 |`==`|`eq`| 124 |`!=`|`ne`| 125 |`>`|`gt`| 126 |`>=`|`ge`| 127 |`<`|`lt`| 128 |`<=`|`le`| 129 |`&&`|Logic `and`| 130 |`\|\|`|Logic `or`| 131 |`()`|Expression group| 132 |`(X)$`|Struct field value named X| 133 |`(X.Y)$`|Struct field value named X.Y| 134 |`$`|Shorthand for `(X)$`, omit `(X)` to indicate current struct field value| 135 |`(X)$['A']`|Map value with key A or struct A sub-field in the struct field X| 136 |`(X)$[0]`|The 0th element or sub-field of the struct field X(type: map, slice, array, struct)| 137 |`len((X)$)`|Built-in function `len`, the length of struct field X| 138 |`mblen((X)$)`|the length of string field X (character number)| 139 |`regexp('^\\w*$', (X)$)`|Regular match the struct field X, return boolean| 140 |`regexp('^\\w*$')`|Regular match the current struct field, return boolean| 141 |`sprintf('X value: %v', (X)$)`|`fmt.Sprintf`, format the value of struct field X| 142 143 <!-- |`(X)$k`|Traverse each element key of the struct field X(type: map, slice, array)| 144 |`(X)$v`|Traverse each element value of the struct field X(type: map, slice, array)| --> 145 146 <!-- |`&`|Integer bitwise `and`| 147 |`\|`|Integer bitwise `or`| 148 |`^`|Integer bitwise `not` or `xor`| 149 |`&^`|Integer bitwise `clean`| 150 |`<<`|Integer bitwise `shift left`| 151 |`>>`|Integer bitwise `shift right`| --> 152 153 Operator priority(high -> low): 154 155 * `()` `!` `bool` `float64` `string` `nil` 156 * `*` `/` `%` 157 * `+` `-` 158 * `<` `<=` `>` `>=` 159 * `==` `!=` 160 * `&&` 161 * `||` 162 163 ## Field Selector 164 165 ``` 166 field_lv1.field_lv2...field_lvn 167 ``` 168 169 ## Expression Selector 170 171 - If expression is **single model** or exprName is `@`: 172 173 ``` 174 field_lv1.field_lv2...field_lvn 175 ``` 176 177 - If expression is **multiple model** and exprName is not `@`: 178 179 ``` 180 field_lv1.field_lv2...field_lvn@exprName 181 ``` 182 183 ## Benchmark 184 185 ``` 186 goos: darwin 187 goarch: amd64 188 pkg: github.com/bytedance/go-tagexpr 189 BenchmarkTagExpr-4 10000000 148 ns/op 32 B/op 3 allocs/op 190 BenchmarkReflect-4 10000000 182 ns/op 16 B/op 2 allocs/op 191 PASS 192 ``` 193 194 [Go to test code](https://github.com/bytedance/go-tagexpr/blob/master/tagexpr_test.go#L9-L56)