3 Star 16 Fork 3

Gitee 极速下载 / go-tagexpr

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/bytedance/go-tagexpr/
克隆/下载
tagexpr.go 27.53 KB
一键复制 编辑 原始数据 按行查看 历史
andeyalee 提交于 2020-12-28 18:23 . fix: runtime type id
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088
// 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.
package tagexpr
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"sync"
"unsafe"
"github.com/henrylee2cn/goutil"
"github.com/henrylee2cn/goutil/tpack"
)
// Internally unified data types
type (
Number = float64
Null = interface{}
Boolean = bool
String = string
)
// VM struct tag expression interpreter
type VM struct {
tagName string
structJar map[uintptr]*structVM
rw sync.RWMutex
}
// structVM tag expression set of struct
type structVM struct {
vm *VM
name string
fields map[string]*fieldVM
fieldSelectorList []string
fieldsWithIndirectStructVM []*fieldVM
exprs map[string]*Expr
exprSelectorList []string
ifaceTagExprGetters []func(unsafe.Pointer, string, func(*TagExpr, error) error) error
}
// fieldVM tag expression set of struct field
type fieldVM struct {
structField reflect.StructField
ptrDeep int
getPtr func(unsafe.Pointer) unsafe.Pointer
elemType reflect.Type
elemKind reflect.Kind
valueGetter func(unsafe.Pointer) interface{}
reflectValueGetter func(unsafe.Pointer, bool) reflect.Value
exprs map[string]*Expr
origin *structVM
mapKeyStructVM *structVM
mapOrSliceElemStructVM *structVM
mapOrSliceIfaceKinds [2]bool // [value, key/index]
fieldSelector string
tagOp string
}
// 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 New(tagName ...string) *VM {
if len(tagName) == 0 {
tagName = append(tagName, "")
}
return &VM{
tagName: tagName[0],
structJar: make(map[uintptr]*structVM, 256),
}
}
// MustRun is similar to Run, but panic when error.
func (vm *VM) MustRun(structOrStructPtrOrReflectValue interface{}) *TagExpr {
te, err := vm.Run(structOrStructPtrOrReflectValue)
if err != nil {
panic(err)
}
return te
}
var unsupportNil = errors.New("unsupport data: nil")
// 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.
// Disable new -d=checkptr behaviour for Go 1.14
//go:nocheckptr
func (vm *VM) Run(structOrStructPtrOrReflectValue interface{}) (*TagExpr, error) {
var u tpack.U
v, isReflectValue := structOrStructPtrOrReflectValue.(reflect.Value)
if isReflectValue {
u = tpack.From(v)
} else {
u = tpack.Unpack(structOrStructPtrOrReflectValue)
}
ptr := unsafe.Pointer(u.Pointer())
if ptr == nil {
return nil, unsupportNil
}
u = u.UnderlyingElem()
tid := u.RuntimeTypeID()
var err error
vm.rw.RLock()
s, ok := vm.structJar[tid]
vm.rw.RUnlock()
if !ok {
vm.rw.Lock()
s, ok = vm.structJar[tid]
if !ok {
if isReflectValue {
s, err = vm.registerStructLocked(v.Type())
} else {
s, err = vm.registerStructLocked(reflect.TypeOf(structOrStructPtrOrReflectValue))
}
if err != nil {
vm.rw.Unlock()
return nil, err
}
}
vm.rw.Unlock()
}
return s.newTagExpr(ptr, ""), nil
}
// 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.
func (vm *VM) RunAny(v interface{}, fn func(*TagExpr, error) error) error {
vv, isReflectValue := v.(reflect.Value)
if !isReflectValue {
vv = reflect.ValueOf(v)
}
return vm.subRunAll(false, "", vv, fn)
}
func (vm *VM) subRunAll(omitNil bool, tePath string, value reflect.Value, fn func(*TagExpr, error) error) error {
rv := goutil.DereferenceIfaceValue(value)
if !rv.IsValid() {
return nil
}
rt := goutil.DereferenceType(rv.Type())
rv = goutil.DereferenceValue(rv)
switch rt.Kind() {
case reflect.Struct:
ptr := unsafe.Pointer(tpack.From(rv).Pointer())
if ptr == nil {
if omitNil {
return nil
}
return fn(nil, unsupportNil)
}
return fn(vm.subRun(tePath, rt, tpack.RuntimeTypeID(rt), ptr))
case reflect.Slice, reflect.Array:
count := rv.Len()
if count == 0 {
return nil
}
switch goutil.DereferenceType(rv.Type().Elem()).Kind() {
case reflect.Struct, reflect.Interface, reflect.Slice, reflect.Array, reflect.Map:
for i := count - 1; i >= 0; i-- {
err := vm.subRunAll(omitNil, tePath+"["+strconv.Itoa(i)+"]", rv.Index(i), fn)
if err != nil {
return err
}
}
default:
return nil
}
case reflect.Map:
if rv.Len() == 0 {
return nil
}
var canKey, canValue bool
rt := rv.Type()
switch goutil.DereferenceType(rt.Key()).Kind() {
case reflect.Struct, reflect.Interface, reflect.Slice, reflect.Array, reflect.Map:
canKey = true
}
switch goutil.DereferenceType(rt.Elem()).Kind() {
case reflect.Struct, reflect.Interface, reflect.Slice, reflect.Array, reflect.Map:
canValue = true
}
if !canKey && !canValue {
return nil
}
for _, key := range rv.MapKeys() {
if canKey {
err := vm.subRunAll(omitNil, tePath+"{}", key, fn)
if err != nil {
return err
}
}
if canValue {
err := vm.subRunAll(omitNil, tePath+"{K:"+key.String()+"}", rv.MapIndex(key), fn)
if err != nil {
return err
}
}
}
}
return nil
}
func (vm *VM) subRun(path string, t reflect.Type, tid uintptr, ptr unsafe.Pointer) (*TagExpr, error) {
var err error
vm.rw.RLock()
s, ok := vm.structJar[tid]
vm.rw.RUnlock()
if !ok {
vm.rw.Lock()
s, ok = vm.structJar[tid]
if !ok {
s, err = vm.registerStructLocked(t)
if err != nil {
vm.rw.Unlock()
return nil, err
}
}
vm.rw.Unlock()
}
return s.newTagExpr(ptr, path), nil
}
func (vm *VM) registerStructLocked(structType reflect.Type) (*structVM, error) {
structType, err := vm.getStructType(structType)
if err != nil {
return nil, err
}
tid := tpack.RuntimeTypeID(structType)
s, had := vm.structJar[tid]
if had {
return s, nil
}
s = vm.newStructVM()
s.name = structType.String()
vm.structJar[tid] = s
var numField = structType.NumField()
var structField reflect.StructField
var sub *structVM
for i := 0; i < numField; i++ {
structField = structType.Field(i)
field, err := s.newFieldVM(structField)
if err != nil {
return nil, err
}
switch field.elemKind {
default:
field.setUnsupportGetter()
switch field.elemKind {
case reflect.Struct:
sub, err = vm.registerStructLocked(field.structField.Type)
if err != nil {
return nil, err
}
s.mergeSubStructVM(field, sub)
case reflect.Interface:
s.setIfaceTagExprGetter(field)
}
case reflect.Float32, reflect.Float64,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
field.setFloatGetter()
case reflect.String:
field.setStringGetter()
case reflect.Bool:
field.setBoolGetter()
case reflect.Array, reflect.Slice, reflect.Map:
err = vm.registerIndirectStructLocked(field)
if err != nil {
return nil, err
}
}
}
return s, nil
}
func (vm *VM) registerIndirectStructLocked(field *fieldVM) error {
field.setLengthGetter()
if field.tagOp == tagOmit {
return nil
}
a := make([]reflect.Type, 1, 2)
a[0] = derefType(field.elemType.Elem())
if field.elemKind == reflect.Map {
a = append(a, derefType(field.elemType.Key()))
}
for i, t := range a {
kind := t.Kind()
if kind != reflect.Struct {
if kind == reflect.Interface {
field.mapOrSliceIfaceKinds[i] = true
field.origin.fieldsWithIndirectStructVM = append(field.origin.fieldsWithIndirectStructVM, field)
}
continue
}
s, err := vm.registerStructLocked(t)
if err != nil {
return err
}
if len(s.exprSelectorList) > 0 ||
len(s.ifaceTagExprGetters) > 0 ||
len(s.fieldsWithIndirectStructVM) > 0 {
if i == 0 {
field.mapOrSliceElemStructVM = s
} else {
field.mapKeyStructVM = s
}
field.origin.fieldsWithIndirectStructVM = append(field.origin.fieldsWithIndirectStructVM, field)
}
}
return nil
}
func (vm *VM) newStructVM() *structVM {
return &structVM{
vm: vm,
fields: make(map[string]*fieldVM, 32),
fieldSelectorList: make([]string, 0, 32),
fieldsWithIndirectStructVM: make([]*fieldVM, 0, 32),
exprs: make(map[string]*Expr, 64),
exprSelectorList: make([]string, 0, 64),
}
}
func (s *structVM) newFieldVM(structField reflect.StructField) (*fieldVM, error) {
f := &fieldVM{
structField: structField,
exprs: make(map[string]*Expr, 8),
origin: s,
fieldSelector: structField.Name,
}
err := f.parseExprs(structField.Tag.Get(s.vm.tagName))
if err != nil {
return nil, err
}
s.fields[f.fieldSelector] = f
s.fieldSelectorList = append(s.fieldSelectorList, f.fieldSelector)
var t = structField.Type
var ptrDeep int
for t.Kind() == reflect.Ptr {
t = t.Elem()
ptrDeep++
}
f.ptrDeep = ptrDeep
var offset = structField.Offset
f.getPtr = func(ptr unsafe.Pointer) unsafe.Pointer {
if ptr == nil {
return nil
}
return unsafe.Pointer(uintptr(ptr) + offset)
}
f.elemType = t
f.elemKind = t.Kind()
f.reflectValueGetter = func(ptr unsafe.Pointer, initZero bool) reflect.Value {
v := f.packRawFrom(ptr)
if initZero {
f.ensureInit(v)
}
return v
}
return f, nil
}
func (f *fieldVM) ensureInit(v reflect.Value) {
if safeIsNil(v) && v.CanSet() {
newField := reflect.New(f.elemType).Elem()
for i := 0; i < f.ptrDeep; i++ {
if newField.CanAddr() {
newField = newField.Addr()
} else {
newField2 := reflect.New(newField.Type())
newField2.Elem().Set(newField)
newField = newField2
}
}
v.Set(newField)
}
}
func (s *structVM) mergeSubStructVM(field *fieldVM, sub *structVM) {
field.origin = sub
fieldsWithIndirectStructVM := make(map[*fieldVM]struct{}, len(sub.fieldsWithIndirectStructVM))
for _, subField := range sub.fieldsWithIndirectStructVM {
fieldsWithIndirectStructVM[subField] = struct{}{}
}
for _, k := range sub.fieldSelectorList {
v := sub.fields[k]
f := s.newChildField(field, v, true)
if _, ok := fieldsWithIndirectStructVM[v]; ok {
s.fieldsWithIndirectStructVM = append(s.fieldsWithIndirectStructVM, f)
// TODO: maybe needed?
// delete(fieldsWithIndirectStructVM, v)
}
}
// TODO: maybe needed?
// for v := range fieldsWithIndirectStructVM {
// f := s.newChildField(field, v, false)
// s.fieldsWithIndirectStructVM = append(s.fieldsWithIndirectStructVM, f)
// }
for _, _subFn := range sub.ifaceTagExprGetters {
subFn := _subFn
s.ifaceTagExprGetters = append(s.ifaceTagExprGetters, func(ptr unsafe.Pointer, pathPrefix string, fn func(*TagExpr, error) error) error {
ptr = field.getElemPtr(ptr)
if ptr == nil {
return nil
}
var path string
if pathPrefix == "" {
path = field.fieldSelector
} else {
path = pathPrefix + FieldSeparator + field.fieldSelector
}
return subFn(ptr, path, fn)
})
}
}
func (s *structVM) newChildField(parent *fieldVM, child *fieldVM, toBind bool) *fieldVM {
f := &fieldVM{
structField: child.structField,
exprs: make(map[string]*Expr, len(child.exprs)),
ptrDeep: child.ptrDeep,
elemType: child.elemType,
elemKind: child.elemKind,
origin: child.origin,
mapKeyStructVM: child.mapKeyStructVM,
mapOrSliceElemStructVM: child.mapOrSliceElemStructVM,
mapOrSliceIfaceKinds: child.mapOrSliceIfaceKinds,
fieldSelector: parent.fieldSelector + FieldSeparator + child.fieldSelector,
}
if parent.tagOp != tagOmit {
f.tagOp = child.tagOp
} else {
f.tagOp = parent.tagOp
}
f.getPtr = func(ptr unsafe.Pointer) unsafe.Pointer {
ptr = parent.getElemPtr(ptr)
if ptr == nil {
return nil
}
return child.getPtr(ptr)
}
if child.valueGetter != nil {
if parent.ptrDeep == 0 {
f.valueGetter = func(ptr unsafe.Pointer) interface{} {
return child.valueGetter(parent.getPtr(ptr))
}
f.reflectValueGetter = func(ptr unsafe.Pointer, initZero bool) reflect.Value {
return child.reflectValueGetter(parent.getPtr(ptr), initZero)
}
} else {
f.valueGetter = func(ptr unsafe.Pointer) interface{} {
newField := reflect.NewAt(parent.structField.Type, parent.getPtr(ptr))
for i := 0; i < parent.ptrDeep; i++ {
newField = newField.Elem()
}
if newField.IsNil() {
return nil
}
return child.valueGetter(unsafe.Pointer(newField.Pointer()))
}
f.reflectValueGetter = func(ptr unsafe.Pointer, initZero bool) reflect.Value {
newField := reflect.NewAt(parent.structField.Type, parent.getPtr(ptr))
if initZero {
parent.ensureInit(newField.Elem())
}
for i := 0; i < parent.ptrDeep; i++ {
newField = newField.Elem()
}
if (newField == reflect.Value{}) || (!initZero && newField.IsNil()) {
return reflect.Value{}
}
return child.reflectValueGetter(unsafe.Pointer(newField.Pointer()), initZero)
}
}
}
if toBind {
s.fields[f.fieldSelector] = f
s.fieldSelectorList = append(s.fieldSelectorList, f.fieldSelector)
if parent.tagOp != tagOmit {
for k, v := range child.exprs {
selector := parent.fieldSelector + FieldSeparator + k
f.exprs[selector] = v
s.exprs[selector] = v
s.exprSelectorList = append(s.exprSelectorList, selector)
}
}
}
return f
}
func (f *fieldVM) getElemPtr(ptr unsafe.Pointer) unsafe.Pointer {
ptr = f.getPtr(ptr)
for i := f.ptrDeep; ptr != nil && i > 0; i-- {
ptr = ptrElem(ptr)
}
return ptr
}
func (f *fieldVM) packRawFrom(ptr unsafe.Pointer) reflect.Value {
return reflect.NewAt(f.structField.Type, f.getPtr(ptr)).Elem()
}
func (f *fieldVM) packElemFrom(ptr unsafe.Pointer) reflect.Value {
return reflect.NewAt(f.elemType, f.getElemPtr(ptr)).Elem()
}
func (s *structVM) setIfaceTagExprGetter(f *fieldVM) {
if f.tagOp == tagOmit {
return
}
s.ifaceTagExprGetters = append(s.ifaceTagExprGetters, func(ptr unsafe.Pointer, pathPrefix string, fn func(*TagExpr, error) error) error {
v := f.packElemFrom(ptr)
if !v.IsValid() || v.IsNil() {
return nil
}
var path string
if pathPrefix == "" {
path = f.fieldSelector
} else {
path = pathPrefix + FieldSeparator + f.fieldSelector
}
return s.vm.subRunAll(f.tagOp == tagOmitNil, path, v, fn)
})
}
func (f *fieldVM) setFloatGetter() {
if f.ptrDeep == 0 {
f.valueGetter = func(ptr unsafe.Pointer) interface{} {
ptr = f.getPtr(ptr)
if ptr == nil {
return nil
}
return getFloat64(f.elemKind, ptr)
}
} else {
f.valueGetter = func(ptr unsafe.Pointer) interface{} {
v := f.packElemFrom(ptr)
if v.CanAddr() {
return getFloat64(f.elemKind, unsafe.Pointer(v.UnsafeAddr()))
}
return nil
}
}
}
func (f *fieldVM) setBoolGetter() {
if f.ptrDeep == 0 {
f.valueGetter = func(ptr unsafe.Pointer) interface{} {
ptr = f.getPtr(ptr)
if ptr == nil {
return nil
}
return *(*bool)(ptr)
}
} else {
f.valueGetter = func(ptr unsafe.Pointer) interface{} {
v := f.packElemFrom(ptr)
if v.IsValid() {
return v.Bool()
}
return nil
}
}
}
func (f *fieldVM) setStringGetter() {
if f.ptrDeep == 0 {
f.valueGetter = func(ptr unsafe.Pointer) interface{} {
ptr = f.getPtr(ptr)
if ptr == nil {
return nil
}
return *(*string)(ptr)
}
} else {
f.valueGetter = func(ptr unsafe.Pointer) interface{} {
v := f.packElemFrom(ptr)
if v.IsValid() {
return v.String()
}
return nil
}
}
}
func (f *fieldVM) setLengthGetter() {
f.valueGetter = func(ptr unsafe.Pointer) interface{} {
v := f.packElemFrom(ptr)
if v.IsValid() {
return v.Interface()
}
return nil
}
}
func (f *fieldVM) setUnsupportGetter() {
f.valueGetter = func(ptr unsafe.Pointer) interface{} {
raw := f.packRawFrom(ptr)
if safeIsNil(raw) {
return nil
}
v := raw
for i := 0; i < f.ptrDeep; i++ {
v = v.Elem()
}
for v.Kind() == reflect.Interface {
v = v.Elem()
}
return anyValueGetter(raw, v)
}
}
func (vm *VM) getStructType(t reflect.Type) (reflect.Type, error) {
structType := t
for structType.Kind() == reflect.Ptr {
structType = structType.Elem()
}
if structType.Kind() != reflect.Struct {
return nil, fmt.Errorf("unsupport type: %s", t.String())
}
return structType, nil
}
func (s *structVM) newTagExpr(ptr unsafe.Pointer, path string) *TagExpr {
te := &TagExpr{
s: s,
ptr: ptr,
sub: make(map[string]*TagExpr, 8),
path: strings.TrimPrefix(path, "."),
}
return te
}
// TagExpr struct tag expression evaluator
type TagExpr struct {
s *structVM
ptr unsafe.Pointer
sub map[string]*TagExpr
path string
}
// 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 (t *TagExpr) EvalFloat(exprSelector string) float64 {
r, _ := t.Eval(exprSelector).(float64)
return r
}
// EvalString evaluates the value of the struct tag expression by the selector expression.
// NOTE:
// If the expression value type is not string, return "".
func (t *TagExpr) EvalString(exprSelector string) string {
r, _ := t.Eval(exprSelector).(string)
return r
}
// 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 (t *TagExpr) EvalBool(exprSelector string) bool {
return FakeBool(t.Eval(exprSelector))
}
// FakeBool fakes any type as a boolean.
func FakeBool(v interface{}) bool {
switch r := v.(type) {
case float64:
return r != 0
case string:
return r != ""
case bool:
return r
case nil:
return false
default:
return true
}
}
// Field returns the field handler specified by the selector.
func (t *TagExpr) Field(fieldSelector string) (fh *FieldHandler, found bool) {
f, ok := t.s.fields[fieldSelector]
if !ok {
return nil, false
}
return newFieldHandler(t, fieldSelector, f), true
}
// RangeFields loop through each field.
// When fn returns false, interrupt traversal and return false.
func (t *TagExpr) RangeFields(fn func(*FieldHandler) bool) bool {
if list := t.s.fieldSelectorList; len(list) > 0 {
fields := t.s.fields
for _, fieldSelector := range list {
if !fn(newFieldHandler(t, fieldSelector, fields[fieldSelector])) {
return false
}
}
}
return true
}
// 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 (t *TagExpr) Eval(exprSelector string) interface{} {
expr, ok := t.s.exprs[exprSelector]
if !ok {
// Compatible with single mode or the expression with the name @
if strings.HasSuffix(exprSelector, ExprNameSeparator) {
exprSelector = exprSelector[:len(exprSelector)-1]
if strings.HasSuffix(exprSelector, ExprNameSeparator) {
exprSelector = exprSelector[:len(exprSelector)-1]
}
expr, ok = t.s.exprs[exprSelector]
}
if !ok {
return nil
}
}
dir, base := splitFieldSelector(exprSelector)
targetTagExpr, err := t.checkout(dir)
if err != nil {
return nil
}
return expr.run(base, targetTagExpr)
}
// Range loop through each tag expression.
// When fn returns false, interrupt traversal and return false.
// NOTE:
// eval result types: float64, string, bool, nil
func (t *TagExpr) Range(fn func(*ExprHandler) error) error {
var err error
if list := t.s.exprSelectorList; len(list) > 0 {
for _, es := range list {
dir, base := splitFieldSelector(es)
targetTagExpr, err := t.checkout(dir)
if err != nil {
continue
}
err = fn(newExprHandler(t, targetTagExpr, base, es))
if err != nil {
return err
}
}
}
ptr := t.ptr
if list := t.s.fieldsWithIndirectStructVM; len(list) > 0 {
for _, f := range list {
v := f.packElemFrom(ptr)
if !v.IsValid() {
continue
}
omitNil := f.tagOp == tagOmitNil
mapKeyStructVM := f.mapKeyStructVM
mapOrSliceElemStructVM := f.mapOrSliceElemStructVM
valueIface := f.mapOrSliceIfaceKinds[0]
keyIface := f.mapOrSliceIfaceKinds[1]
if f.elemKind == reflect.Map &&
(mapOrSliceElemStructVM != nil || mapKeyStructVM != nil || valueIface || keyIface) {
keyPath := f.fieldSelector + FieldSeparator + "{}"
for _, key := range v.MapKeys() {
if mapKeyStructVM != nil {
p := unsafe.Pointer(tpack.From(derefValue(key)).Pointer())
if omitNil && p == nil {
continue
}
err = mapKeyStructVM.newTagExpr(p, keyPath).Range(fn)
if err != nil {
return err
}
} else if keyIface {
err = t.subRange(omitNil, keyPath, key, fn)
if err != nil {
return err
}
}
if mapOrSliceElemStructVM != nil {
p := unsafe.Pointer(tpack.From(derefValue(v.MapIndex(key))).Pointer())
if omitNil && p == nil {
continue
}
err = mapOrSliceElemStructVM.newTagExpr(p, f.fieldSelector+"{"+key.String()+"}").Range(fn)
if err != nil {
return err
}
} else if valueIface {
err = t.subRange(omitNil, f.fieldSelector+"{"+key.String()+"}", v.MapIndex(key), fn)
if err != nil {
return err
}
}
}
} else if mapOrSliceElemStructVM != nil || valueIface {
// slice or array
for i := v.Len() - 1; i >= 0; i-- {
if mapOrSliceElemStructVM != nil {
p := unsafe.Pointer(tpack.From(derefValue(v.Index(i))).Pointer())
if omitNil && p == nil {
continue
}
err = mapOrSliceElemStructVM.newTagExpr(p, f.fieldSelector+"["+strconv.Itoa(i)+"]").Range(fn)
if err != nil {
return err
}
} else if valueIface {
err = t.subRange(omitNil, f.fieldSelector+"["+strconv.Itoa(i)+"]", v.Index(i), fn)
if err != nil {
return err
}
}
}
}
}
}
if list := t.s.ifaceTagExprGetters; len(list) > 0 {
for _, getter := range list {
err = getter(ptr, "", func(te *TagExpr, err error) error {
if err != nil {
return err
}
return te.Range(fn)
})
if err != nil {
return err
}
}
}
return nil
}
func (t *TagExpr) subRange(omitNil bool, path string, value reflect.Value, fn func(*ExprHandler) error) error {
return t.s.vm.subRunAll(omitNil, path, value, func(te *TagExpr, err error) error {
if err != nil {
return err
}
return te.Range(fn)
})
}
var (
errFieldSelector = errors.New("field selector does not exist")
errOmitNil = errors.New("omit nil")
)
func (t *TagExpr) checkout(fs string) (*TagExpr, error) {
if fs == "" {
return t, nil
}
subTagExpr, ok := t.sub[fs]
if ok {
if subTagExpr == nil {
return nil, errOmitNil
}
return subTagExpr, nil
}
f, ok := t.s.fields[fs]
if !ok {
return nil, errFieldSelector
}
ptr := f.getElemPtr(t.ptr)
if f.tagOp == tagOmitNil && unsafe.Pointer(ptr) == nil {
t.sub[fs] = nil
return nil, errOmitNil
}
subTagExpr = f.origin.newTagExpr(ptr, t.path)
t.sub[fs] = subTagExpr
return subTagExpr, nil
}
func (t *TagExpr) getValue(fieldSelector string, subFields []interface{}) (v interface{}) {
f := t.s.fields[fieldSelector]
if f == nil {
return nil
}
if f.valueGetter == nil {
return nil
}
v = f.valueGetter(t.ptr)
if v == nil {
return nil
}
if len(subFields) == 0 {
return v
}
vv := reflect.ValueOf(v)
var kind reflect.Kind
for i, k := range subFields {
kind = vv.Kind()
for kind == reflect.Ptr || kind == reflect.Interface {
vv = vv.Elem()
kind = vv.Kind()
}
switch kind {
case reflect.Slice, reflect.Array, reflect.String:
if float, ok := k.(float64); ok {
idx := int(float)
if idx >= vv.Len() {
return nil
}
vv = vv.Index(idx)
} else {
return nil
}
case reflect.Map:
k := safeConvert(reflect.ValueOf(k), vv.Type().Key())
if !k.IsValid() {
return nil
}
vv = vv.MapIndex(k)
case reflect.Struct:
if float, ok := k.(float64); ok {
idx := int(float)
if idx < 0 || idx >= vv.NumField() {
return nil
}
vv = vv.Field(idx)
} else if str, ok := k.(string); ok {
vv = vv.FieldByName(str)
} else {
return nil
}
default:
if i < len(subFields)-1 {
return nil
}
}
if !vv.IsValid() {
return nil
}
}
raw := vv
for vv.Kind() == reflect.Ptr || vv.Kind() == reflect.Interface {
vv = vv.Elem()
}
return anyValueGetter(raw, vv)
}
func safeConvert(v reflect.Value, t reflect.Type) reflect.Value {
defer func() { recover() }()
return v.Convert(t)
}
var float64Type = reflect.TypeOf(float64(0))
func splitFieldSelector(selector string) (dir, base string) {
idx := strings.LastIndex(selector, ExprNameSeparator)
if idx != -1 {
selector = selector[:idx]
}
idx = strings.LastIndex(selector, FieldSeparator)
if idx != -1 {
return selector[:idx], selector[idx+1:]
}
return "", selector
}
func getFloat64(kind reflect.Kind, p unsafe.Pointer) interface{} {
switch kind {
case reflect.Float32:
return float64(*(*float32)(p))
case reflect.Float64:
return *(*float64)(p)
case reflect.Int:
return float64(*(*int)(p))
case reflect.Int8:
return float64(*(*int8)(p))
case reflect.Int16:
return float64(*(*int16)(p))
case reflect.Int32:
return float64(*(*int32)(p))
case reflect.Int64:
return float64(*(*int64)(p))
case reflect.Uint:
return float64(*(*uint)(p))
case reflect.Uint8:
return float64(*(*uint8)(p))
case reflect.Uint16:
return float64(*(*uint16)(p))
case reflect.Uint32:
return float64(*(*uint32)(p))
case reflect.Uint64:
return float64(*(*uint64)(p))
case reflect.Uintptr:
return float64(*(*uintptr)(p))
}
return nil
}
func anyValueGetter(raw, elem reflect.Value) interface{} {
if !elem.IsValid() || !raw.IsValid() {
return nil
}
kind := elem.Kind()
switch kind {
case reflect.Float32, reflect.Float64,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
if elem.CanAddr() {
return getFloat64(kind, unsafe.Pointer(elem.UnsafeAddr()))
}
switch kind {
case reflect.Float32, reflect.Float64:
return elem.Float()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(elem.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return float64(elem.Uint())
}
case reflect.String:
return elem.String()
case reflect.Bool:
return elem.Bool()
}
if raw.CanInterface() {
return raw.Interface()
}
return nil
}
func safeIsNil(v reflect.Value) bool {
if !v.IsValid() {
return true
}
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr,
reflect.UnsafePointer, reflect.Interface, reflect.Slice:
return v.IsNil()
}
return false
}
//go:nocheckptr
func ptrElem(ptr unsafe.Pointer) unsafe.Pointer {
return unsafe.Pointer(*(*uintptr)(ptr))
}
func derefType(t reflect.Type) reflect.Type {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t
}
func derefValue(v reflect.Value) reflect.Value {
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
return v
}
1
https://gitee.com/mirrors/go-tagexpr.git
git@gitee.com:mirrors/go-tagexpr.git
mirrors
go-tagexpr
go-tagexpr
v2.7.4

搜索帮助