github.com/easysoft/zendata@v0.0.0-20240513203326-705bd5a7fd67/internal/pkg/service/expression.go (about)

     1  package service
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/Knetic/govaluate"
    10  	"github.com/easysoft/zendata/internal/pkg/domain"
    11  	"github.com/easysoft/zendata/internal/pkg/helper"
    12  	logUtils "github.com/easysoft/zendata/pkg/utils/log"
    13  	"github.com/easysoft/zendata/pkg/utils/vari"
    14  	"github.com/mattn/go-runewidth"
    15  )
    16  
    17  type ExpressionService struct {
    18  }
    19  
    20  func (s *ExpressionService) GenExpressionValues(field domain.DefField) (ret []interface{}) {
    21  	valuesMap := vari.GlobalVars.FieldNameToValuesMap
    22  	fieldMap := vari.GlobalVars.FieldNameToFieldMap
    23  
    24  	exp := field.Value
    25  
    26  	reg := regexp.MustCompile(`\$([_,a-z,A-Z][_,a-z,A-Z,0-9]*)`)
    27  	arr := reg.FindAllStringSubmatch(exp, -1)
    28  	if arr == nil {
    29  		return field.Values
    30  	}
    31  
    32  	total := 1
    33  	typeGrade := map[string]int{
    34  		"int":    0,
    35  		"float":  1,
    36  		"string": 2,
    37  	}
    38  	expressionType := "int"
    39  	if strings.Contains(exp, "'") {
    40  		expressionType = "string"
    41  	}
    42  	for _, items := range arr { // computer total
    43  		placeholder := items[0]
    44  		fieldName := items[1]
    45  		exp = strings.Replace(exp, placeholder, fieldName, 1)
    46  
    47  		size := len(valuesMap[fieldName])
    48  		if total < size {
    49  			total = size
    50  		}
    51  
    52  		// judge type of expression
    53  		referField := fieldMap[fieldName]
    54  		tp := s.getValuesType(valuesMap[fieldName], referField.Prefix, referField.Postfix)
    55  		if typeGrade[tp] > typeGrade[expressionType] {
    56  			expressionType = tp
    57  		}
    58  	}
    59  
    60  	for i := 0; i < total; i++ {
    61  		params := make(map[string]interface{})
    62  
    63  		for _, items := range arr {
    64  			fieldName := items[1]
    65  			referValues := valuesMap[fieldName]
    66  			referField := fieldMap[fieldName]
    67  
    68  			valStr := "N/A"
    69  			var val interface{}
    70  			if len(referValues) > 0 {
    71  				valStr = fmt.Sprintf("%v", referValues[i%len(referValues)])
    72  				valStr = strings.TrimLeft(valStr, referField.Prefix)
    73  				valStr = strings.TrimRight(valStr, referField.Postfix)
    74  
    75  				val = s.parseValue(valStr, expressionType)
    76  			}
    77  			params[fieldName] = val
    78  		}
    79  
    80  		expr, err := govaluate.NewEvaluableExpression(exp)
    81  		if err != nil {
    82  			logUtils.PrintErrMsg(err.Error())
    83  			ret = append(ret, "ERR")
    84  		} else {
    85  			result, err := expr.Evaluate(params)
    86  			if err != nil {
    87  				logUtils.PrintErrMsg(err.Error())
    88  			}
    89  
    90  			mask := ""
    91  			if expressionType == "int" {
    92  				mask = "%.0f"
    93  			} else if expressionType == "float" {
    94  				mask = "%f"
    95  			} else {
    96  				mask = "%s"
    97  			}
    98  
    99  			str := fmt.Sprintf(mask, result)
   100  			if field.Length > runewidth.StringWidth(str) {
   101  				str = helper.AddPad(str, field)
   102  			}
   103  			str = field.Prefix + str + field.Postfix
   104  			ret = append(ret, str)
   105  		}
   106  	}
   107  
   108  	return
   109  }
   110  
   111  func (s *ExpressionService) ReplaceVariableValues(exp string, valuesMap map[string][]interface{}) (ret []string) {
   112  	reg := regexp.MustCompile(`\$\{([_,a-z,A-Z,0-9]+)\}`)
   113  	arr := reg.FindAllStringSubmatch(exp, -1)
   114  
   115  	total := 1
   116  	for _, items := range arr { // computer total
   117  		fieldName := items[1]
   118  
   119  		size := len(valuesMap[fieldName])
   120  		if total < size {
   121  			total = size
   122  		}
   123  	}
   124  
   125  	for i := 0; i < total; i++ {
   126  		item := exp
   127  		for _, items := range arr {
   128  			fieldSlot := items[0]
   129  			fieldName := items[1]
   130  			referValues := valuesMap[fieldName]
   131  			referField := vari.GlobalVars.TopFieldMap[fieldName]
   132  
   133  			valStr := "N/A"
   134  			if len(referValues) > 0 {
   135  				valStr = referValues[i%len(referValues)].(string)
   136  				valStr = strings.TrimLeft(valStr, referField.Prefix)
   137  				valStr = strings.TrimRight(valStr, referField.Postfix)
   138  			}
   139  
   140  			item = strings.ReplaceAll(item, fieldSlot, valStr)
   141  		}
   142  
   143  		ret = append(ret, item)
   144  	}
   145  
   146  	return
   147  }
   148  
   149  func (s *ExpressionService) getValuesType(values []interface{}, prefix string, postfix string) (tp string) {
   150  	tool := map[string]int{
   151  		"int":    0,
   152  		"float":  1,
   153  		"string": 2,
   154  	}
   155  	tp = "int"
   156  	for _, item := range values {
   157  		valStr := strings.TrimLeft(fmt.Sprintf("%v", item), prefix)
   158  		valStr = strings.TrimRight(valStr, postfix)
   159  		_, t := s.getType(valStr)
   160  		if tool[t] > tool[tp] {
   161  			tp = t
   162  		}
   163  		if tp == "string" {
   164  			break
   165  		}
   166  	}
   167  	return
   168  }
   169  
   170  func (s *ExpressionService) getType(str string) (val interface{}, tp string) {
   171  	val, errInt := strconv.ParseInt(str, 0, 64)
   172  	if errInt == nil {
   173  		tp = "int"
   174  		return
   175  	}
   176  
   177  	val, errFloat := strconv.ParseFloat(str, 64)
   178  	if errFloat == nil {
   179  		tp = "float"
   180  		return
   181  	}
   182  	val = str
   183  	tp = "string"
   184  	return
   185  }
   186  
   187  func (s *ExpressionService) parseValue(str string, tp string) (val interface{}) {
   188  	var err error
   189  	if tp == "int" {
   190  		val, err = strconv.ParseInt(str, 0, 64)
   191  		if err != nil {
   192  			val = 0
   193  		}
   194  	} else if tp == "float" {
   195  		val, err = strconv.ParseFloat(str, 64)
   196  		if err != nil {
   197  			val = 0.0
   198  		}
   199  	} else {
   200  		val = str
   201  	}
   202  	return
   203  }