Extend template's mod and modBool functions to accept any int types

Fixes #575
This commit is contained in:
Tatsushi Demachi 2014-10-22 00:51:29 +09:00 committed by spf13
parent d4ed59198a
commit af47e5a2cf
2 changed files with 111 additions and 2 deletions

View 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) },

View 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{}