# README
go-tagexpr

An interesting go struct tag expression syntax for field validation, etc.
Usage
-
Validator: A powerful validator that supports struct tag expression
-
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 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
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:"$"`
}
}
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},
}
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"))
// Output:
// true
// true
// true
// C must be true when T.f.g>0
// invalid d: [x y]
// true
// false
// 1
}
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 |
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
# Functions
FakeBool fakes any type as a boolean.
JoinExprSelector creates a expression selector.
JoinFieldSelector creates a field selector.
MustRegFunc registers function expression.
New creates a tag expression interpreter that uses tagName as the tag name.
RegFunc registers function expression.
# Constants
DefaultExprName the default name of single model expression.
ExprNameSeparator in the expression selector, the separator of the field name and expression name.
FieldSeparator in the expression selector, the separator between field names.
# Structs
Expr expression.
ExprHandler expr handler.
FieldHandler field handler.
TagExpr struct tag expression evaluator.
VM struct tag expression interpreter.
# Type aliases
Internally unified data types.
ExprSelector expression selector.
FieldSelector expression selector.
Internally unified data types.
Internally unified data types.