tagexpr

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Dec 22, 2022 License: Apache-2.0 Imports: 16 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/38888/go-tagexpr-new"
)

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

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/38888/go-tagexpr-new"
)

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

func FakeBool(v interface{}) bool

FakeBool fakes any type as a boolean.

func JoinExprSelector

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

JoinExprSelector creates a expression selector.

func JoinFieldSelector

func JoinFieldSelector(path ...string) string

JoinFieldSelector creates a field selector.

func RegFunc

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 Boolean

type Boolean = bool

Internally unified data types

type Expr

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

Expr expression

type ExprHandler

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

ExprHandler expr handler

func (*ExprHandler) Eval

func (e *ExprHandler) Eval() interface{}

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

result types: float64, string, bool, nil

func (*ExprHandler) EvalBool

func (e *ExprHandler) EvalBool() bool

EvalBool evaluates the value of the struct tag expression. NOTE:

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

func (*ExprHandler) EvalFloat

func (e *ExprHandler) EvalFloat() float64

EvalFloat evaluates the value of the struct tag expression. NOTE:

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

func (*ExprHandler) EvalString

func (e *ExprHandler) EvalString() string

EvalString evaluates the value of the struct tag expression. NOTE:

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

func (*ExprHandler) ExprSelector

func (e *ExprHandler) ExprSelector() ExprSelector

ExprSelector returns the expression selector of ExprSelector type.

func (*ExprHandler) Path

func (e *ExprHandler) Path() string

Path returns the path description of the expression.

func (*ExprHandler) StringSelector

func (e *ExprHandler) StringSelector() string

StringSelector returns the expression selector of string type.

func (*ExprHandler) TagExpr

func (e *ExprHandler) TagExpr() *TagExpr

TagExpr returns the *TagExpr.

type ExprNode

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

ExprNode expression interface

type ExprSelector

type ExprSelector string

ExprSelector expression selector

func (ExprSelector) Field

func (e ExprSelector) Field() string

Field returns the field selector it belongs to.

func (ExprSelector) Name

func (e ExprSelector) Name() string

Name returns the name of the expression.

func (ExprSelector) ParentField

func (e ExprSelector) ParentField() (string, bool)

ParentField returns the parent field selector it belongs to.

func (ExprSelector) Split

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

Split returns the field selector and the expression name.

func (ExprSelector) String

func (e ExprSelector) String() string

String returns string type value.

type FieldHandler

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

FieldHandler field handler

func (*FieldHandler) EvalFuncs

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

EvalFuncs returns the tag expression eval functions.

func (*FieldHandler) FieldSelector

func (f *FieldHandler) FieldSelector() FieldSelector

FieldSelector returns the field selector of FieldSelector type.

func (*FieldHandler) StringSelector

func (f *FieldHandler) StringSelector() string

StringSelector returns the field selector of string type.

func (*FieldHandler) StructField

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

StructField returns the field StructField object.

func (*FieldHandler) Value

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

type FieldSelector string

FieldSelector expression selector

func (FieldSelector) Name

func (f FieldSelector) Name() string

Name returns the current field name.

func (FieldSelector) Parent

func (f FieldSelector) Parent() (string, bool)

Parent returns the parent FieldSelector.

func (FieldSelector) Split

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

Split returns the path segments and the current field name.

func (FieldSelector) String

func (f FieldSelector) String() string

String returns string type value.

type Null

type Null = interface{}

Internally unified data types

type Number

type Number = float64

Internally unified data types

type String

type String = string

Internally unified data types

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

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

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

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

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

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

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

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

func (*TagExpr) Field

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(*ExprHandler) error) error

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

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. NOTE:

If no tagName is specified, no tag expression will be interpreted,
but still can operate the various fields.

func (*VM) MustRun

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

MustRun is similar to Run, but panic when error.

func (*VM) Run

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

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

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

Disable new -d=checkptr behaviour for Go 1.14

func (*VM) RunAny

func (vm *VM) RunAny(v interface{}, fn func(*TagExpr, error) error) error

RunAny returns the tag expression handler for the @v. NOTE:

The @v can be structured data such as struct, map, slice, array, interface, reflcet.Value, etc.
If the structure type has not been warmed up,
it will be slower when it is first called.

Directories

Path Synopsis
Package goutil is a common and useful toolkit for Go project development.
Package goutil is a common and useful toolkit for Go project development.
calendar/cron
Package cron implements a cron spec parser and job runner.
Package cron implements a cron spec parser and job runner.
cmder
Package cmder exec cmd and catch the result.
Package cmder exec cmd and catch the result.
errors
errors is improved errors package.
errors is improved errors package.
graceful
graceful package shutdown or reboot current process gracefully.
graceful package shutdown or reboot current process gracefully.
graceful/inherit_net
Package inherit_net provides a family of Listen functions that either open a fresh connection or provide an inherited connection from when the process was started.
Package inherit_net provides a family of Listen functions that either open a fresh connection or provide an inherited connection from when the process was started.
status
Package status is a handling status with code, msg, cause and stack.
Package status is a handling status with code, msg, cause and stack.
versioning
Package versioning is a version comparison tool that conforms to semantic version 2.0.0
Package versioning is a version comparison tool that conforms to semantic version 2.0.0
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