6 "github.com/zclconf/go-cty/cty"
7 "github.com/zclconf/go-cty/cty/function"
8 "github.com/zclconf/go-cty/cty/gocty"
9 "github.com/apparentlymart/go-textseg/textseg"
12 var UpperFunc = function.New(&function.Spec{
13 Params: []function.Parameter{
17 AllowDynamicType: true,
20 Type: function.StaticReturnType(cty.String),
21 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
22 in := args[0].AsString()
23 out := strings.ToUpper(in)
24 return cty.StringVal(out), nil
28 var LowerFunc = function.New(&function.Spec{
29 Params: []function.Parameter{
33 AllowDynamicType: true,
36 Type: function.StaticReturnType(cty.String),
37 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
38 in := args[0].AsString()
39 out := strings.ToLower(in)
40 return cty.StringVal(out), nil
44 var ReverseFunc = function.New(&function.Spec{
45 Params: []function.Parameter{
49 AllowDynamicType: true,
52 Type: function.StaticReturnType(cty.String),
53 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
54 in := []byte(args[0].AsString())
55 out := make([]byte, len(in))
59 for i := 0; i < len(in); {
60 d, _, _ := textseg.ScanGraphemeClusters(inB[i:], true)
61 cluster := in[i : i+d]
63 copy(out[pos:], cluster)
67 return cty.StringVal(string(out)), nil
71 var StrlenFunc = function.New(&function.Spec{
72 Params: []function.Parameter{
76 AllowDynamicType: true,
79 Type: function.StaticReturnType(cty.Number),
80 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
81 in := args[0].AsString()
85 for i := 0; i < len(in); {
86 d, _, _ := textseg.ScanGraphemeClusters(inB[i:], true)
91 return cty.NumberIntVal(int64(l)), nil
95 var SubstrFunc = function.New(&function.Spec{
96 Params: []function.Parameter{
100 AllowDynamicType: true,
105 AllowDynamicType: true,
110 AllowDynamicType: true,
113 Type: function.StaticReturnType(cty.String),
114 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
115 in := []byte(args[0].AsString())
116 var offset, length int
119 err = gocty.FromCtyValue(args[1], &offset)
121 return cty.NilVal, err
123 err = gocty.FromCtyValue(args[2], &length)
125 return cty.NilVal, err
129 totalLenNum, err := Strlen(args[0])
131 // should never happen
132 panic("Stdlen returned an error")
136 err = gocty.FromCtyValue(totalLenNum, &totalLen)
138 // should never happen
139 panic("Stdlen returned a non-int number")
149 // First we'll seek forward to our offset
151 for i = 0; i < len(sub); {
152 d, _, _ := textseg.ScanGraphemeClusters(sub[i:], true)
159 return cty.StringVal(""), nil
167 // Taking the remainder of the string is a fast path since
168 // we can just return the rest of the buffer verbatim.
169 return cty.StringVal(string(sub)), nil
172 // Otherwise we need to start seeking forward again until we
173 // reach the length we want.
175 for i = 0; i < len(sub); {
176 d, _, _ := textseg.ScanGraphemeClusters(sub[i:], true)
186 return cty.StringVal(string(sub)), nil
190 // Upper is a Function that converts a given string to uppercase.
191 func Upper(str cty.Value) (cty.Value, error) {
192 return UpperFunc.Call([]cty.Value{str})
195 // Lower is a Function that converts a given string to lowercase.
196 func Lower(str cty.Value) (cty.Value, error) {
197 return LowerFunc.Call([]cty.Value{str})
200 // Reverse is a Function that reverses the order of the characters in the
203 // As usual, "character" for the sake of this function is a grapheme cluster,
204 // so combining diacritics (for example) will be considered together as a
206 func Reverse(str cty.Value) (cty.Value, error) {
207 return ReverseFunc.Call([]cty.Value{str})
210 // Strlen is a Function that returns the length of the given string in
213 // As usual, "character" for the sake of this function is a grapheme cluster,
214 // so combining diacritics (for example) will be considered together as a
216 func Strlen(str cty.Value) (cty.Value, error) {
217 return StrlenFunc.Call([]cty.Value{str})
220 // Substr is a Function that extracts a sequence of characters from another
221 // string and creates a new string.
223 // As usual, "character" for the sake of this function is a grapheme cluster,
224 // so combining diacritics (for example) will be considered together as a
227 // The "offset" index may be negative, in which case it is relative to the
228 // end of the given string.
230 // The "length" may be -1, in which case the remainder of the string after
231 // the given offset will be returned.
232 func Substr(str cty.Value, offset cty.Value, length cty.Value) (cty.Value, error) {
233 return SubstrFunc.Call([]cty.Value{str, offset, length})