tagexpr

package module
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Jun 6, 2019 License: Apache-2.0 Imports: 12 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

  • 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"
)

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
\\' Escape ' delims in string
\" Escape " delims in 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	       148 ns/op	      32 B/op	       3 allocs/op
BenchmarkReflect-4   	10000000	       182 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

View Source
const (
	// FieldSeparator in the expression selector,
	// the separator between field names
	FieldSeparator = "."
	// ExprNameSeparator in the expression selector,
	// the separator of the field name and expression name
	ExprNameSeparator = "@"
	// DefaultExprName the default name of single model expression
	DefaultExprName = ExprNameSeparator
)

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 JoinExprSelector added in v1.4.0

func JoinExprSelector(pathFields []string, exprName string) string

JoinExprSelector creates a expression selector.

func JoinFieldSelector added in v1.4.0

func JoinFieldSelector(path ...string) string

JoinFieldSelector creates a field selector.

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 ExprSelector added in v1.4.0

type ExprSelector string

ExprSelector expression selector

func (ExprSelector) Field added in v1.4.0

func (e ExprSelector) Field() string

Field returns the name of the field it belongs to.

func (ExprSelector) Name added in v1.4.0

func (e ExprSelector) Name() string

Name returns the name of the expression.

func (ExprSelector) Split added in v1.4.0

func (e ExprSelector) Split() (field FieldSelector, name string)

Split returns the field selector and the expression name.

func (ExprSelector) String added in v1.4.0

func (e ExprSelector) String() string

String returns string type value.

type FieldHandler added in v1.4.0

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

FieldHandler field handler

func (*FieldHandler) EvalFuncs added in v1.4.0

func (f *FieldHandler) EvalFuncs() map[ExprSelector]func() interface{}

EvalFuncs returns the tag expression eval functions.

func (*FieldHandler) FieldSelector added in v1.4.0

func (f *FieldHandler) FieldSelector() FieldSelector

FieldSelector returns the field selector of FieldSelector type.

func (*FieldHandler) StringSelector added in v1.4.0

func (f *FieldHandler) StringSelector() string

StringSelector returns the field selector of string type.

func (*FieldHandler) StructField added in v1.4.0

func (f *FieldHandler) StructField() reflect.StructField

StructField returns the field StructField object.

func (*FieldHandler) Value added in v1.4.0

func (f *FieldHandler) Value(initZero bool) reflect.Value

Value returns the field value. NOTE:

If initZero==true, initialize nil pointer to zero value

type FieldSelector added in v1.4.0

type FieldSelector string

FieldSelector expression selector

func (FieldSelector) Name added in v1.4.0

func (f FieldSelector) Name() string

Name returns the current field name.

func (FieldSelector) Split added in v1.4.0

func (f FieldSelector) Split() (paths []string, name string)

Split returns the path segments and the current field name.

func (FieldSelector) String added in v1.4.0

func (f FieldSelector) String() string

String returns string type value.

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) (fh *FieldHandler, found bool)

Field returns the field handler specified by the selector.

func (*TagExpr) Range

func (t *TagExpr) Range(fn func(es ExprSelector, eval func() interface{}) bool) bool

Range loop through each tag expression. When fn returns false, interrupt traversal and return false. NOTE:

eval result types: float64, string, bool, nil

func (*TagExpr) RangeFields added in v1.4.0

func (t *TagExpr) RangeFields(fn func(*FieldHandler) bool) bool

RangeFields loop through each field. When fn returns false, interrupt traversal and return false.

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(structOrStructPtrOrReflectValue interface{}) *TagExpr

MustRun is similar to Run, but panic when error.

func (*VM) MustWarmUp added in v1.2.0

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

MustWarmUp is similar to WarmUp, but panic when error.

func (*VM) Run

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

Run returns the tag expression handler of the @structOrStructPtrOrReflectValue. 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(structOrStructPtrOrReflect ...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