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 }