shithub: hugo

Download patch

ref: 0be2aade99a4e7d16a48cb4372d383a3d1748d10
parent: 358dcce7a6b4a8100964597f81cf371451f6c4d3
author: bep <[email protected]>
date: Tue Feb 24 05:56:16 EST 2015

Add Seq template func

Very similar to GNU's seq.

Fixes #552

Conflicts:
	tpl/template.go

--- a/helpers/general.go
+++ b/helpers/general.go
@@ -19,6 +19,7 @@
 	"encoding/hex"
 	"errors"
 	"fmt"
+	"github.com/spf13/cast"
 	bp "github.com/spf13/hugo/bufferpool"
 	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
@@ -157,6 +158,74 @@
 	h := md5.New()
 	h.Write([]byte(f))
 	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
--- a/helpers/general_test.go
+++ b/helpers/general_test.go
@@ -133,6 +133,49 @@
 	}
 }
 
+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{}
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -1340,6 +1340,7 @@
 		"getJson":     GetJSON,
 		"getCSV":      GetCSV,
 		"getCsv":      GetCSV,
+		"seq":         helpers.Seq,
 	}
 
 }