Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add startswith and endswith funcs #31220

Merged
merged 5 commits into from Jul 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 52 additions & 0 deletions internal/lang/funcs/string.go
Expand Up @@ -8,6 +8,58 @@ import (
"github.com/zclconf/go-cty/cty/function"
)

// StartsWithFunc constructs a function that checks if a string starts with
// a specific prefix using strings.HasPrefix
var StartsWithFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "str",
Type: cty.String,
},
{
Name: "prefix",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.Bool),
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
str := args[0].AsString()
prefix := args[1].AsString()

if strings.HasPrefix(str, prefix) {
return cty.True, nil
}

return cty.False, nil
},
})

// EndsWithFunc constructs a function that checks if a string ends with
// a specific suffix using strings.HasSuffix
var EndsWithFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "str",
Type: cty.String,
},
{
Name: "suffix",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.Bool),
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
str := args[0].AsString()
suffix := args[1].AsString()

if strings.HasSuffix(str, suffix) {
return cty.True, nil
}

return cty.False, nil
},
})

// ReplaceFunc constructs a function that searches a given string for another
// given substring, and replaces each occurence with a given replacement string.
var ReplaceFunc = function.New(&function.Spec{
Expand Down
2 changes: 2 additions & 0 deletions internal/lang/functions.go
Expand Up @@ -59,6 +59,7 @@ func (s *Scope) Functions() map[string]function.Function {
"dirname": funcs.DirnameFunc,
"distinct": stdlib.DistinctFunc,
"element": stdlib.ElementFunc,
"endswith": funcs.EndsWithFunc,
"chunklist": stdlib.ChunklistFunc,
"file": funcs.MakeFileFunc(s.BaseDir, false),
"fileexists": funcs.MakeFileExistsFunc(s.BaseDir),
Expand Down Expand Up @@ -115,6 +116,7 @@ func (s *Scope) Functions() map[string]function.Function {
"slice": stdlib.SliceFunc,
"sort": stdlib.SortFunc,
"split": stdlib.SplitFunc,
"startswith": funcs.StartsWithFunc,
"strrev": stdlib.ReverseFunc,
"substr": stdlib.SubstrFunc,
"sum": funcs.SumFunc,
Expand Down
82 changes: 82 additions & 0 deletions internal/lang/functions_test.go
Expand Up @@ -314,6 +314,47 @@ func TestFunctions(t *testing.T) {
},
},

"endswith": {
{
`endswith("hello world", "world")`,
cty.True,
},
{
`endswith("hello world", "hello")`,
cty.False,
},
{
`endswith("hello world", "")`,
cty.True,
// Completely empty suffix value ( "" )
// will always evaluate to true for all strings.
},
{
`endswith("hello world", " ")`,
cty.False,
},
{
`endswith("", "")`,
cty.True,
},
{
`endswith("", " ")`,
cty.False,
},
{
`endswith(" ", "")`,
cty.True,
},
{
`endswith("", "hello")`,
cty.False,
},
{
`endswith(" ", "hello")`,
cty.False,
},
},

"file": {
{
`file("hello.txt")`,
Expand Down Expand Up @@ -816,6 +857,47 @@ func TestFunctions(t *testing.T) {
},
},

"startswith": {
{
`startswith("hello world", "hello")`,
cty.True,
},
{
`startswith("hello world", "world")`,
cty.False,
},
alisdair marked this conversation as resolved.
Show resolved Hide resolved
{
`startswith("hello world", "")`,
cty.True,
// Completely empty prefix value ( "" )
// will always evaluate to true for all strings.
},
{
`startswith("hello world", " ")`,
cty.False,
},
{
`startswith("", "")`,
cty.True,
},
{
`startswith("", " ")`,
cty.False,
},
{
`startswith(" ", "")`,
cty.True,
},
{
`startswith("", "hello")`,
cty.False,
},
{
`startswith(" ", "hello")`,
cty.False,
},
},

"strrev": {
{
`strrev("hello world")`,
Expand Down
10 changes: 10 additions & 0 deletions website/data/language-nav-data.json
Expand Up @@ -319,6 +319,10 @@
"title": "<code>chomp</code>",
"href": "/language/functions/chomp"
},
{
"title": "<code>endswith</code>",
"href": "/language/functions/endswith"
},
{
"title": "<code>format</code>",
"href": "/language/functions/format"
Expand Down Expand Up @@ -352,6 +356,10 @@
"title": "<code>split</code>",
"href": "/language/functions/split"
},
{
"title": "<code>startswith</code>",
"href": "/language/functions/startswith"
},
{
"title": "<code>strrev</code>",
"href": "/language/functions/strrev"
Expand Down Expand Up @@ -776,6 +784,7 @@
{ "title": "dirname", "path": "functions/dirname", "hidden": true },
{ "title": "distinct", "path": "functions/distinct", "hidden": true },
{ "title": "element", "path": "functions/element", "hidden": true },
{ "title": "endswith", "path": "functions/endswith", "hidden": true },
{ "title": "file", "path": "functions/file", "hidden": true },
{ "title": "filebase64", "path": "functions/filebase64", "hidden": true },
{
Expand Down Expand Up @@ -851,6 +860,7 @@
{ "title": "slice", "path": "functions/slice", "hidden": true },
{ "title": "sort", "path": "functions/sort", "hidden": true },
{ "title": "split", "path": "functions/split", "hidden": true },
{ "title": "startswith", "path": "functions/startswith", "hidden": true },
{ "title": "strrev", "path": "functions/strrev", "hidden": true },
{ "title": "substr", "path": "functions/substr", "hidden": true },
{ "title": "sum", "path": "functions/sum", "hidden": true },
Expand Down
27 changes: 27 additions & 0 deletions website/docs/language/functions/endswith.mdx
@@ -0,0 +1,27 @@
---
page_title: endswith - Functions - Configuration Language
description: |-
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.
---

# `endswith` Function

`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.

```hcl
endswith(string, suffix)
```

## Examples

```
> endswith("hello world", "world")
true

> endswith("hello world", "hello")
false
```

## Related Functions

- [`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.
27 changes: 27 additions & 0 deletions website/docs/language/functions/startswith.mdx
@@ -0,0 +1,27 @@
---
page_title: startsswith - Functions - Configuration Language
description: |-
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.
---

# `startswith` Function

`startswith` takes two values: a string to check and a prefix string. The function returns true if the string begins with that exact prefix.

```hcl
startswith(string, prefix)
```

## Examples

```
> startswith("hello world", "hello")
true

> startswith("hello world", "world")
false
```

## Related Functions

- [`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.
8 changes: 8 additions & 0 deletions website/layouts/language.erb
Expand Up @@ -370,6 +370,10 @@
<a href="/docs/language/functions/chomp.html">chomp</a>
</li>

<li>
<a href="/docs/language/functions/endswith.html">endswith</a>
</li>

<li>
<a href="/docs/language/functions/format.html">format</a>
</li>
Expand Down Expand Up @@ -406,6 +410,10 @@
<a href="/docs/language/functions/split.html">split</a>
</li>

<li>
<a href="/docs/language/functions/startswith.html">startswith</a>
</li>

<li>
<a href="/docs/language/functions/strrev.html">strrev</a>
</li>
Expand Down