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)