Skip to content

Commit d3284bd

Browse files
authored
Merge pull request #31220 from FooBartn/string-starts-ends-with-funcs
feat: add startswith and endswith funcs
2 parents f30738d + 7190898 commit d3284bd

File tree

7 files changed

+208
-0
lines changed

7 files changed

+208
-0
lines changed

internal/lang/funcs/string.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,58 @@ import (
88
"github.com/zclconf/go-cty/cty/function"
99
)
1010

11+
// StartsWithFunc constructs a function that checks if a string starts with
12+
// a specific prefix using strings.HasPrefix
13+
var StartsWithFunc = function.New(&function.Spec{
14+
Params: []function.Parameter{
15+
{
16+
Name: "str",
17+
Type: cty.String,
18+
},
19+
{
20+
Name: "prefix",
21+
Type: cty.String,
22+
},
23+
},
24+
Type: function.StaticReturnType(cty.Bool),
25+
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
26+
str := args[0].AsString()
27+
prefix := args[1].AsString()
28+
29+
if strings.HasPrefix(str, prefix) {
30+
return cty.True, nil
31+
}
32+
33+
return cty.False, nil
34+
},
35+
})
36+
37+
// EndsWithFunc constructs a function that checks if a string ends with
38+
// a specific suffix using strings.HasSuffix
39+
var EndsWithFunc = function.New(&function.Spec{
40+
Params: []function.Parameter{
41+
{
42+
Name: "str",
43+
Type: cty.String,
44+
},
45+
{
46+
Name: "suffix",
47+
Type: cty.String,
48+
},
49+
},
50+
Type: function.StaticReturnType(cty.Bool),
51+
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
52+
str := args[0].AsString()
53+
suffix := args[1].AsString()
54+
55+
if strings.HasSuffix(str, suffix) {
56+
return cty.True, nil
57+
}
58+
59+
return cty.False, nil
60+
},
61+
})
62+
1163
// ReplaceFunc constructs a function that searches a given string for another
1264
// given substring, and replaces each occurence with a given replacement string.
1365
var ReplaceFunc = function.New(&function.Spec{

internal/lang/functions.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ func (s *Scope) Functions() map[string]function.Function {
5959
"dirname": funcs.DirnameFunc,
6060
"distinct": stdlib.DistinctFunc,
6161
"element": stdlib.ElementFunc,
62+
"endswith": funcs.EndsWithFunc,
6263
"chunklist": stdlib.ChunklistFunc,
6364
"file": funcs.MakeFileFunc(s.BaseDir, false),
6465
"fileexists": funcs.MakeFileExistsFunc(s.BaseDir),
@@ -115,6 +116,7 @@ func (s *Scope) Functions() map[string]function.Function {
115116
"slice": stdlib.SliceFunc,
116117
"sort": stdlib.SortFunc,
117118
"split": stdlib.SplitFunc,
119+
"startswith": funcs.StartsWithFunc,
118120
"strrev": stdlib.ReverseFunc,
119121
"substr": stdlib.SubstrFunc,
120122
"sum": funcs.SumFunc,

internal/lang/functions_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,47 @@ func TestFunctions(t *testing.T) {
314314
},
315315
},
316316

317+
"endswith": {
318+
{
319+
`endswith("hello world", "world")`,
320+
cty.True,
321+
},
322+
{
323+
`endswith("hello world", "hello")`,
324+
cty.False,
325+
},
326+
{
327+
`endswith("hello world", "")`,
328+
cty.True,
329+
// Completely empty suffix value ( "" )
330+
// will always evaluate to true for all strings.
331+
},
332+
{
333+
`endswith("hello world", " ")`,
334+
cty.False,
335+
},
336+
{
337+
`endswith("", "")`,
338+
cty.True,
339+
},
340+
{
341+
`endswith("", " ")`,
342+
cty.False,
343+
},
344+
{
345+
`endswith(" ", "")`,
346+
cty.True,
347+
},
348+
{
349+
`endswith("", "hello")`,
350+
cty.False,
351+
},
352+
{
353+
`endswith(" ", "hello")`,
354+
cty.False,
355+
},
356+
},
357+
317358
"file": {
318359
{
319360
`file("hello.txt")`,
@@ -816,6 +857,47 @@ func TestFunctions(t *testing.T) {
816857
},
817858
},
818859

860+
"startswith": {
861+
{
862+
`startswith("hello world", "hello")`,
863+
cty.True,
864+
},
865+
{
866+
`startswith("hello world", "world")`,
867+
cty.False,
868+
},
869+
{
870+
`startswith("hello world", "")`,
871+
cty.True,
872+
// Completely empty prefix value ( "" )
873+
// will always evaluate to true for all strings.
874+
},
875+
{
876+
`startswith("hello world", " ")`,
877+
cty.False,
878+
},
879+
{
880+
`startswith("", "")`,
881+
cty.True,
882+
},
883+
{
884+
`startswith("", " ")`,
885+
cty.False,
886+
},
887+
{
888+
`startswith(" ", "")`,
889+
cty.True,
890+
},
891+
{
892+
`startswith("", "hello")`,
893+
cty.False,
894+
},
895+
{
896+
`startswith(" ", "hello")`,
897+
cty.False,
898+
},
899+
},
900+
819901
"strrev": {
820902
{
821903
`strrev("hello world")`,

website/data/language-nav-data.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@
319319
"title": "<code>chomp</code>",
320320
"href": "/language/functions/chomp"
321321
},
322+
{
323+
"title": "<code>endswith</code>",
324+
"href": "/language/functions/endswith"
325+
},
322326
{
323327
"title": "<code>format</code>",
324328
"href": "/language/functions/format"
@@ -352,6 +356,10 @@
352356
"title": "<code>split</code>",
353357
"href": "/language/functions/split"
354358
},
359+
{
360+
"title": "<code>startswith</code>",
361+
"href": "/language/functions/startswith"
362+
},
355363
{
356364
"title": "<code>strrev</code>",
357365
"href": "/language/functions/strrev"
@@ -776,6 +784,7 @@
776784
{ "title": "dirname", "path": "functions/dirname", "hidden": true },
777785
{ "title": "distinct", "path": "functions/distinct", "hidden": true },
778786
{ "title": "element", "path": "functions/element", "hidden": true },
787+
{ "title": "endswith", "path": "functions/endswith", "hidden": true },
779788
{ "title": "file", "path": "functions/file", "hidden": true },
780789
{ "title": "filebase64", "path": "functions/filebase64", "hidden": true },
781790
{
@@ -851,6 +860,7 @@
851860
{ "title": "slice", "path": "functions/slice", "hidden": true },
852861
{ "title": "sort", "path": "functions/sort", "hidden": true },
853862
{ "title": "split", "path": "functions/split", "hidden": true },
863+
{ "title": "startswith", "path": "functions/startswith", "hidden": true },
854864
{ "title": "strrev", "path": "functions/strrev", "hidden": true },
855865
{ "title": "substr", "path": "functions/substr", "hidden": true },
856866
{ "title": "sum", "path": "functions/sum", "hidden": true },
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
page_title: endswith - Functions - Configuration Language
3+
description: |-
4+
The endswith function takes two values: a string to check and a suffix string. It returns true if the first string ends with that exact suffix.
5+
---
6+
7+
# `endswith` Function
8+
9+
`endswith` takes two values: a string to check and a suffix string. The function returns true if the first string ends with that exact suffix.
10+
11+
```hcl
12+
endswith(string, suffix)
13+
```
14+
15+
## Examples
16+
17+
```
18+
> endswith("hello world", "world")
19+
true
20+
21+
> endswith("hello world", "hello")
22+
false
23+
```
24+
25+
## Related Functions
26+
27+
- [`startswith`](/language/functions/startswith) takes two values: a string to check and a prefix string. The function returns true if the string begins with that exact prefix.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
page_title: startsswith - Functions - Configuration Language
3+
description: |-
4+
The startswith function takes two values: a string to check and a prefix string. It returns true if the string begins with that exact prefix.
5+
---
6+
7+
# `startswith` Function
8+
9+
`startswith` takes two values: a string to check and a prefix string. The function returns true if the string begins with that exact prefix.
10+
11+
```hcl
12+
startswith(string, prefix)
13+
```
14+
15+
## Examples
16+
17+
```
18+
> startswith("hello world", "hello")
19+
true
20+
21+
> startswith("hello world", "world")
22+
false
23+
```
24+
25+
## Related Functions
26+
27+
- [`endswith`](/language/functions/endswith) takes two values: a string to check and a suffix string. The function returns true if the first string ends with that exact suffix.

website/layouts/language.erb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,10 @@
370370
<a href="/docs/language/functions/chomp.html">chomp</a>
371371
</li>
372372

373+
<li>
374+
<a href="/docs/language/functions/endswith.html">endswith</a>
375+
</li>
376+
373377
<li>
374378
<a href="/docs/language/functions/format.html">format</a>
375379
</li>
@@ -406,6 +410,10 @@
406410
<a href="/docs/language/functions/split.html">split</a>
407411
</li>
408412

413+
<li>
414+
<a href="/docs/language/functions/startswith.html">startswith</a>
415+
</li>
416+
409417
<li>
410418
<a href="/docs/language/functions/strrev.html">strrev</a>
411419
</li>

0 commit comments

Comments
 (0)