From 2b8d907ab731627f4e2a30442cd729064516c8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Fri, 6 Jul 2018 14:12:10 +0200 Subject: [PATCH] Add a newScratch template func Fixes #4685 --- {hugolib => common/maps}/scratch.go | 8 +- {hugolib => common/maps}/scratch_test.go | 22 ++-- common/math/math.go | 135 +++++++++++++++++++++++ common/math/math_test.go | 109 ++++++++++++++++++ docs/content/en/functions/scratch.md | 3 + hugolib/page.go | 6 +- hugolib/page_test.go | 27 +++++ hugolib/shortcode.go | 7 +- hugolib/site.go | 3 +- tpl/collections/collections.go | 7 ++ tpl/collections/init.go | 8 ++ tpl/math/math.go | 127 +-------------------- tpl/math/math_test.go | 87 --------------- tpl/tplimpl/template_funcs_test.go | 1 - 14 files changed, 319 insertions(+), 231 deletions(-) rename {hugolib => common/maps}/scratch.go (96%) rename {hugolib => common/maps}/scratch_test.go (92%) create mode 100644 common/math/math.go create mode 100644 common/math/math_test.go diff --git a/hugolib/scratch.go b/common/maps/scratch.go similarity index 96% rename from hugolib/scratch.go rename to common/maps/scratch.go index 37ed5df35..d00971435 100644 --- a/hugolib/scratch.go +++ b/common/maps/scratch.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Hugo Authors. All rights reserved. +// Copyright 2018 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,14 +11,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -package hugolib +package maps import ( "reflect" "sort" "sync" - "github.com/gohugoio/hugo/tpl/math" + "github.com/gohugoio/hugo/common/math" ) // Scratch is a writable context used for stateful operations in Page/Node rendering. @@ -130,6 +130,6 @@ func (c *Scratch) GetSortedMapValues(key string) interface{} { return sortedArray } -func newScratch() *Scratch { +func NewScratch() *Scratch { return &Scratch{values: make(map[string]interface{})} } diff --git a/hugolib/scratch_test.go b/common/maps/scratch_test.go similarity index 92% rename from hugolib/scratch_test.go rename to common/maps/scratch_test.go index 5ec2b89c8..8397ba830 100644 --- a/hugolib/scratch_test.go +++ b/common/maps/scratch_test.go @@ -1,4 +1,4 @@ -// Copyright 2015 The Hugo Authors. All rights reserved. +// Copyright 2018 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package hugolib +package maps import ( "reflect" @@ -23,7 +23,7 @@ import ( func TestScratchAdd(t *testing.T) { t.Parallel() - scratch := newScratch() + scratch := NewScratch() scratch.Add("int1", 10) scratch.Add("int1", 20) scratch.Add("int2", 20) @@ -53,7 +53,7 @@ func TestScratchAdd(t *testing.T) { func TestScratchAddSlice(t *testing.T) { t.Parallel() - scratch := newScratch() + scratch := NewScratch() _, err := scratch.Add("intSlice", []int{1, 2}) assert.Nil(t, err) @@ -82,14 +82,14 @@ func TestScratchAddSlice(t *testing.T) { func TestScratchSet(t *testing.T) { t.Parallel() - scratch := newScratch() + scratch := NewScratch() scratch.Set("key", "val") assert.Equal(t, "val", scratch.Get("key")) } func TestScratchDelete(t *testing.T) { t.Parallel() - scratch := newScratch() + scratch := NewScratch() scratch.Set("key", "val") scratch.Delete("key") scratch.Add("key", "Lucy Parsons") @@ -99,7 +99,7 @@ func TestScratchDelete(t *testing.T) { // Issue #2005 func TestScratchInParallel(t *testing.T) { var wg sync.WaitGroup - scratch := newScratch() + scratch := NewScratch() key := "counter" scratch.Set(key, int64(1)) for i := 1; i <= 10; i++ { @@ -133,7 +133,7 @@ func TestScratchInParallel(t *testing.T) { func TestScratchGet(t *testing.T) { t.Parallel() - scratch := newScratch() + scratch := NewScratch() nothing := scratch.Get("nothing") if nothing != nil { t.Errorf("Should not return anything, but got %v", nothing) @@ -142,7 +142,7 @@ func TestScratchGet(t *testing.T) { func TestScratchSetInMap(t *testing.T) { t.Parallel() - scratch := newScratch() + scratch := NewScratch() scratch.SetInMap("key", "lux", "Lux") scratch.SetInMap("key", "abc", "Abc") scratch.SetInMap("key", "zyx", "Zyx") @@ -153,7 +153,7 @@ func TestScratchSetInMap(t *testing.T) { func TestScratchGetSortedMapValues(t *testing.T) { t.Parallel() - scratch := newScratch() + scratch := NewScratch() nothing := scratch.GetSortedMapValues("nothing") if nothing != nil { t.Errorf("Should not return anything, but got %v", nothing) @@ -161,7 +161,7 @@ func TestScratchGetSortedMapValues(t *testing.T) { } func BenchmarkScratchGet(b *testing.B) { - scratch := newScratch() + scratch := NewScratch() scratch.Add("A", 1) b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/common/math/math.go b/common/math/math.go new file mode 100644 index 000000000..3c5ef1f9d --- /dev/null +++ b/common/math/math.go @@ -0,0 +1,135 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package math + +import ( + "errors" + "reflect" +) + +// DoArithmetic performs arithmetic operations (+,-,*,/) using reflection to +// determine the type of the two terms. +func DoArithmetic(a, b interface{}, op rune) (interface{}, error) { + av := reflect.ValueOf(a) + bv := reflect.ValueOf(b) + var ai, bi int64 + var af, bf float64 + var au, bu uint64 + switch av.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + ai = av.Int() + switch bv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + bi = bv.Int() + case reflect.Float32, reflect.Float64: + af = float64(ai) // may overflow + ai = 0 + bf = bv.Float() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + bu = bv.Uint() + if ai >= 0 { + au = uint64(ai) + ai = 0 + } else { + bi = int64(bu) // may overflow + bu = 0 + } + default: + return nil, errors.New("Can't apply the operator to the values") + } + case reflect.Float32, reflect.Float64: + af = av.Float() + switch bv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + bf = float64(bv.Int()) // may overflow + case reflect.Float32, reflect.Float64: + bf = bv.Float() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + bf = float64(bv.Uint()) // may overflow + default: + return nil, errors.New("Can't apply the operator to the values") + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + au = av.Uint() + switch bv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + bi = bv.Int() + if bi >= 0 { + bu = uint64(bi) + bi = 0 + } else { + ai = int64(au) // may overflow + au = 0 + } + case reflect.Float32, reflect.Float64: + af = float64(au) // may overflow + au = 0 + bf = bv.Float() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + bu = bv.Uint() + default: + return nil, errors.New("Can't apply the operator to the values") + } + case reflect.String: + as := av.String() + if bv.Kind() == reflect.String && op == '+' { + bs := bv.String() + return as + bs, nil + } + return nil, errors.New("Can't apply the operator to the values") + default: + return nil, errors.New("Can't apply the operator to the values") + } + + switch op { + case '+': + if ai != 0 || bi != 0 { + return ai + bi, nil + } else if af != 0 || bf != 0 { + return af + bf, nil + } else if au != 0 || bu != 0 { + return au + bu, nil + } + return 0, nil + case '-': + if ai != 0 || bi != 0 { + return ai - bi, nil + } else if af != 0 || bf != 0 { + return af - bf, nil + } else if au != 0 || bu != 0 { + return au - bu, nil + } + return 0, nil + case '*': + if ai != 0 || bi != 0 { + return ai * bi, nil + } else if af != 0 || bf != 0 { + return af * bf, nil + } else if au != 0 || bu != 0 { + return au * bu, nil + } + return 0, nil + case '/': + if bi != 0 { + return ai / bi, nil + } else if bf != 0 { + return af / bf, nil + } else if bu != 0 { + return au / bu, nil + } + return nil, errors.New("Can't divide the value by 0") + default: + return nil, errors.New("There is no such an operation") + } +} diff --git a/common/math/math_test.go b/common/math/math_test.go new file mode 100644 index 000000000..613ac3073 --- /dev/null +++ b/common/math/math_test.go @@ -0,0 +1,109 @@ +// Copyright 2018 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package math + +import ( + "fmt" + "testing" + + "github.com/alecthomas/assert" + "github.com/stretchr/testify/require" +) + +func TestDoArithmetic(t *testing.T) { + t.Parallel() + + for i, test := range []struct { + a interface{} + b interface{} + op rune + expect interface{} + }{ + {3, 2, '+', int64(5)}, + {3, 2, '-', int64(1)}, + {3, 2, '*', int64(6)}, + {3, 2, '/', int64(1)}, + {3.0, 2, '+', float64(5)}, + {3.0, 2, '-', float64(1)}, + {3.0, 2, '*', float64(6)}, + {3.0, 2, '/', float64(1.5)}, + {3, 2.0, '+', float64(5)}, + {3, 2.0, '-', float64(1)}, + {3, 2.0, '*', float64(6)}, + {3, 2.0, '/', float64(1.5)}, + {3.0, 2.0, '+', float64(5)}, + {3.0, 2.0, '-', float64(1)}, + {3.0, 2.0, '*', float64(6)}, + {3.0, 2.0, '/', float64(1.5)}, + {uint(3), uint(2), '+', uint64(5)}, + {uint(3), uint(2), '-', uint64(1)}, + {uint(3), uint(2), '*', uint64(6)}, + {uint(3), uint(2), '/', uint64(1)}, + {uint(3), 2, '+', uint64(5)}, + {uint(3), 2, '-', uint64(1)}, + {uint(3), 2, '*', uint64(6)}, + {uint(3), 2, '/', uint64(1)}, + {3, uint(2), '+', uint64(5)}, + {3, uint(2), '-', uint64(1)}, + {3, uint(2), '*', uint64(6)}, + {3, uint(2), '/', uint64(1)}, + {uint(3), -2, '+', int64(1)}, + {uint(3), -2, '-', int64(5)}, + {uint(3), -2, '*', int64(-6)}, + {uint(3), -2, '/', int64(-1)}, + {-3, uint(2), '+', int64(-1)}, + {-3, uint(2), '-', int64(-5)}, + {-3, uint(2), '*', int64(-6)}, + {-3, uint(2), '/', int64(-1)}, + {uint(3), 2.0, '+', float64(5)}, + {uint(3), 2.0, '-', float64(1)}, + {uint(3), 2.0, '*', float64(6)}, + {uint(3), 2.0, '/', float64(1.5)}, + {3.0, uint(2), '+', float64(5)}, + {3.0, uint(2), '-', float64(1)}, + {3.0, uint(2), '*', float64(6)}, + {3.0, uint(2), '/', float64(1.5)}, + {0, 0, '+', 0}, + {0, 0, '-', 0}, + {0, 0, '*', 0}, + {"foo", "bar", '+', "foobar"}, + {3, 0, '/', false}, + {3.0, 0, '/', false}, + {3, 0.0, '/', false}, + {uint(3), uint(0), '/', false}, + {3, uint(0), '/', false}, + {-3, uint(0), '/', false}, + {uint(3), 0, '/', false}, + {3.0, uint(0), '/', false}, + {uint(3), 0.0, '/', false}, + {3, "foo", '+', false}, + {3.0, "foo", '+', false}, + {uint(3), "foo", '+', false}, + {"foo", 3, '+', false}, + {"foo", "bar", '-', false}, + {3, 2, '%', false}, + } { + errMsg := fmt.Sprintf("[%d] %v", i, test) + + result, err := DoArithmetic(test.a, test.b, test.op) + + if b, ok := test.expect.(bool); ok && !b { + require.Error(t, err, errMsg) + continue + } + + require.NoError(t, err, errMsg) + assert.Equal(t, test.expect, result, errMsg) + } +} diff --git a/docs/content/en/functions/scratch.md b/docs/content/en/functions/scratch.md index 9df78fe43..93a1e426a 100644 --- a/docs/content/en/functions/scratch.md +++ b/docs/content/en/functions/scratch.md @@ -22,6 +22,9 @@ aliases: [/extras/scratch/,/doc/scratch/] In most cases you can do okay without `Scratch`, but due to scoping issues, there are many use cases that aren't solvable in Go Templates without `Scratch`'s help. +`.Scratch` is available as methods on `Page` and `Shortcode`. Since Hugo 0.43 you can also create a locally scoped `Scratch` using the template func `newScratch`. + + {{% note %}} See [this Go issue](https://github.com/golang/go/issues/10608) for the main motivation behind Scratch. {{% /note %}} diff --git a/hugolib/page.go b/hugolib/page.go index d9a3fe31c..6e21d75ae 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -257,7 +257,7 @@ type Page struct { layoutDescriptor output.LayoutDescriptor - scratch *Scratch + scratch *maps.Scratch // It would be tempting to use the language set on the Site, but in they way we do // multi-site processing, these values may differ during the initial page processing. @@ -2052,9 +2052,9 @@ func (p *Page) String() string { } // Scratch returns the writable context associated with this Page. -func (p *Page) Scratch() *Scratch { +func (p *Page) Scratch() *maps.Scratch { if p.scratch == nil { - p.scratch = newScratch() + p.scratch = maps.NewScratch() } return p.scratch } diff --git a/hugolib/page_test.go b/hugolib/page_test.go index 985373a90..b512a9a5a 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -1830,6 +1830,33 @@ Summary: In Chinese, 好 means good. } +func TestScratchSite(t *testing.T) { + t.Parallel() + + b := newTestSitesBuilder(t) + b.WithSimpleConfigFile().WithTemplatesAdded("index.html", ` +{{ .Scratch.Set "b" "bv" }} +B: {{ .Scratch.Get "b" }} +`, + "shortcodes/scratch.html", ` +{{ .Scratch.Set "c" "cv" }} +C: {{ .Scratch.Get "c" }} +`, + ) + + b.WithContentAdded("scratchme.md", ` +--- +title: Scratch Me! +--- + +{{< scratch >}} +`) + b.Build(BuildCfg{}) + + b.AssertFileContent("public/index.html", "B: bv") + b.AssertFileContent("public/scratchme/index.html", "C: cv") +} + func BenchmarkParsePage(b *testing.B) { s := newTestSite(b) f, _ := os.Open("testdata/redis.cn.md") diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go index c07a5586a..cf5b0ece0 100644 --- a/hugolib/shortcode.go +++ b/hugolib/shortcode.go @@ -24,6 +24,7 @@ import ( "strings" "sync" + "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/output" "github.com/gohugoio/hugo/media" @@ -45,7 +46,7 @@ type ShortcodeWithPage struct { // this ordinal will represent the position of this shortcode in the page content. Ordinal int - scratch *Scratch + scratch *maps.Scratch } // Site returns information about the current site. @@ -65,9 +66,9 @@ func (scp *ShortcodeWithPage) RelRef(ref string) (string, error) { // Scratch returns a scratch-pad scoped for this shortcode. This can be used // as a temporary storage for variables, counters etc. -func (scp *ShortcodeWithPage) Scratch() *Scratch { +func (scp *ShortcodeWithPage) Scratch() *maps.Scratch { if scp.scratch == nil { - scp.scratch = newScratch() + scp.scratch = maps.NewScratch() } return scp.scratch } diff --git a/hugolib/site.go b/hugolib/site.go index df7e66d4a..a749bafd0 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -27,6 +27,7 @@ import ( "strings" "time" + "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/resource" "github.com/gohugoio/hugo/langs" @@ -1509,7 +1510,7 @@ func (s *Site) resetBuildState() { for _, p := range s.rawAllPages { p.subSections = Pages{} p.parent = nil - p.scratch = newScratch() + p.scratch = maps.NewScratch() p.mainPageOutput = nil } } diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go index dd418d7d2..51bc5c796 100644 --- a/tpl/collections/collections.go +++ b/tpl/collections/collections.go @@ -23,6 +23,7 @@ import ( "strings" "time" + "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/helpers" @@ -650,3 +651,9 @@ func (ns *Namespace) Uniq(l interface{}) (interface{}, error) { func (ns *Namespace) KeyVals(key interface{}, vals ...interface{}) (types.KeyValues, error) { return types.KeyValues{Key: key, Values: vals}, nil } + +// NewScratch creates a new Scratch which can be used to store values in a +// thread safe way. +func (ns *Namespace) NewScratch() *maps.Scratch { + return maps.NewScratch() +} diff --git a/tpl/collections/init.go b/tpl/collections/init.go index 91b0dea01..b986b3b42 100644 --- a/tpl/collections/init.go +++ b/tpl/collections/init.go @@ -144,6 +144,14 @@ func init() { {`{{ seq 3 }}`, `[1 2 3]`}, }, ) + + ns.AddMethodMapping(ctx.NewScratch, + []string{"newScratch"}, + [][2]string{ + {`{{ $scratch := newScratch }}{{ $scratch.Add "b" 2 }}{{ $scratch.Add "b" 2 }}{{ $scratch.Get "b" }}`, `4`}, + }, + ) + ns.AddMethodMapping(ctx.Uniq, []string{"uniq"}, [][2]string{ diff --git a/tpl/math/math.go b/tpl/math/math.go index 534f7f284..fcc6abc3d 100644 --- a/tpl/math/math.go +++ b/tpl/math/math.go @@ -16,7 +16,8 @@ package math import ( "errors" "math" - "reflect" + + _math "github.com/gohugoio/hugo/common/math" "github.com/spf13/cast" ) @@ -31,7 +32,7 @@ type Namespace struct{} // Add adds two numbers. func (ns *Namespace) Add(a, b interface{}) (interface{}, error) { - return DoArithmetic(a, b, '+') + return _math.DoArithmetic(a, b, '+') } // Ceil returns the least integer value greater than or equal to x. @@ -46,7 +47,7 @@ func (ns *Namespace) Ceil(x interface{}) (float64, error) { // Div divides two numbers. func (ns *Namespace) Div(a, b interface{}) (interface{}, error) { - return DoArithmetic(a, b, '/') + return _math.DoArithmetic(a, b, '/') } // Floor returns the greatest integer value less than or equal to x. @@ -98,7 +99,7 @@ func (ns *Namespace) ModBool(a, b interface{}) (bool, error) { // Mul multiplies two numbers. func (ns *Namespace) Mul(a, b interface{}) (interface{}, error) { - return DoArithmetic(a, b, '*') + return _math.DoArithmetic(a, b, '*') } // Round returns the nearest integer, rounding half away from zero. @@ -113,121 +114,5 @@ func (ns *Namespace) Round(x interface{}) (float64, error) { // Sub subtracts two numbers. func (ns *Namespace) Sub(a, b interface{}) (interface{}, error) { - return DoArithmetic(a, b, '-') -} - -// DoArithmetic performs arithmetic operations (+,-,*,/) using reflection to -// determine the type of the two terms. -func DoArithmetic(a, b interface{}, op rune) (interface{}, error) { - av := reflect.ValueOf(a) - bv := reflect.ValueOf(b) - var ai, bi int64 - var af, bf float64 - var au, bu uint64 - switch av.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - ai = av.Int() - switch bv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - bi = bv.Int() - case reflect.Float32, reflect.Float64: - af = float64(ai) // may overflow - ai = 0 - bf = bv.Float() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - bu = bv.Uint() - if ai >= 0 { - au = uint64(ai) - ai = 0 - } else { - bi = int64(bu) // may overflow - bu = 0 - } - default: - return nil, errors.New("Can't apply the operator to the values") - } - case reflect.Float32, reflect.Float64: - af = av.Float() - switch bv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - bf = float64(bv.Int()) // may overflow - case reflect.Float32, reflect.Float64: - bf = bv.Float() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - bf = float64(bv.Uint()) // may overflow - default: - return nil, errors.New("Can't apply the operator to the values") - } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - au = av.Uint() - switch bv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - bi = bv.Int() - if bi >= 0 { - bu = uint64(bi) - bi = 0 - } else { - ai = int64(au) // may overflow - au = 0 - } - case reflect.Float32, reflect.Float64: - af = float64(au) // may overflow - au = 0 - bf = bv.Float() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - bu = bv.Uint() - default: - return nil, errors.New("Can't apply the operator to the values") - } - case reflect.String: - as := av.String() - if bv.Kind() == reflect.String && op == '+' { - bs := bv.String() - return as + bs, nil - } - return nil, errors.New("Can't apply the operator to the values") - default: - return nil, errors.New("Can't apply the operator to the values") - } - - switch op { - case '+': - if ai != 0 || bi != 0 { - return ai + bi, nil - } else if af != 0 || bf != 0 { - return af + bf, nil - } else if au != 0 || bu != 0 { - return au + bu, nil - } - return 0, nil - case '-': - if ai != 0 || bi != 0 { - return ai - bi, nil - } else if af != 0 || bf != 0 { - return af - bf, nil - } else if au != 0 || bu != 0 { - return au - bu, nil - } - return 0, nil - case '*': - if ai != 0 || bi != 0 { - return ai * bi, nil - } else if af != 0 || bf != 0 { - return af * bf, nil - } else if au != 0 || bu != 0 { - return au * bu, nil - } - return 0, nil - case '/': - if bi != 0 { - return ai / bi, nil - } else if bf != 0 { - return af / bf, nil - } else if bu != 0 { - return au / bu, nil - } - return nil, errors.New("Can't divide the value by 0") - default: - return nil, errors.New("There is no such an operation") - } + return _math.DoArithmetic(a, b, '-') } diff --git a/tpl/math/math_test.go b/tpl/math/math_test.go index 97acfaeba..f2e6236af 100644 --- a/tpl/math/math_test.go +++ b/tpl/math/math_test.go @@ -56,93 +56,6 @@ func TestBasicNSArithmetic(t *testing.T) { } } -func TestDoArithmetic(t *testing.T) { - t.Parallel() - - for i, test := range []struct { - a interface{} - b interface{} - op rune - expect interface{} - }{ - {3, 2, '+', int64(5)}, - {3, 2, '-', int64(1)}, - {3, 2, '*', int64(6)}, - {3, 2, '/', int64(1)}, - {3.0, 2, '+', float64(5)}, - {3.0, 2, '-', float64(1)}, - {3.0, 2, '*', float64(6)}, - {3.0, 2, '/', float64(1.5)}, - {3, 2.0, '+', float64(5)}, - {3, 2.0, '-', float64(1)}, - {3, 2.0, '*', float64(6)}, - {3, 2.0, '/', float64(1.5)}, - {3.0, 2.0, '+', float64(5)}, - {3.0, 2.0, '-', float64(1)}, - {3.0, 2.0, '*', float64(6)}, - {3.0, 2.0, '/', float64(1.5)}, - {uint(3), uint(2), '+', uint64(5)}, - {uint(3), uint(2), '-', uint64(1)}, - {uint(3), uint(2), '*', uint64(6)}, - {uint(3), uint(2), '/', uint64(1)}, - {uint(3), 2, '+', uint64(5)}, - {uint(3), 2, '-', uint64(1)}, - {uint(3), 2, '*', uint64(6)}, - {uint(3), 2, '/', uint64(1)}, - {3, uint(2), '+', uint64(5)}, - {3, uint(2), '-', uint64(1)}, - {3, uint(2), '*', uint64(6)}, - {3, uint(2), '/', uint64(1)}, - {uint(3), -2, '+', int64(1)}, - {uint(3), -2, '-', int64(5)}, - {uint(3), -2, '*', int64(-6)}, - {uint(3), -2, '/', int64(-1)}, - {-3, uint(2), '+', int64(-1)}, - {-3, uint(2), '-', int64(-5)}, - {-3, uint(2), '*', int64(-6)}, - {-3, uint(2), '/', int64(-1)}, - {uint(3), 2.0, '+', float64(5)}, - {uint(3), 2.0, '-', float64(1)}, - {uint(3), 2.0, '*', float64(6)}, - {uint(3), 2.0, '/', float64(1.5)}, - {3.0, uint(2), '+', float64(5)}, - {3.0, uint(2), '-', float64(1)}, - {3.0, uint(2), '*', float64(6)}, - {3.0, uint(2), '/', float64(1.5)}, - {0, 0, '+', 0}, - {0, 0, '-', 0}, - {0, 0, '*', 0}, - {"foo", "bar", '+', "foobar"}, - {3, 0, '/', false}, - {3.0, 0, '/', false}, - {3, 0.0, '/', false}, - {uint(3), uint(0), '/', false}, - {3, uint(0), '/', false}, - {-3, uint(0), '/', false}, - {uint(3), 0, '/', false}, - {3.0, uint(0), '/', false}, - {uint(3), 0.0, '/', false}, - {3, "foo", '+', false}, - {3.0, "foo", '+', false}, - {uint(3), "foo", '+', false}, - {"foo", 3, '+', false}, - {"foo", "bar", '-', false}, - {3, 2, '%', false}, - } { - errMsg := fmt.Sprintf("[%d] %v", i, test) - - result, err := DoArithmetic(test.a, test.b, test.op) - - if b, ok := test.expect.(bool); ok && !b { - require.Error(t, err, errMsg) - continue - } - - require.NoError(t, err, errMsg) - assert.Equal(t, test.expect, result, errMsg) - } -} - func TestCeil(t *testing.T) { t.Parallel() diff --git a/tpl/tplimpl/template_funcs_test.go b/tpl/tplimpl/template_funcs_test.go index 341be805a..8594c67a4 100644 --- a/tpl/tplimpl/template_funcs_test.go +++ b/tpl/tplimpl/template_funcs_test.go @@ -125,7 +125,6 @@ func TestTemplateFuncsExamples(t *testing.T) { } } } - } // TODO(bep) it would be dandy to put this one into the partials package, but