pkg/strings: add SliceRunes
There was some discussions on slack about this and turns out there is no real solution to slice strings easily in cue.
Added a builtin which works internally on runes to work properly on strings with unicode chars.
Not sure about the name of the builtin.
Closes #424
https://github.com/cuelang/cue/pull/424
GitOrigin-RevId: 55640e7cfdf8878e5a90b386fc43b9650656d1bc
Change-Id: I40293d2e1646d1eee2e9074af9954a4be339de78
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6400
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/builtin_test.go b/cue/builtin_test.go
index a165a4c..1901d34 100644
--- a/cue/builtin_test.go
+++ b/cue/builtin_test.go
@@ -395,6 +395,9 @@
test("strings", `strings.ByteSlice("Hello", 2, 5)`),
`'llo'`,
}, {
+ test("strings", `strings.SliceRunes("✓ Hello", 0, 3)`),
+ `"✓ H"`,
+ }, {
test("strings", `strings.Runes("Café")`),
strings.Replace(fmt.Sprint([]rune{'C', 'a', 'f', 'é'}), " ", ",", -1),
}, {
diff --git a/cue/builtins.go b/cue/builtins.go
index 73b80eb..747c846 100644
--- a/cue/builtins.go
+++ b/cue/builtins.go
@@ -2978,6 +2978,22 @@
}
},
}, {
+ Name: "SliceRunes",
+ Params: []kind{stringKind, intKind, intKind},
+ Result: stringKind,
+ Func: func(c *callCtxt) {
+ s, start, end := c.string(0), c.int(1), c.int(2)
+ if c.do() {
+ c.ret, c.err = func() (interface{}, error) {
+ runes := []rune(s)
+ if start < 0 || start > end || end > len(runes) {
+ return "", fmt.Errorf("index out of range")
+ }
+ return string(runes[start:end]), nil
+ }()
+ }
+ },
+ }, {
Name: "Compare",
Params: []kind{stringKind, stringKind},
Result: intKind,
diff --git a/pkg/strings/manual.go b/pkg/strings/manual.go
index 508466d..9f53870 100644
--- a/pkg/strings/manual.go
+++ b/pkg/strings/manual.go
@@ -108,3 +108,13 @@
},
s)
}
+
+// SliceRunes returns a string of the underlying string data from the start index
+// up to but not including the end index.
+func SliceRunes(s string, start, end int) (string, error) {
+ runes := []rune(s)
+ if start < 0 || start > end || end > len(runes) {
+ return "", fmt.Errorf("index out of range")
+ }
+ return string(runes[start:end]), nil
+}