github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/lang/funcs/string.go (about) 1 package funcs 2 3 import ( 4 "fmt" 5 "regexp" 6 "sort" 7 "strings" 8 9 "github.com/zclconf/go-cty/cty" 10 "github.com/zclconf/go-cty/cty/function" 11 "github.com/zclconf/go-cty/cty/gocty" 12 ) 13 14 var JoinFunc = function.New(&function.Spec{ 15 Params: []function.Parameter{ 16 { 17 Name: "separator", 18 Type: cty.String, 19 }, 20 }, 21 VarParam: &function.Parameter{ 22 Name: "lists", 23 Type: cty.List(cty.String), 24 }, 25 Type: function.StaticReturnType(cty.String), 26 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 27 sep := args[0].AsString() 28 listVals := args[1:] 29 if len(listVals) < 1 { 30 return cty.UnknownVal(cty.String), fmt.Errorf("at least one list is required") 31 } 32 33 l := 0 34 for _, list := range listVals { 35 if !list.IsWhollyKnown() { 36 return cty.UnknownVal(cty.String), nil 37 } 38 l += list.LengthInt() 39 } 40 41 items := make([]string, 0, l) 42 for ai, list := range listVals { 43 ei := 0 44 for it := list.ElementIterator(); it.Next(); { 45 _, val := it.Element() 46 if val.IsNull() { 47 if len(listVals) > 1 { 48 return cty.UnknownVal(cty.String), function.NewArgErrorf(ai+1, "element %d of list %d is null; cannot concatenate null values", ei, ai+1) 49 } 50 return cty.UnknownVal(cty.String), function.NewArgErrorf(ai+1, "element %d is null; cannot concatenate null values", ei) 51 } 52 items = append(items, val.AsString()) 53 ei++ 54 } 55 } 56 57 return cty.StringVal(strings.Join(items, sep)), nil 58 }, 59 }) 60 61 var SortFunc = function.New(&function.Spec{ 62 Params: []function.Parameter{ 63 { 64 Name: "list", 65 Type: cty.List(cty.String), 66 }, 67 }, 68 Type: function.StaticReturnType(cty.List(cty.String)), 69 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 70 listVal := args[0] 71 72 if !listVal.IsWhollyKnown() { 73 // If some of the element values aren't known yet then we 74 // can't yet preduct the order of the result. 75 return cty.UnknownVal(retType), nil 76 } 77 if listVal.LengthInt() == 0 { // Easy path 78 return listVal, nil 79 } 80 81 list := make([]string, 0, listVal.LengthInt()) 82 for it := listVal.ElementIterator(); it.Next(); { 83 iv, v := it.Element() 84 if v.IsNull() { 85 return cty.UnknownVal(retType), fmt.Errorf("given list element %s is null; a null string cannot be sorted", iv.AsBigFloat().String()) 86 } 87 list = append(list, v.AsString()) 88 } 89 90 sort.Strings(list) 91 retVals := make([]cty.Value, len(list)) 92 for i, s := range list { 93 retVals[i] = cty.StringVal(s) 94 } 95 return cty.ListVal(retVals), nil 96 }, 97 }) 98 99 var SplitFunc = function.New(&function.Spec{ 100 Params: []function.Parameter{ 101 { 102 Name: "separator", 103 Type: cty.String, 104 }, 105 { 106 Name: "str", 107 Type: cty.String, 108 }, 109 }, 110 Type: function.StaticReturnType(cty.List(cty.String)), 111 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 112 sep := args[0].AsString() 113 str := args[1].AsString() 114 elems := strings.Split(str, sep) 115 elemVals := make([]cty.Value, len(elems)) 116 for i, s := range elems { 117 elemVals[i] = cty.StringVal(s) 118 } 119 if len(elemVals) == 0 { 120 return cty.ListValEmpty(cty.String), nil 121 } 122 return cty.ListVal(elemVals), nil 123 }, 124 }) 125 126 // ChompFunc constructions a function that removes newline characters at the end of a string. 127 var ChompFunc = function.New(&function.Spec{ 128 Params: []function.Parameter{ 129 { 130 Name: "str", 131 Type: cty.String, 132 }, 133 }, 134 Type: function.StaticReturnType(cty.String), 135 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 136 newlines := regexp.MustCompile(`(?:\r\n?|\n)*\z`) 137 return cty.StringVal(newlines.ReplaceAllString(args[0].AsString(), "")), nil 138 }, 139 }) 140 141 // IndentFunc constructions a function that adds a given number of spaces to the 142 // beginnings of all but the first line in a given multi-line string. 143 var IndentFunc = function.New(&function.Spec{ 144 Params: []function.Parameter{ 145 { 146 Name: "spaces", 147 Type: cty.Number, 148 }, 149 { 150 Name: "str", 151 Type: cty.String, 152 }, 153 }, 154 Type: function.StaticReturnType(cty.String), 155 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 156 var spaces int 157 if err := gocty.FromCtyValue(args[0], &spaces); err != nil { 158 return cty.UnknownVal(cty.String), err 159 } 160 data := args[1].AsString() 161 pad := strings.Repeat(" ", spaces) 162 return cty.StringVal(strings.Replace(data, "\n", "\n"+pad, -1)), nil 163 }, 164 }) 165 166 // ReplaceFunc constructions a function that searches a given string for another 167 // given substring, and replaces each occurence with a given replacement string. 168 var ReplaceFunc = function.New(&function.Spec{ 169 Params: []function.Parameter{ 170 { 171 Name: "str", 172 Type: cty.String, 173 }, 174 { 175 Name: "substr", 176 Type: cty.String, 177 }, 178 { 179 Name: "replace", 180 Type: cty.String, 181 }, 182 }, 183 Type: function.StaticReturnType(cty.String), 184 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 185 str := args[0].AsString() 186 substr := args[1].AsString() 187 replace := args[2].AsString() 188 189 // We search/replace using a regexp if the string is surrounded 190 // in forward slashes. 191 if len(substr) > 1 && substr[0] == '/' && substr[len(substr)-1] == '/' { 192 re, err := regexp.Compile(substr[1 : len(substr)-1]) 193 if err != nil { 194 return cty.UnknownVal(cty.String), err 195 } 196 197 return cty.StringVal(re.ReplaceAllString(str, replace)), nil 198 } 199 200 return cty.StringVal(strings.Replace(str, substr, replace, -1)), nil 201 }, 202 }) 203 204 // TitleFunc constructions a function that converts the first letter of each word 205 // in the given string to uppercase. 206 var TitleFunc = function.New(&function.Spec{ 207 Params: []function.Parameter{ 208 { 209 Name: "str", 210 Type: cty.String, 211 }, 212 }, 213 Type: function.StaticReturnType(cty.String), 214 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 215 return cty.StringVal(strings.Title(args[0].AsString())), nil 216 }, 217 }) 218 219 // TrimSpaceFunc constructions a function that removes any space characters from 220 // the start and end of the given string. 221 var TrimSpaceFunc = function.New(&function.Spec{ 222 Params: []function.Parameter{ 223 { 224 Name: "str", 225 Type: cty.String, 226 }, 227 }, 228 Type: function.StaticReturnType(cty.String), 229 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 230 return cty.StringVal(strings.TrimSpace(args[0].AsString())), nil 231 }, 232 }) 233 234 // Join concatenates together the string elements of one or more lists with a 235 // given separator. 236 func Join(sep cty.Value, lists ...cty.Value) (cty.Value, error) { 237 args := make([]cty.Value, len(lists)+1) 238 args[0] = sep 239 copy(args[1:], lists) 240 return JoinFunc.Call(args) 241 } 242 243 // Sort re-orders the elements of a given list of strings so that they are 244 // in ascending lexicographical order. 245 func Sort(list cty.Value) (cty.Value, error) { 246 return SortFunc.Call([]cty.Value{list}) 247 } 248 249 // Split divides a given string by a given separator, returning a list of 250 // strings containing the characters between the separator sequences. 251 func Split(sep, str cty.Value) (cty.Value, error) { 252 return SplitFunc.Call([]cty.Value{sep, str}) 253 } 254 255 // Chomp removes newline characters at the end of a string. 256 func Chomp(str cty.Value) (cty.Value, error) { 257 return ChompFunc.Call([]cty.Value{str}) 258 } 259 260 // Indent adds a given number of spaces to the beginnings of all but the first 261 // line in a given multi-line string. 262 func Indent(spaces, str cty.Value) (cty.Value, error) { 263 return IndentFunc.Call([]cty.Value{spaces, str}) 264 } 265 266 // Replace searches a given string for another given substring, 267 // and replaces all occurences with a given replacement string. 268 func Replace(str, substr, replace cty.Value) (cty.Value, error) { 269 return ReplaceFunc.Call([]cty.Value{str, substr, replace}) 270 } 271 272 // Title converts the first letter of each word in the given string to uppercase. 273 func Title(str cty.Value) (cty.Value, error) { 274 return TitleFunc.Call([]cty.Value{str}) 275 } 276 277 // TrimSpace removes any space characters from the start and end of the given string. 278 func TrimSpace(str cty.Value) (cty.Value, error) { 279 return TrimSpaceFunc.Call([]cty.Value{str}) 280 }