Extend template's mod and modBool functions to accept any int types
authorTatsushi Demachi <tdemachi@gmail.com>
Tue, 21 Oct 2014 15:51:29 +0000 (00:51 +0900)
committerspf13 <steve.francia@gmail.com>
Sun, 2 Nov 2014 03:00:46 +0000 (23:00 -0400)
Fixes #575

hugolib/template.go
hugolib/template_test.go

index 0d8c29bf44a43fa35f97a2d678816cb753b7e7cc..20c57701d9d7d9434e0cc68145fde6d5e859bd6d 100644 (file)
@@ -446,6 +446,40 @@ func doArithmetic(a, b interface{}, op rune) (interface{}, error) {
        }
 }
 
+func Mod(a, b interface{}) (int64, error) {
+       av := reflect.ValueOf(a)
+       bv := reflect.ValueOf(b)
+       var ai, bi int64
+
+       switch av.Kind() {
+       case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+               ai = av.Int()
+       default:
+               return 0, errors.New("Modulo operator can't be used with non integer value")
+       }
+
+       switch bv.Kind() {
+       case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+               bi = bv.Int()
+       default:
+               return 0, errors.New("Modulo operator can't be used with non integer value")
+       }
+
+       if bi == 0 {
+               return 0, errors.New("The number can't be divided by zero at modulo operation")
+       }
+
+       return ai % bi, nil
+}
+
+func ModBool(a, b interface{}) (bool, error) {
+       res, err := Mod(a, b)
+       if err != nil {
+               return false, err
+       }
+       return res == int64(0), nil
+}
+
 type Template interface {
        ExecuteTemplate(wr io.Writer, name string, data interface{}) error
        Lookup(name string) *template.Template
@@ -496,9 +530,9 @@ func NewTemplate() Template {
                "add":         func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '+') },
                "sub":         func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '-') },
                "div":         func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '/') },
-               "mod":         func(a, b int) int { return a % b },
+               "mod":         Mod,
                "mul":         func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '*') },
-               "modBool":     func(a, b int) bool { return a%b == 0 },
+               "modBool":     ModBool,
                "lower":       func(a string) string { return strings.ToLower(a) },
                "upper":       func(a string) string { return strings.ToUpper(a) },
                "title":       func(a string) string { return strings.Title(a) },
index b4d95f0b416b96e4e980ba9383328dbcfbef0185..a573f11250d5f491aeed858ac97726459b435139 100644 (file)
@@ -123,6 +123,81 @@ func TestDoArithmetic(t *testing.T) {
        }
 }
 
+func TestMod(t *testing.T) {
+       for i, this := range []struct {
+               a      interface{}
+               b      interface{}
+               expect interface{}
+       }{
+               {3, 2, int64(1)},
+               {3, 1, int64(0)},
+               {3, 0, false},
+               {0, 3, int64(0)},
+               {3.1, 2, false},
+               {3, 2.1, false},
+               {3.1, 2.1, false},
+               {int8(3), int8(2), int64(1)},
+               {int16(3), int16(2), int64(1)},
+               {int32(3), int32(2), int64(1)},
+               {int64(3), int64(2), int64(1)},
+       } {
+               result, err := Mod(this.a, this.b)
+               if b, ok := this.expect.(bool); ok && !b {
+                       if err == nil {
+                               t.Errorf("[%d] modulo didn't return an expected error")
+                       }
+               } else {
+                       if err != nil {
+                               t.Errorf("[%d] failed: %s", i, err)
+                               continue
+                       }
+                       if !reflect.DeepEqual(result, this.expect) {
+                               t.Errorf("[%d] modulo got %v but expected %v", i, result, this.expect)
+                       }
+               }
+       }
+}
+
+func TestModBool(t *testing.T) {
+       for i, this := range []struct {
+               a      interface{}
+               b      interface{}
+               expect interface{}
+       }{
+               {3, 3, true},
+               {3, 2, false},
+               {3, 1, true},
+               {3, 0, nil},
+               {0, 3, true},
+               {3.1, 2, nil},
+               {3, 2.1, nil},
+               {3.1, 2.1, nil},
+               {int8(3), int8(3), true},
+               {int8(3), int8(2), false},
+               {int16(3), int16(3), true},
+               {int16(3), int16(2), false},
+               {int32(3), int32(3), true},
+               {int32(3), int32(2), false},
+               {int64(3), int64(3), true},
+               {int64(3), int64(2), false},
+       } {
+               result, err := ModBool(this.a, this.b)
+               if this.expect == nil {
+                       if err == nil {
+                               t.Errorf("[%d] modulo didn't return an expected error")
+                       }
+               } else {
+                       if err != nil {
+                               t.Errorf("[%d] failed: %s", i, err)
+                               continue
+                       }
+                       if !reflect.DeepEqual(result, this.expect) {
+                               t.Errorf("[%d] modulo got %v but expected %v", i, result, this.expect)
+                       }
+               }
+       }
+}
+
 func TestFirst(t *testing.T) {
        for i, this := range []struct {
                count    interface{}