aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--helpers/general.go69
-rw-r--r--helpers/general_test.go43
-rw-r--r--tpl/template.go1
3 files changed, 113 insertions, 0 deletions
diff --git a/helpers/general.go b/helpers/general.go
index a3bfc83de..a218cbd5d 100644
--- a/helpers/general.go
+++ b/helpers/general.go
@@ -19,6 +19,7 @@ import (
"encoding/hex"
"errors"
"fmt"
+ "github.com/spf13/cast"
bp "github.com/spf13/hugo/bufferpool"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
@@ -159,6 +160,74 @@ func Md5String(f string) string {
return hex.EncodeToString(h.Sum([]byte{}))
}
+// Seq creates a sequence of integers.
+// It's named and used as GNU's seq.
+// Examples:
+// 3 => 1, 2, 3
+// 1 2 4 => 1, 3
+// -3 => -1, -2, -3
+// 1 4 => 1, 2, 3, 4
+// 1 -2 => 1, 0, -1, -2
+func Seq(args ...interface{}) ([]int, error) {
+ if len(args) < 1 || len(args) > 3 {
+ return nil, errors.New("Seq, invalid number of args: 'first' 'increment' (optional) 'last' (optional)")
+ }
+
+ intArgs := cast.ToIntSlice(args)
+
+ var inc int = 1
+ var last int
+ var first = intArgs[0]
+
+ if len(intArgs) == 1 {
+ last = first
+ if last == 0 {
+ return []int{}, nil
+ } else if last > 0 {
+ first = 1
+ } else {
+ first = -1
+ inc = -1
+ }
+ } else if len(intArgs) == 2 {
+ last = intArgs[1]
+ if last < first {
+ inc = -1
+ }
+ } else {
+ inc = intArgs[1]
+ last = intArgs[2]
+ if inc == 0 {
+ return nil, errors.New("'increment' must not be 0")
+ }
+ if first < last && inc < 0 {
+ return nil, errors.New("'increment' must be > 0")
+ }
+ if first > last && inc > 0 {
+ return nil, errors.New("'increment' must be < 0")
+ }
+ }
+
+ size := int(((last - first) / inc) + 1)
+
+ // sanity check
+ if size > 2000 {
+ return nil, errors.New("size of result exeeds limit")
+ }
+
+ seq := make([]int, size)
+ val := first
+ for i := 0; ; i++ {
+ seq[i] = val
+ val += inc
+ if (inc < 0 && val < last) || (inc > 0 && val > last) {
+ break
+ }
+ }
+
+ return seq, nil
+}
+
// DoArithmetic performs arithmetic operations (+,-,*,/) using reflection to
// determine the type of the two terms.
func DoArithmetic(a, b interface{}, op rune) (interface{}, error) {
diff --git a/helpers/general_test.go b/helpers/general_test.go
index e185fe08c..9d28d214c 100644
--- a/helpers/general_test.go
+++ b/helpers/general_test.go
@@ -133,6 +133,49 @@ func TestMd5StringEmpty(t *testing.T) {
}
}
+func TestSeq(t *testing.T) {
+ for i, this := range []struct {
+ in []interface{}
+ expect interface{}
+ }{
+ {[]interface{}{-2, 5}, []int{-2, -1, 0, 1, 2, 3, 4, 5}},
+ {[]interface{}{1, 2, 4}, []int{1, 3}},
+ {[]interface{}{1}, []int{1}},
+ {[]interface{}{3}, []int{1, 2, 3}},
+ {[]interface{}{3.2}, []int{1, 2, 3}},
+ {[]interface{}{0}, []int{}},
+ {[]interface{}{-1}, []int{-1}},
+ {[]interface{}{-3}, []int{-1, -2, -3}},
+ {[]interface{}{3, -2}, []int{3, 2, 1, 0, -1, -2}},
+ {[]interface{}{6, -2, 2}, []int{6, 4, 2}},
+ {[]interface{}{1, 0, 2}, false},
+ {[]interface{}{1, -1, 2}, false},
+ {[]interface{}{2, 1, 1}, false},
+ {[]interface{}{2, 1, 1, 1}, false},
+ {[]interface{}{2001}, false},
+ {[]interface{}{}, false},
+ {[]interface{}{t}, []int{}},
+ {nil, false},
+ } {
+
+ result, err := Seq(this.in...)
+
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] TestSeq didn't return an expected error %s", i)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("[%d] failed: %s", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(result, this.expect) {
+ t.Errorf("[%d] TestSeq got %v but expected %v", i, result, this.expect)
+ }
+ }
+ }
+}
+
func TestDoArithmetic(t *testing.T) {
for i, this := range []struct {
a interface{}
diff --git a/tpl/template.go b/tpl/template.go
index 39f93a8f0..9322dc009 100644
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -1340,6 +1340,7 @@ func init() {
"getJson": GetJSON,
"getCSV": GetCSV,
"getCsv": GetCSV,
+ "seq": helpers.Seq,
}
}