tagexpr

package module
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 22, 2019 License: Apache-2.0 Imports: 11 Imported by: 0

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

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

Example

package tagexpr_test

import (
	"fmt"

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

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")
	err := vm.WarmUp(new(T))
	if err != nil {
		panic(err)
	}

	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}]..."`
    ...
}

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
nil nil, undefined
! not, suitable for bool, string, float64, nil and $
+ 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
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

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	       195 ns/op	      40 B/op	       4 allocs/op
BenchmarkReflect-4   	10000000	       208 ns/op	      16 B/op	       2 allocs/op
PASS

Go to test code

Documentation

Overview

Package tagexpr is an interesting go struct tag expression syntax for field validation, etc.

Copyright 2019 Bytedance Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License"); 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

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Example
package main

import (
	"fmt"

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

func main() {
	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")
	err := vm.WarmUp(new(T))
	if err != nil {
		panic(err)
	}

	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

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func FakeBool added in v1.2.1

func FakeBool(v interface{}) bool

FakeBool fakes any type as a boolean.

func RegFunc added in v1.2.0

func RegFunc(funcName string, fn func(...interface{}) interface{}, force ...bool) error

RegFunc registers function expression. NOTE:

example: len($), regexp("\\d") or regexp("\\d",$);
If @force=true, allow to cover the existed same @funcName;
The go number types always are float64;
The go string types always are string.

Types

type Expr

type Expr struct {
	// contains filtered or unexported fields
}

Expr expression

type ExprNode

type ExprNode interface {
	SetParent(ExprNode)
	Parent() ExprNode
	LeftOperand() ExprNode
	RightOperand() ExprNode
	SetLeftOperand(ExprNode)
	SetRightOperand(ExprNode)
	Run(string, *TagExpr) interface{}
}

ExprNode expression interface

type TagExpr

type TagExpr struct {
	// contains filtered or unexported fields
}

TagExpr struct tag expression evaluator

func (*TagExpr) Eval

func (t *TagExpr) Eval(exprSelector string) interface{}

Eval evaluate the value of the struct tag expression by the selector expression. NOTE:

format: fieldName, fieldName.exprName, fieldName1.fieldName2.exprName1
result types: float64, string, bool, nil

func (*TagExpr) EvalBool added in v1.1.0

func (t *TagExpr) EvalBool(exprSelector string) bool

EvalBool evaluate the value of the struct tag expression by the selector expression. NOTE:

If the expression value is not 0, '' or nil, return true.

func (*TagExpr) EvalFloat added in v1.1.0

func (t *TagExpr) EvalFloat(exprSelector string) float64

EvalFloat evaluate the value of the struct tag expression by the selector expression. NOTE:

If the expression value type is not float64, return 0.

func (*TagExpr) EvalString added in v1.1.0

func (t *TagExpr) EvalString(exprSelector string) string

EvalString evaluate the value of the struct tag expression by the selector expression. NOTE:

If the expression value type is not string, return "".

func (*TagExpr) Field added in v1.1.3

func (t *TagExpr) Field(fieldSelector string) interface{}

Field returns the field value specified by the selector. NOTE:

Return nil if the field is not exist

func (*TagExpr) Range

func (t *TagExpr) Range(fn func(exprSelector string, eval func() interface{}) bool)

Range loop through each tag expression NOTE:

eval result types: float64, string, bool, nil

type VM

type VM struct {
	// contains filtered or unexported fields
}

VM struct tag expression interpreter

func New

func New(tagName string) *VM

New creates a tag expression interpreter that uses @tagName as the tag name.

func (*VM) MustRun added in v1.2.0

func (vm *VM) MustRun(structPtr interface{}) *TagExpr

MustRun is similar to Run, but panic when error.

func (*VM) MustWarmUp added in v1.2.0

func (vm *VM) MustWarmUp(structOrStructPtr ...interface{})

MustWarmUp is similar to WarmUp, but panic when error.

func (*VM) Run

func (vm *VM) Run(structPtr interface{}) (*TagExpr, error)

Run returns the tag expression handler of the @structPtr. NOTE:

If the structure type has not been warmed up,
it will be slower when it is first called.

func (*VM) WarmUp

func (vm *VM) WarmUp(structOrStructPtr ...interface{}) error

WarmUp preheating some interpreters of the struct type in batches, to improve the performance of the vm.Run.

Directories

Path Synopsis
Package validator is a powerful validator that supports struct tag expression.
Package validator is a powerful validator that supports struct tag expression.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL