github.com/GuanceCloud/cliutils@v1.1.21/pipeline/ptinput/funcs/fn_kv.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  package funcs
     7  
     8  import (
     9  	"fmt"
    10  	"regexp"
    11  	"strings"
    12  	"sync"
    13  
    14  	"github.com/GuanceCloud/cliutils/pipeline/ptinput"
    15  	"github.com/GuanceCloud/platypus/pkg/ast"
    16  	"github.com/GuanceCloud/platypus/pkg/engine/runtime"
    17  	"github.com/GuanceCloud/platypus/pkg/errchain"
    18  )
    19  
    20  var (
    21  	_defaultFieldSplitPattern = regexp.MustCompile(" ")
    22  	_defaultValueSplitPattern = regexp.MustCompile("=")
    23  )
    24  
    25  var _regexpCache = reCache{
    26  	m: map[string]*regexp.Regexp{},
    27  }
    28  
    29  type reCache struct {
    30  	m map[string]*regexp.Regexp
    31  	sync.RWMutex
    32  }
    33  
    34  func (c *reCache) set(p string) error {
    35  	c.Lock()
    36  	defer c.Unlock()
    37  
    38  	if c.m == nil {
    39  		return fmt.Errorf("nil ptr")
    40  	}
    41  
    42  	if r, err := regexp.Compile(p); err != nil {
    43  		return err
    44  	} else {
    45  		c.m[p] = r
    46  	}
    47  
    48  	return nil
    49  }
    50  
    51  func (c *reCache) get(p string) (*regexp.Regexp, bool) {
    52  	c.RLock()
    53  	defer c.RUnlock()
    54  	if c.m != nil {
    55  		v, ok := c.m[p]
    56  		return v, ok
    57  	}
    58  
    59  	return nil, false
    60  }
    61  
    62  func KVSplitChecking(ctx *runtime.Task, funcExpr *ast.CallExpr) *errchain.PlError {
    63  	if err := normalizeFuncArgsDeprecated(funcExpr, []string{
    64  		"key", "field_split_pattern", "value_split_pattern",
    65  		"trim_key", "trim_value", "include_keys", "prefix",
    66  	}, 1); err != nil {
    67  		return runtime.NewRunError(ctx, err.Error(), funcExpr.NamePos)
    68  	}
    69  
    70  	if _, err := getKeyName(funcExpr.Param[0]); err != nil {
    71  		return runtime.NewRunError(ctx, err.Error(), funcExpr.Param[0].StartPos())
    72  	}
    73  
    74  	// field_split_pattern
    75  	if funcExpr.Param[1] != nil {
    76  		switch funcExpr.Param[1].NodeType { //nolint:exhaustive
    77  		case ast.TypeStringLiteral:
    78  			p := funcExpr.Param[1].StringLiteral().Val
    79  			if err := _regexpCache.set(p); err != nil {
    80  				return runtime.NewRunError(ctx, err.Error(), funcExpr.Param[1].StartPos())
    81  			}
    82  		default:
    83  			return runtime.NewRunError(ctx, fmt.Sprintf("param field_split_pattern expect StringLiteral, got %s",
    84  				funcExpr.Param[0].NodeType), funcExpr.NamePos)
    85  		}
    86  	}
    87  
    88  	// value_split_pattern
    89  	if funcExpr.Param[2] != nil {
    90  		switch funcExpr.Param[2].NodeType { //nolint:exhaustive
    91  		case ast.TypeStringLiteral:
    92  			p := funcExpr.Param[2].StringLiteral().Val
    93  			if err := _regexpCache.set(p); err != nil {
    94  				return runtime.NewRunError(ctx, err.Error(), funcExpr.Param[2].StartPos())
    95  			}
    96  		default:
    97  			return runtime.NewRunError(ctx, fmt.Sprintf("param value_split_pattern expect StringLiteral, got %s",
    98  				funcExpr.Param[2].NodeType), funcExpr.NamePos)
    99  		}
   100  	}
   101  
   102  	if funcExpr.Param[3] != nil {
   103  		switch funcExpr.Param[3].NodeType { //nolint:exhaustive
   104  		case ast.TypeStringLiteral:
   105  		default:
   106  			return runtime.NewRunError(ctx, fmt.Sprintf("param trim_key expect StringLiteral, got %s",
   107  				funcExpr.Param[3].NodeType), funcExpr.NamePos)
   108  		}
   109  	}
   110  
   111  	if funcExpr.Param[4] != nil {
   112  		switch funcExpr.Param[4].NodeType { //nolint:exhaustive
   113  		case ast.TypeStringLiteral:
   114  		default:
   115  			return runtime.NewRunError(ctx, fmt.Sprintf("param trim_value expect StringLiteral, got %s",
   116  				funcExpr.Param[4].NodeType), funcExpr.NamePos)
   117  		}
   118  	}
   119  
   120  	if funcExpr.Param[5] != nil {
   121  		switch funcExpr.Param[5].NodeType { //nolint:exhaustive
   122  		case ast.TypeListLiteral, ast.TypeIdentifier:
   123  		default:
   124  			return runtime.NewRunError(ctx, fmt.Sprintf("param include_keys expect ListInitExpr or Identifier, got %s",
   125  				funcExpr.Param[5].NodeType), funcExpr.NamePos)
   126  		}
   127  	}
   128  
   129  	if funcExpr.Param[6] != nil {
   130  		switch funcExpr.Param[6].NodeType { //nolint:exhaustive
   131  		case ast.TypeStringLiteral:
   132  		default:
   133  			return runtime.NewRunError(ctx, fmt.Sprintf("param prefix expect StringLiteral, got %s",
   134  				funcExpr.Param[6].NodeType), funcExpr.NamePos)
   135  		}
   136  	}
   137  	return nil
   138  }
   139  
   140  func KVSplit(ctx *runtime.Task, funcExpr *ast.CallExpr) *errchain.PlError {
   141  	key, err := getKeyName(funcExpr.Param[0])
   142  	if err != nil {
   143  		return runtime.NewRunError(ctx, err.Error(), funcExpr.Param[0].StartPos())
   144  	}
   145  
   146  	val, err := ctx.GetKeyConv2Str(key)
   147  	if err != nil {
   148  		ctx.Regs.ReturnAppend(false, ast.Bool)
   149  		return nil
   150  	}
   151  
   152  	var fieldSplit, valueSplit *regexp.Regexp
   153  
   154  	// field_split_pattern
   155  	if funcExpr.Param[1] != nil {
   156  		switch funcExpr.Param[1].NodeType { //nolint:exhaustive
   157  		case ast.TypeStringLiteral:
   158  			p := funcExpr.Param[1].StringLiteral().Val
   159  			var ok bool
   160  
   161  			fieldSplit, ok = _regexpCache.get(p)
   162  			if !ok {
   163  				l.Debugf("field split pattern %s not found", p)
   164  				ctx.Regs.ReturnAppend(false, ast.Bool)
   165  				return nil
   166  			}
   167  		default:
   168  			return runtime.NewRunError(ctx, fmt.Sprintf("param field_split_pattern expect StringLiteral, got %s",
   169  				funcExpr.Param[0].NodeType), funcExpr.NamePos)
   170  		}
   171  	}
   172  
   173  	if funcExpr.Param[2] != nil {
   174  		switch funcExpr.Param[2].NodeType { //nolint:exhaustive
   175  		case ast.TypeStringLiteral:
   176  			p := funcExpr.Param[2].StringLiteral().Val
   177  
   178  			var ok bool
   179  
   180  			valueSplit, ok = _regexpCache.get(p)
   181  			if !ok {
   182  				l.Debugf("value split pattern %s not found", p)
   183  				ctx.Regs.ReturnAppend(false, ast.Bool)
   184  				return nil
   185  			}
   186  		default:
   187  			return runtime.NewRunError(ctx, fmt.Sprintf("param value_split_pattern expect StringLiteral, got %s",
   188  				funcExpr.Param[2].NodeType), funcExpr.NamePos)
   189  		}
   190  	}
   191  
   192  	var trimKey, trimValue string
   193  	if funcExpr.Param[3] != nil {
   194  		switch funcExpr.Param[3].NodeType { //nolint:exhaustive
   195  		case ast.TypeStringLiteral:
   196  			trimKey = funcExpr.Param[3].StringLiteral().Val
   197  		default:
   198  			return runtime.NewRunError(ctx, fmt.Sprintf("param trim_key expect StringLiteral, got %s",
   199  				funcExpr.Param[3].NodeType), funcExpr.NamePos)
   200  		}
   201  	}
   202  
   203  	if funcExpr.Param[4] != nil {
   204  		switch funcExpr.Param[4].NodeType { //nolint:exhaustive
   205  		case ast.TypeStringLiteral:
   206  			trimValue = funcExpr.Param[4].StringLiteral().Val
   207  		default:
   208  			return runtime.NewRunError(ctx, fmt.Sprintf("param trim_value expect StringLiteral, got %s",
   209  				funcExpr.Param[4].NodeType), funcExpr.NamePos)
   210  		}
   211  	}
   212  
   213  	var includeKeys []string
   214  	if funcExpr.Param[5] != nil {
   215  		switch funcExpr.Param[5].NodeType { //nolint:exhaustive
   216  		case ast.TypeListLiteral, ast.TypeIdentifier:
   217  			v, dt, err := runtime.RunStmt(ctx, funcExpr.Param[5])
   218  			if err != nil {
   219  				return err
   220  			}
   221  			if dt != ast.List {
   222  				break
   223  			}
   224  			switch v := v.(type) {
   225  			case []any:
   226  				for _, k := range v {
   227  					if k, ok := k.(string); ok {
   228  						includeKeys = append(includeKeys, k)
   229  					}
   230  				}
   231  			default:
   232  			}
   233  
   234  		default:
   235  			return runtime.NewRunError(ctx, fmt.Sprintf("param include_keys expect ListInitExpr or Identifier, got %s",
   236  				funcExpr.Param[5].NodeType), funcExpr.NamePos)
   237  		}
   238  	}
   239  
   240  	if len(includeKeys) == 0 {
   241  		ctx.Regs.ReturnAppend(false, ast.Bool)
   242  		return nil
   243  	}
   244  
   245  	var prefix string
   246  	if funcExpr.Param[6] != nil {
   247  		switch funcExpr.Param[6].NodeType { //nolint:exhaustive
   248  		case ast.TypeStringLiteral:
   249  			prefix = funcExpr.Param[6].StringLiteral().Val
   250  		default:
   251  			return runtime.NewRunError(ctx, fmt.Sprintf("param prefix expect StringLiteral, got %s",
   252  				funcExpr.Param[6].NodeType), funcExpr.NamePos)
   253  		}
   254  	}
   255  
   256  	result := kvSplit(val, includeKeys, fieldSplit, valueSplit, trimKey, trimValue, prefix)
   257  	if len(result) == 0 {
   258  		ctx.Regs.ReturnAppend(false, ast.Bool)
   259  		return nil
   260  	}
   261  
   262  	for k, v := range result {
   263  		_ = addKey2PtWithVal(ctx.InData(), k, v, ast.String, ptinput.KindPtDefault)
   264  	}
   265  
   266  	ctx.Regs.ReturnAppend(true, ast.Bool)
   267  	return nil
   268  }
   269  
   270  func kvSplit(str string, includeKeys []string, fieldSplit, valueSplit *regexp.Regexp,
   271  	trimKey, trimValue, prefix string,
   272  ) map[string]string {
   273  	if str == "" {
   274  		return nil
   275  	}
   276  
   277  	if fieldSplit == nil {
   278  		fieldSplit = _defaultFieldSplitPattern
   279  	}
   280  
   281  	if valueSplit == nil {
   282  		valueSplit = _defaultValueSplitPattern
   283  	}
   284  
   285  	ks := map[string]struct{}{}
   286  
   287  	for _, v := range includeKeys {
   288  		ks[v] = struct{}{}
   289  	}
   290  
   291  	result := map[string]string{}
   292  	fields := fieldSplit.Split(str, -1)
   293  	for _, field := range fields {
   294  		keyValue := valueSplit.Split(field, 2)
   295  
   296  		if len(keyValue) == 2 {
   297  			// trim key
   298  			if tk := strings.Trim(keyValue[0], trimKey); tk != "" {
   299  				keyValue[0] = tk
   300  			} else {
   301  				continue
   302  			}
   303  
   304  			// !include ? continue : ;
   305  			if len(ks) > 0 {
   306  				if _, ok := ks[keyValue[0]]; !ok {
   307  					continue
   308  				}
   309  			}
   310  
   311  			// trim value
   312  			if trimValue != "" {
   313  				keyValue[1] = strings.Trim(keyValue[1], trimValue)
   314  			}
   315  
   316  			// prefix + key
   317  			if prefix != "" {
   318  				keyValue[0] = prefix + keyValue[0]
   319  			}
   320  
   321  			// append to result
   322  			result[keyValue[0]] = keyValue[1]
   323  		}
   324  	}
   325  	return result
   326  }