Repository

go-tagexpr

An interesting go struct tag expression syntax for field validation, etc.

Operator or Operand
Explain
true false
boolean
0 0.0
float64 "0"
''
String

Specs

  • Operator or OperandExplain
  • true falseboolean
  • 0 0.0float64 "0"
  • ''String
  • \\'Escape ' delims in string
  • \"Escape " delims in string
  • nilnil, 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

README

go-tagexpr report card GoDoc

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 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

package tagexpr_test

import (
    "fmt"

    tagexpr "github.com/bytedance/go-tagexpr/v2"
)

func Example() {
    type T struct {
        A  int             `tagexpr:"$=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
`