summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Henderson <dhenderson@gmail.com>2019-03-18 20:42:40 -0400
committerGitHub <noreply@github.com>2019-03-18 20:42:40 -0400
commitd2a6b5201089b14a99c75fb5e4ff4370bc07e4b8 (patch)
tree4a9e4553047003ad09ede4b3047b816bad9289d0
parent142279a94ed69684109f01ddfac2b5a29ba7264b (diff)
parent9c45b8e6e9abff588f3ae439cfac1b55e28faa95 (diff)
Merge pull request #518 from hairyhenderson/add-runecount-func
Adding strings.RuneCount function
-rw-r--r--docs-src/content/functions/strings.yml23
-rw-r--r--docs/content/functions/strings.md37
-rw-r--r--funcs/strings.go10
-rw-r--r--funcs/strings_test.go28
4 files changed, 98 insertions, 0 deletions
diff --git a/docs-src/content/functions/strings.yml b/docs-src/content/functions/strings.yml
index 9dbc886f..b3062dc4 100644
--- a/docs-src/content/functions/strings.yml
+++ b/docs-src/content/functions/strings.yml
@@ -479,6 +479,29 @@ funcs:
http://example.com/a/very/long/url
which should not be
broken
+ - name: strings.RuneCount
+ description: |
+ Return the number of _runes_ (Unicode code-points) contained within the
+ input. This is similar to the built-in `len` function, but `len` counts
+ the length in _bytes_. The length of an input containing multi-byte
+ code-points should therefore be measured with `strings.RuneCount`.
+
+ Inputs will first be converted to strings, and multiple inputs are
+ concatenated.
+
+ This wraps Go's [`utf8.RuneCountInString`](https://golang.org/pkg/unicode/utf8/#RuneCountInString)
+ function.
+ pipeline: true
+ arguments:
+ - name: input
+ required: true
+ description: the input(s) to measure
+ examples:
+ - |
+ $ gomplate -i '{{ range (slice "\u03a9" "\u0030" "\u1430") }}{{ printf "%s is %d bytes and %d runes\n" . (len .) (strings.RuneCount .) }}{{ end }}'
+ Ω is 2 bytes and 1 runes
+ 0 is 1 bytes and 1 runes
+ ᐰ is 3 bytes and 1 runes
- name: contains
description: |
**See [`strings.Contains`](#strings-contains) for a pipeline-compatible version**
diff --git a/docs/content/functions/strings.md b/docs/content/functions/strings.md
index df59bfd6..26254b7a 100644
--- a/docs/content/functions/strings.md
+++ b/docs/content/functions/strings.md
@@ -804,6 +804,43 @@ which should not be
broken
```
+## `strings.RuneCount`
+
+Return the number of _runes_ (Unicode code-points) contained within the
+input. This is similar to the built-in `len` function, but `len` counts
+the length in _bytes_. The length of an input containing multi-byte
+code-points should therefore be measured with `strings.RuneCount`.
+
+Inputs will first be converted to strings, and multiple inputs are
+concatenated.
+
+This wraps Go's [`utf8.RuneCountInString`](https://golang.org/pkg/unicode/utf8/#RuneCountInString)
+function.
+
+### Usage
+
+```go
+strings.RuneCount input
+```
+```go
+input | strings.RuneCount
+```
+
+### Arguments
+
+| name | description |
+|------|-------------|
+| `input` | _(required)_ the input(s) to measure |
+
+### Examples
+
+```console
+$ gomplate -i '{{ range (slice "\u03a9" "\u0030" "\u1430") }}{{ printf "%s is %d bytes and %d runes\n" . (len .) (strings.RuneCount .) }}{{ end }}'
+Ω is 2 bytes and 1 runes
+0 is 1 bytes and 1 runes
+ᐰ is 3 bytes and 1 runes
+```
+
## `contains`
**See [`strings.Contains`](#strings-contains) for a pipeline-compatible version**
diff --git a/funcs/strings.go b/funcs/strings.go
index df070a26..d8fa8641 100644
--- a/funcs/strings.go
+++ b/funcs/strings.go
@@ -8,6 +8,7 @@ package funcs
import (
"fmt"
"sync"
+ "unicode/utf8"
"github.com/Masterminds/goutils"
"github.com/hairyhenderson/gomplate/conv"
@@ -262,3 +263,12 @@ func (f *StringFuncs) WordWrap(args ...interface{}) (string, error) {
}
return gompstrings.WordWrap(in, opts), nil
}
+
+// RuneCount - like len(s), but for runes
+func (f *StringFuncs) RuneCount(args ...interface{}) (int, error) {
+ s := ""
+ for _, arg := range args {
+ s += conv.ToString(arg)
+ }
+ return utf8.RuneCountInString(s), nil
+}
diff --git a/funcs/strings_test.go b/funcs/strings_test.go
index 04a97bcc..c79e5d24 100644
--- a/funcs/strings_test.go
+++ b/funcs/strings_test.go
@@ -144,3 +144,31 @@ func TestSquote(t *testing.T) {
assert.Equal(t, d.out, sf.Squote(d.in))
}
}
+
+func TestRuneCount(t *testing.T) {
+ sf := &StringFuncs{}
+
+ n, err := sf.RuneCount("")
+ assert.NoError(t, err)
+ assert.Equal(t, 0, n)
+
+ n, err = sf.RuneCount("foo")
+ assert.NoError(t, err)
+ assert.Equal(t, 3, n)
+
+ n, err = sf.RuneCount("foo", "bar")
+ assert.NoError(t, err)
+ assert.Equal(t, 6, n)
+
+ n, err = sf.RuneCount(42, true)
+ assert.NoError(t, err)
+ assert.Equal(t, 6, n)
+
+ n, err = sf.RuneCount("😂\U0001F602")
+ assert.NoError(t, err)
+ assert.Equal(t, 2, n)
+
+ n, err = sf.RuneCount("\U0001F600", 3.14)
+ assert.NoError(t, err)
+ assert.Equal(t, 5, n)
+}