github.com/bytedance/go-tagexpr/v2@v2.9.8/spec_func.go (about)

     1  // Copyright 2019 Bytedance Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package tagexpr
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"reflect"
    21  	"regexp"
    22  	"strings"
    23  
    24  	"github.com/andeya/goutil/errors"
    25  )
    26  
    27  // --------------------------- Custom function ---------------------------
    28  
    29  var funcList = map[string]func(p *Expr, expr *string) ExprNode{}
    30  
    31  // MustRegFunc registers function expression.
    32  // NOTE:
    33  //
    34  //	example: len($), regexp("\\d") or regexp("\\d",$);
    35  //	If @force=true, allow to cover the existed same @funcName;
    36  //	The go number types always are float64;
    37  //	The go string types always are string;
    38  //	Panic if there is an error.
    39  func MustRegFunc(funcName string, fn func(...interface{}) interface{}, force ...bool) {
    40  	err := RegFunc(funcName, fn, force...)
    41  	if err != nil {
    42  		panic(err)
    43  	}
    44  }
    45  
    46  // RegFunc registers function expression.
    47  // NOTE:
    48  //
    49  //	example: len($), regexp("\\d") or regexp("\\d",$);
    50  //	If @force=true, allow to cover the existed same @funcName;
    51  //	The go number types always are float64;
    52  //	The go string types always are string.
    53  func RegFunc(funcName string, fn func(...interface{}) interface{}, force ...bool) error {
    54  	if len(force) == 0 || !force[0] {
    55  		_, ok := funcList[funcName]
    56  		if ok {
    57  			return errors.Errorf("duplicate registration expression function: %s", funcName)
    58  		}
    59  	}
    60  	funcList[funcName] = newFunc(funcName, fn)
    61  	return nil
    62  }
    63  
    64  func (p *Expr) parseFuncSign(funcName string, expr *string) (boolOpposite *bool, signOpposite *bool, args []ExprNode, found bool) {
    65  	prefix := funcName + "("
    66  	length := len(funcName)
    67  	last, boolOpposite, signOpposite := getBoolAndSignOpposite(expr)
    68  	if !strings.HasPrefix(last, prefix) {
    69  		return
    70  	}
    71  	*expr = last[length:]
    72  	lastStr := *expr
    73  	subExprNode := readPairedSymbol(expr, '(', ')')
    74  	if subExprNode == nil {
    75  		return
    76  	}
    77  	*subExprNode = "," + *subExprNode
    78  	for {
    79  		if strings.HasPrefix(*subExprNode, ",") {
    80  			*subExprNode = (*subExprNode)[1:]
    81  			operand := newGroupExprNode()
    82  			err := p.parseExprNode(trimLeftSpace(subExprNode), operand)
    83  			if err != nil {
    84  				*expr = lastStr
    85  				return
    86  			}
    87  			sortPriority(operand)
    88  			args = append(args, operand)
    89  		} else {
    90  			*expr = lastStr
    91  			return
    92  		}
    93  		trimLeftSpace(subExprNode)
    94  		if len(*subExprNode) == 0 {
    95  			found = true
    96  			return
    97  		}
    98  	}
    99  }
   100  
   101  func newFunc(funcName string, fn func(...interface{}) interface{}) func(*Expr, *string) ExprNode {
   102  	return func(p *Expr, expr *string) ExprNode {
   103  		boolOpposite, signOpposite, args, found := p.parseFuncSign(funcName, expr)
   104  		if !found {
   105  			return nil
   106  		}
   107  		return &funcExprNode{
   108  			fn:           fn,
   109  			boolOpposite: boolOpposite,
   110  			signOpposite: signOpposite,
   111  			args:         args,
   112  		}
   113  	}
   114  }
   115  
   116  type funcExprNode struct {
   117  	exprBackground
   118  	args         []ExprNode
   119  	fn           func(...interface{}) interface{}
   120  	boolOpposite *bool
   121  	signOpposite *bool
   122  }
   123  
   124  func (f *funcExprNode) String() string {
   125  	return "func()"
   126  }
   127  
   128  func (f *funcExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} {
   129  	var args []interface{}
   130  	if n := len(f.args); n > 0 {
   131  		args = make([]interface{}, n)
   132  		for k, v := range f.args {
   133  			args[k] = v.Run(ctx, currField, tagExpr)
   134  		}
   135  	}
   136  	return realValue(f.fn(args...), f.boolOpposite, f.signOpposite)
   137  }
   138  
   139  // --------------------------- Built-in function ---------------------------
   140  func init() {
   141  	funcList["regexp"] = readRegexpFuncExprNode
   142  	funcList["sprintf"] = readSprintfFuncExprNode
   143  	funcList["range"] = readRangeFuncExprNode
   144  	// len: Built-in function len, the length of struct field X
   145  	MustRegFunc("len", func(args ...interface{}) (n interface{}) {
   146  		if len(args) != 1 {
   147  			return 0
   148  		}
   149  		v := args[0]
   150  		switch e := v.(type) {
   151  		case string:
   152  			return float64(len(e))
   153  		case float64, bool, nil:
   154  			return 0
   155  		}
   156  		defer func() {
   157  			if recover() != nil {
   158  				n = 0
   159  			}
   160  		}()
   161  		return float64(reflect.ValueOf(v).Len())
   162  	}, true)
   163  	// mblen: get the length of string field X (character number)
   164  	MustRegFunc("mblen", func(args ...interface{}) (n interface{}) {
   165  		if len(args) != 1 {
   166  			return 0
   167  		}
   168  		v := args[0]
   169  		switch e := v.(type) {
   170  		case string:
   171  			return float64(len([]rune(e)))
   172  		case float64, bool, nil:
   173  			return 0
   174  		}
   175  		defer func() {
   176  			if recover() != nil {
   177  				n = 0
   178  			}
   179  		}()
   180  		return float64(reflect.ValueOf(v).Len())
   181  	}, true)
   182  
   183  	// in: Check if the first parameter is one of the enumerated parameters
   184  	MustRegFunc("in", func(args ...interface{}) interface{} {
   185  		switch len(args) {
   186  		case 0:
   187  			return true
   188  		case 1:
   189  			return false
   190  		default:
   191  			elem := args[0]
   192  			set := args[1:]
   193  			for _, e := range set {
   194  				if elem == e {
   195  					return true
   196  				}
   197  			}
   198  			return false
   199  		}
   200  	}, true)
   201  }
   202  
   203  type regexpFuncExprNode struct {
   204  	exprBackground
   205  	re           *regexp.Regexp
   206  	boolOpposite bool
   207  }
   208  
   209  func (re *regexpFuncExprNode) String() string {
   210  	return "regexp()"
   211  }
   212  
   213  func readRegexpFuncExprNode(p *Expr, expr *string) ExprNode {
   214  	last, boolOpposite, _ := getBoolAndSignOpposite(expr)
   215  	if !strings.HasPrefix(last, "regexp(") {
   216  		return nil
   217  	}
   218  	*expr = last[6:]
   219  	lastStr := *expr
   220  	subExprNode := readPairedSymbol(expr, '(', ')')
   221  	if subExprNode == nil {
   222  		return nil
   223  	}
   224  	s := readPairedSymbol(trimLeftSpace(subExprNode), '\'', '\'')
   225  	if s == nil {
   226  		*expr = lastStr
   227  		return nil
   228  	}
   229  	rege, err := regexp.Compile(*s)
   230  	if err != nil {
   231  		*expr = lastStr
   232  		return nil
   233  	}
   234  	operand := newGroupExprNode()
   235  	trimLeftSpace(subExprNode)
   236  	if strings.HasPrefix(*subExprNode, ",") {
   237  		*subExprNode = (*subExprNode)[1:]
   238  		err = p.parseExprNode(trimLeftSpace(subExprNode), operand)
   239  		if err != nil {
   240  			*expr = lastStr
   241  			return nil
   242  		}
   243  	} else {
   244  		var currFieldVal = "$"
   245  		p.parseExprNode(&currFieldVal, operand)
   246  	}
   247  	trimLeftSpace(subExprNode)
   248  	if *subExprNode != "" {
   249  		*expr = lastStr
   250  		return nil
   251  	}
   252  	e := &regexpFuncExprNode{
   253  		re: rege,
   254  	}
   255  	if boolOpposite != nil {
   256  		e.boolOpposite = *boolOpposite
   257  	}
   258  	e.SetRightOperand(operand)
   259  	return e
   260  }
   261  
   262  func (re *regexpFuncExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} {
   263  	param := re.rightOperand.Run(ctx, currField, tagExpr)
   264  	switch v := param.(type) {
   265  	case string:
   266  		bol := re.re.MatchString(v)
   267  		if re.boolOpposite {
   268  			return !bol
   269  		}
   270  		return bol
   271  	case float64, bool:
   272  		return false
   273  	}
   274  	v := reflect.ValueOf(param)
   275  	if v.Kind() == reflect.String {
   276  		bol := re.re.MatchString(v.String())
   277  		if re.boolOpposite {
   278  			return !bol
   279  		}
   280  		return bol
   281  	}
   282  	return false
   283  }
   284  
   285  type sprintfFuncExprNode struct {
   286  	exprBackground
   287  	format string
   288  	args   []ExprNode
   289  }
   290  
   291  func (se *sprintfFuncExprNode) String() string {
   292  	return "sprintf()"
   293  }
   294  
   295  func readSprintfFuncExprNode(p *Expr, expr *string) ExprNode {
   296  	if !strings.HasPrefix(*expr, "sprintf(") {
   297  		return nil
   298  	}
   299  	*expr = (*expr)[7:]
   300  	lastStr := *expr
   301  	subExprNode := readPairedSymbol(expr, '(', ')')
   302  	if subExprNode == nil {
   303  		return nil
   304  	}
   305  	format := readPairedSymbol(trimLeftSpace(subExprNode), '\'', '\'')
   306  	if format == nil {
   307  		*expr = lastStr
   308  		return nil
   309  	}
   310  	e := &sprintfFuncExprNode{
   311  		format: *format,
   312  	}
   313  	for {
   314  		trimLeftSpace(subExprNode)
   315  		if len(*subExprNode) == 0 {
   316  			return e
   317  		}
   318  		if strings.HasPrefix(*subExprNode, ",") {
   319  			*subExprNode = (*subExprNode)[1:]
   320  			operand := newGroupExprNode()
   321  			err := p.parseExprNode(trimLeftSpace(subExprNode), operand)
   322  			if err != nil {
   323  				*expr = lastStr
   324  				return nil
   325  			}
   326  			sortPriority(operand)
   327  			e.args = append(e.args, operand)
   328  		} else {
   329  			*expr = lastStr
   330  			return nil
   331  		}
   332  	}
   333  }
   334  
   335  func (se *sprintfFuncExprNode) Run(ctx context.Context, currField string, tagExpr *TagExpr) interface{} {
   336  	var args []interface{}
   337  	if n := len(se.args); n > 0 {
   338  		args = make([]interface{}, n)
   339  		for i, e := range se.args {
   340  			args[i] = e.Run(ctx, currField, tagExpr)
   341  		}
   342  	}
   343  	return fmt.Sprintf(se.format, args...)
   344  }