github.com/kubeshop/testkube@v1.17.23/pkg/tcl/expressionstcl/call.go (about)

     1  // Copyright 2024 Testkube.
     2  //
     3  // Licensed as a Testkube Pro file under the Testkube Community
     4  // License (the "License"); you may not use this file except in compliance with
     5  // the License. You may obtain a copy of the License at
     6  //
     7  //     https://github.com/kubeshop/testkube/blob/main/licenses/TCL.txt
     8  
     9  package expressionstcl
    10  
    11  import (
    12  	"fmt"
    13  	"maps"
    14  	"strings"
    15  )
    16  
    17  type call struct {
    18  	name string
    19  	args []callArgument
    20  }
    21  
    22  type callArgument struct {
    23  	expr   Expression
    24  	spread bool
    25  }
    26  
    27  func newCall(name string, args []callArgument) Expression {
    28  	for i := range args {
    29  		if args[i].expr == nil {
    30  			args[i].expr = None
    31  		}
    32  	}
    33  	return &call{name: name, args: args}
    34  }
    35  
    36  func (s *call) Type() Type {
    37  	if IsStdFunction(s.name) {
    38  		return GetStdFunctionReturnType(s.name)
    39  	}
    40  	return TypeUnknown
    41  }
    42  
    43  func (s *call) String() string {
    44  	args := make([]string, len(s.args))
    45  	for i, arg := range s.args {
    46  		args[i] = arg.expr.String()
    47  		if arg.spread {
    48  			args[i] += "..."
    49  		}
    50  	}
    51  	return fmt.Sprintf("%s(%s)", s.name, strings.Join(args, ","))
    52  }
    53  
    54  func (s *call) SafeString() string {
    55  	return s.String()
    56  }
    57  
    58  func (s *call) Template() string {
    59  	if s.name == stringCastStdFn {
    60  		args := make([]string, len(s.args))
    61  		for i, a := range s.args {
    62  			args[i] = a.expr.Template()
    63  		}
    64  		return strings.Join(args, "")
    65  	}
    66  	return "{{" + s.String() + "}}"
    67  }
    68  
    69  func (s *call) isResolved() bool {
    70  	for i := range s.args {
    71  		if s.args[i].expr.Static() == nil {
    72  			return false
    73  		}
    74  	}
    75  	return true
    76  }
    77  
    78  func (s *call) resolvedArgs() ([]StaticValue, error) {
    79  	v := make([]StaticValue, 0)
    80  	for _, vv := range s.args {
    81  		value := vv.expr.Static()
    82  		if vv.spread {
    83  			if value.IsNone() {
    84  				continue
    85  			}
    86  			items, err := value.SliceValue()
    87  			if err != nil {
    88  				return nil, fmt.Errorf("spread operator (...) used against non-list parameter: %s", value)
    89  			}
    90  			staticItems := make([]StaticValue, len(items))
    91  			for i := range items {
    92  				staticItems[i] = NewValue(items[i])
    93  			}
    94  			v = append(v, staticItems...)
    95  		} else {
    96  			v = append(v, value)
    97  		}
    98  	}
    99  	return v, nil
   100  }
   101  
   102  func (s *call) SafeResolve(m ...Machine) (v Expression, changed bool, err error) {
   103  	var ch bool
   104  	for i := range s.args {
   105  		s.args[i].expr, ch, err = s.args[i].expr.SafeResolve(m...)
   106  		changed = changed || ch
   107  		if err != nil {
   108  			return nil, changed, err
   109  		}
   110  	}
   111  	if s.isResolved() {
   112  		args, err := s.resolvedArgs()
   113  		if err != nil {
   114  			return nil, true, err
   115  		}
   116  		result, ok, err := StdLibMachine.Call(s.name, args...)
   117  		if ok {
   118  			if err != nil {
   119  				return nil, true, fmt.Errorf("error while calling %s: %s", s.String(), err.Error())
   120  			}
   121  			return result, true, nil
   122  		}
   123  		for i := range m {
   124  			result, ok, err = m[i].Call(s.name, args...)
   125  			if err != nil {
   126  				return nil, true, fmt.Errorf("error while calling %s: %s", s.String(), err.Error())
   127  			}
   128  			if ok {
   129  				return result, true, nil
   130  			}
   131  		}
   132  	}
   133  	return s, changed, nil
   134  }
   135  
   136  func (s *call) Resolve(m ...Machine) (v Expression, err error) {
   137  	return deepResolve(s, m...)
   138  }
   139  
   140  func (s *call) Static() StaticValue {
   141  	return nil
   142  }
   143  
   144  func (s *call) Accessors() map[string]struct{} {
   145  	result := make(map[string]struct{})
   146  	for i := range s.args {
   147  		maps.Copy(result, s.args[i].expr.Accessors())
   148  	}
   149  	return result
   150  }
   151  
   152  func (s *call) Functions() map[string]struct{} {
   153  	result := make(map[string]struct{})
   154  	for i := range s.args {
   155  		maps.Copy(result, s.args[i].expr.Functions())
   156  	}
   157  	result[s.name] = struct{}{}
   158  	return result
   159  }