From af47e5a2cfc5441005cccbb1684f3850a0f62bd1 Mon Sep 17 00:00:00 2001 From: Tatsushi Demachi Date: Wed, 22 Oct 2014 00:51:29 +0900 Subject: [PATCH] Extend template's mod and modBool functions to accept any int types Fixes #575 --- hugolib/template.go | 38 ++++++++++++++++++-- hugolib/template_test.go | 75 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/hugolib/template.go b/hugolib/template.go index 0d8c29bf4..20c57701d 100644 --- a/hugolib/template.go +++ b/hugolib/template.go @@ -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) }, diff --git a/hugolib/template_test.go b/hugolib/template_test.go index b4d95f0b4..a573f1125 100644 --- a/hugolib/template_test.go +++ b/hugolib/template_test.go @@ -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{}