Fixing regression for numeric values refs #6708

This commit is a fix for the regression introduced in 2.15
with an attempt to carry all numeric values in json.Number container
(see https://github.com/helm/helm/issues/6708 for more details).

The fix overloads a series of Helm template functions:
  * int64
  * int
  * float64
  * add1
  * add
  * sub
  * div
  * mod
  * mul
  * max
  * min
  * ceil
  * floor
  * round
  * until
  * untilStep
  * splitn
  * abbrev
  * abbrevboth
  * trunc
  * substr
  * repeat
  * randAlphaNum
  * randAlpha
  * randAscii
  * randNumeric
  * wrap
  * wrapWith
  * indent
  * nindent
  * plural
  * slice
  * eq
  * gt
  * ge
  * lt
  * le
  * ne

Corresponding test cases are included in the engine test suite.

Signed-off-by: Oleg Sidorov <oleg.sidorov@booking.com>
pull/6709/head
Oleg Sidorov 6 years ago
parent 9668ad4d90
commit 9ed8bcad89

@ -96,7 +96,7 @@ func FuncMap() template.FuncMap {
f[k] = v f[k] = v
} }
return f return OverloadJsonNumberFuncs(f)
} }
// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates. // Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.

@ -17,6 +17,7 @@ limitations under the License.
package engine package engine
import ( import (
"encoding/json"
"fmt" "fmt"
"sync" "sync"
"testing" "testing"
@ -587,5 +588,465 @@ func TestAlterFuncMap(t *testing.T) {
if gotStrTplWithInclude := outTplWithInclude["TplFunction/templates/base"]; gotStrTplWithInclude != expectedTplStrWithInclude { if gotStrTplWithInclude := outTplWithInclude["TplFunction/templates/base"]; gotStrTplWithInclude != expectedTplStrWithInclude {
t.Errorf("Expected %q, got %q (%v)", expectedTplStrWithInclude, gotStrTplWithInclude, outTplWithInclude) t.Errorf("Expected %q, got %q (%v)", expectedTplStrWithInclude, gotStrTplWithInclude, outTplWithInclude)
} }
}
func TestOverloadFuncs(t *testing.T) {
tests := []struct {
Name string
Templates []*chart.Template
Values chartutil.Values
ExpectTplStr string
}{
{
Name: "TplIntFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | int}}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplInt64Function",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | int64}}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplFloat64Function",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | float64}}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("3.14159265359"),
},
ExpectTplStr: "Evaluate tpl Value: 3.14159265359",
},
{
Name: "TplAdd1Function",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | add1}}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 43",
},
{
Name: "TplAddFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ add .Values.value 1 2 3}}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 48",
},
{
Name: "TplSubFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ sub .Values.value 20}}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 22",
},
{
Name: "TplDivFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ div .Values.value 2}}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 21",
},
{
Name: "TplModFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ mod .Values.value 5}}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 2",
},
{
Name: "TplMulFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ mul .Values.value 1 2 3}}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 252",
},
{
Name: "TplMaxFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ max .Values.value 100 1 0 -1}}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 100",
},
{
Name: "TplMinFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ min .Values.value 100 1 0 -1}}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: -1",
},
{
Name: "TplCeilFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ ceil .Values.value }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("3.14159265359"),
},
ExpectTplStr: "Evaluate tpl Value: 4",
},
{
Name: "TplFloorFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ floor .Values.value }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("3.14159265359"),
},
ExpectTplStr: "Evaluate tpl Value: 3",
},
{
Name: "TplRoundFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ round .Values.value 2 }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("3.14159265359"),
},
ExpectTplStr: "Evaluate tpl Value: 3.14",
},
{
Name: "TplEqFunctionInt",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if eq .Values.value 42 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplEqFunctionFloat",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if eq .Values.value 42.0 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplGtFunctionInt",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if gt .Values.value 41 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplGtFunctionFloat",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if gt .Values.value 41.0 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplGeFunctionInt",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if ge .Values.value 42 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplGeFunctionFloat",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if ge .Values.value 42.0 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplLtFunctionInt",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if lt .Values.value 43 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplLtFunctionFloat",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if lt .Values.value 43.0 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplLeFunctionInt",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if le .Values.value 42 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplLeFunctionFloat",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if le .Values.value 42.0 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplNeFunctionInt",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if ne .Values.value 43 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplNeFunctionFloat",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ if ne .Values.value 43.0 }}Value: {{ .Values.value }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl Value: 42",
},
{
Name: "TplUntilFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ range $ix := until .Values.value }}{{ $ix }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("5"),
},
ExpectTplStr: "Evaluate tpl 01234",
},
{
Name: "TplUntilStepFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ range $ix := untilStep 0 .Values.value 7 }}{{ $ix }} {{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl 0 7 14 21 28 35 ",
},
{
Name: "TplSplitnFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ range $s := splitn \".\" .Values.value \"foo.bar.baz.boo\" }}{{ $s }}{{ end }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("3"),
},
ExpectTplStr: "Evaluate tpl foobarbaz.boo",
},
{
Name: "TplAbbrevFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ abbrev .Values.value \"hello world\" }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("5"),
},
ExpectTplStr: "Evaluate tpl he...",
},
{
Name: "TplAbbrevBothFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ abbrevboth .Values.value 10 \"1234 5678 9123\" }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("5"),
},
ExpectTplStr: "Evaluate tpl ...5678...",
},
{
Name: "TplTruncFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ trunc .Values.value \"hello world\" }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("5"),
},
ExpectTplStr: "Evaluate tpl hello",
},
{
Name: "TplSubstrFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ substr 0 .Values.value \"hello world\" }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("5"),
},
ExpectTplStr: "Evaluate tpl hello",
},
{
Name: "TplRepeatFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ repeat .Values.value \"hello\" }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("3"),
},
ExpectTplStr: "Evaluate tpl hellohellohello",
},
{
Name: "TplWrapFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ wrap .Values.value \"hello world\" }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("5"),
},
ExpectTplStr: "Evaluate tpl hello\nworld",
},
{
Name: "TplWrapWithFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ wrapWith .Values.value \"\t\" \"hello world\" }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("5"),
},
ExpectTplStr: "Evaluate tpl hello\tworld",
},
{
Name: "TplIndentFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ indent .Values.value \"hello world\" }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("4"),
},
ExpectTplStr: "Evaluate tpl hello world",
},
{
Name: "TplNindentFunction",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ nindent .Values.value \"hello world\" }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("4"),
},
ExpectTplStr: "Evaluate tpl \n hello world",
},
{
Name: "TplPluralFunctionSingular",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ plural \"one anchovy\" \"many anchovies\" .Values.value }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("1"),
},
ExpectTplStr: "Evaluate tpl one anchovy",
},
{
Name: "TplPluralFunctionPlural",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ plural \"one anchovy\" \"many anchovies\" .Values.value }}" .}}`)},
},
Values: chartutil.Values{
"value": json.Number("42"),
},
ExpectTplStr: "Evaluate tpl many anchovies",
},
{
Name: "TplSliceFunctionNoOffset",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ slice .Values.slice .Values.value }}" .}}`)},
},
Values: chartutil.Values{
"slice": []int{1, 2, 3, 4, 5},
"value": json.Number("2"),
},
ExpectTplStr: "Evaluate tpl [3 4 5]",
},
{
Name: "TplSliceFunctionWithOffset",
Templates: []*chart.Template{
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "{{ slice .Values.slice .Values.start .Values.end }}" .}}`)},
},
Values: chartutil.Values{
"slice": []int{1, 2, 3, 4, 5},
"start": json.Number("2"),
"end": json.Number("4"),
},
ExpectTplStr: "Evaluate tpl [3 4]",
},
}
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
tplChart := &chart.Chart{
Metadata: &chart.Metadata{Name: tt.Name},
Templates: tt.Templates,
Values: &chart.Config{Raw: ``},
Dependencies: []*chart.Chart{},
}
tplValues := chartutil.Values{
"Values": tt.Values,
"Chart": tplChart.Metadata,
"Release": chartutil.Values{
"Name": "TestRelease",
},
}
outTpl, err := New().Render(tplChart, tplValues)
if err != nil {
t.Fatal(err)
}
if gotTplStr := outTpl[tt.Name+"/templates/base"]; gotTplStr != tt.ExpectTplStr {
t.Errorf("Expected %q, got %q (%v)", tt.ExpectTplStr, gotTplStr, outTpl)
}
})
}
} }

@ -0,0 +1,422 @@
/*
Copyright The Helm Authors.
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 engine
import (
"C"
"encoding/json"
"reflect"
"text/template"
_ "unsafe"
)
// These functions below are linked to unexported test/template functions.
// See https://golang.org/src/text/template/funcs.go for more details.
//go:linkname _templateBuiltinEq text/template.eq
func _templateBuiltinEq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error)
//go:linkname _templateBuiltinGe text/template.ge
func _templateBuiltinGe(arg1, arg2 reflect.Value) (bool, error)
//go:linkname _templateBuiltinGt text/template.gt
func _templateBuiltinGt(arg1, arg2 reflect.Value) (bool, error)
//go:linkname _templateBuiltinLe text/template.le
func _templateBuiltinLe(arg1, arg2 reflect.Value) (bool, error)
//go:linkname _templateBuiltinLt text/template.lt
func _templateBuiltinLt(arg1, arg2 reflect.Value) (bool, error)
//go:linkname _templateBuiltinNe text/template.ne
func _templateBuiltinNe(arg1, arg2 reflect.Value) (bool, error)
func OverloadJsonNumberFuncs(f template.FuncMap) template.FuncMap {
// Locally overloaded functions. The first block is a simple decoration
// on top of existing function map.
// The second block is tricky: it overloads built-in template functions.
overloads := template.FuncMap{
"int64": overloadInt64(f["int64"]),
"int": overloadInt(f["int"]),
"float64": overloadFloat64(f["float64"]),
"add1": overloadAdd1(f["add1"]),
"add": overloadAdd(f["add"]),
"sub": overloadSub(f["sub"]),
"div": overloadDiv(f["div"]),
"mod": overloadMod(f["mod"]),
"mul": overloadMul(f["mul"]),
"max": overloadMax(f["max"]),
"biggest": overloadBiggest(f["biggest"]),
"min": overloadMin(f["min"]),
"ceil": overloadCeil(f["ceil"]),
"floor": overloadFloor(f["floor"]),
"round": overloadRound(f["round"]),
"until": overloadUntil(f["until"]),
"untilStep": overloadUntilStep(f["untilStep"]),
"splitn": overloadSplitn(f["splitn"]),
"abbrev": overloadAbbrev(f["abbrev"]),
"abbrevboth": overloadAbbrevBoth(f["abbrevboth"]),
"trunc": overloadTrunc(f["trunc"]),
"substr": overloadSubstr(f["substr"]),
"repeat": overloadRepeat(f["repeat"]),
"randAlphaNum": overloadRandAlphaNum(f["randAlphaNum"]),
"randAlpha": overloadRandAlpha(f["randAlpha"]),
"randAscii": overloadRandAscii(f["randAscii"]),
"randNumeric": overloadRandNumeric(f["randNumeric"]),
"wrap": overloadWrap(f["wrap"]),
"wrapWith": overloadWrapWith(f["wrapWith"]),
"indent": overloadIndent(f["indent"]),
"nindent": overloadNindent(f["nindent"]),
"plural": overloadPlural(f["plural"]),
"slice": overloadSlice(f["slice"]),
"eq": _overloadTemplateBuiltinOnePlus(_templateBuiltinEq),
"ge": _overloadTemplateBuiltinTuple(_templateBuiltinGe),
"gt": _overloadTemplateBuiltinTuple(_templateBuiltinGt),
"le": _overloadTemplateBuiltinTuple(_templateBuiltinLe),
"lt": _overloadTemplateBuiltinTuple(_templateBuiltinLt),
"ne": _overloadTemplateBuiltinTuple(_templateBuiltinNe),
}
for k, o := range overloads {
f[k] = o
}
return f
}
var (
overloadInt64 = _overloadSingleInt64
overloadInt = _overloadSingleInt
overloadFloat64 = _overloadSingleFloat64
overloadAdd1 = _overloadSingleInt64
overloadAdd = _overloadMultiInt64
overloadSub = _overloadTupleInt64
overloadDiv = _overloadTupleInt64
overloadMod = _overloadTupleInt64
overloadMul = _overloadOnePlusInt64
overloadBiggest = _overloadOnePlusInt64
overloadMax = _overloadOnePlusInt64
overloadMin = _overloadOnePlusInt64
overloadCeil = _overloadSingleFloat64
overloadFloor = _overloadSingleFloat64
overloadAbbrev = _overloadSingleIntStrToStr
overloadAbbrevBoth = _overloadDualIntStrToStr
overloadTrunc = _overloadSingleIntStrToStr
overloadSubstr = _overloadDualIntStrToStr
overloadRepeat = _overloadSingleIntStrToStr
overloadRandAlphaNum = _overloadSingleIntToStr
overloadRandAlpha = _overloadSingleIntToStr
overloadRandAscii = _overloadSingleIntToStr
overloadRandNumeric = _overloadSingleIntToStr
overloadWrap = _overloadSingleIntStrToStr
overloadIndent = _overloadSingleIntStrToStr
overloadNindent = _overloadSingleIntStrToStr
)
// context for built-ins: template builtins are context-dependant and will try
// to cast the arguments to comparable primitives. Here we define 2 constants:
// integer-context and float-context. These are bit flags which we expect to
// check on guessArgsNumCtx return.
const (
ctxInt uint8 = 1 << iota
ctxFloat
)
// guessArgsNumCtx tries to guess numeric argument context. As a result,
// it returns a bit mask with int or/and float context bit set.
// 0 means we couldn't conclude any specific context.
// both 0b01 and 0b10 are normal masks denoting a clear mono-context.
// 0b11 means both float and int arguments have been met in the argument list,
// therefore the context is dirty.
func guessArgsNumCtx(i []interface{}) uint8 {
var ctx uint8
for _, v := range i {
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
ctx |= ctxInt
case reflect.Float32, reflect.Float64:
ctx |= ctxFloat
}
}
return ctx
}
// convArgsToVals takes a list of interface{} arguments and converts them to an
// array of reflect.Value with a little modification. Firstly, it will try to
// guess the argument context: whether it's an int, float or neither. Based on
// this conclusion, it will convert all json.Number values to:
// * int64 for int context
// * float64 for float context
// * string otherwise
// Returns an error if json.Number conversion fails or the original routine
// returns it.
func convArgsToVals(i []interface{}, ctx uint8) ([]reflect.Value, error) {
vals := make([]reflect.Value, 0, len(i))
for _, v := range i {
if jsnum, ok := v.(json.Number); ok {
switch ctx {
case ctxFloat:
fv64, err := jsnum.Float64()
if err != nil {
return nil, err
}
v = fv64
case ctxInt:
iv64, err := jsnum.Int64()
if err != nil {
return nil, err
}
v = iv64
default:
v = jsnum.String()
}
}
vals = append(vals, reflect.ValueOf(v))
}
return vals, nil
}
func _overloadTemplateBuiltinOnePlus(orig interface{}) func(interface{}, ...interface{}) (bool, error) {
return func(a interface{}, i ...interface{}) (bool, error) {
args := append([]interface{}{a}, i...)
ctx := guessArgsNumCtx(args)
vals, err := convArgsToVals(args, ctx)
if err != nil {
return false, err
}
res, err := orig.(func(reflect.Value, ...reflect.Value) (bool, error))(vals[0], vals[1:]...)
return res, err
}
}
func _overloadTemplateBuiltinTuple(orig interface{}) func(interface{}, interface{}) (bool, error) {
return func(a interface{}, b interface{}) (bool, error) {
args := []interface{}{a, b}
ctx := guessArgsNumCtx(args)
vals, err := convArgsToVals(args, ctx)
if err != nil {
return false, err
}
res, err := orig.(func(reflect.Value, reflect.Value) (bool, error))(vals[0], vals[1])
return res, err
}
}
func _overloadSingleInt(orig interface{}) func(interface{}) int {
return func(v interface{}) int {
if num, ok := v.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
v = int(iv64)
}
}
return orig.(func(interface{}) int)(v)
}
}
func _overloadSingleInt64(orig interface{}) func(interface{}) int64 {
return func(v interface{}) int64 {
if num, ok := v.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
v = iv64
}
}
return orig.(func(interface{}) int64)(v)
}
}
func _overloadSingleFloat64(orig interface{}) func(interface{}) float64 {
return func(v interface{}) float64 {
if num, ok := v.(json.Number); ok {
if fv64, err := num.Float64(); err == nil {
v = fv64
}
}
return orig.(func(interface{}) float64)(v)
}
}
func _overloadMultiInt64(orig interface{}) func(...interface{}) int64 {
return func(i ...interface{}) int64 {
convs := make([]interface{}, 0, len(i))
for _, conv := range i {
if num, ok := conv.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
conv = iv64
}
}
convs = append(convs, conv)
}
val := orig.(func(...interface{}) int64)(convs...)
return val
}
}
func _overloadTupleInt64(orig interface{}) func(interface{}, interface{}) int64 {
return func(a, b interface{}) int64 {
convs := [2]interface{}{a, b}
for i := 0; i < len(convs); i++ {
conv := convs[i]
if num, ok := conv.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
conv = iv64
}
}
convs[i] = conv
}
return orig.(func(interface{}, interface{}) int64)(convs[0], convs[1])
}
}
func _overloadOnePlusInt64(orig interface{}) func(interface{}, ...interface{}) int64 {
return func(a interface{}, i ...interface{}) int64 {
convs := make([]interface{}, 0, len(i)+1)
for _, conv := range append([]interface{}{a}, i...) {
if num, ok := conv.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
conv = iv64
}
}
convs = append(convs, conv)
}
return orig.(func(interface{}, ...interface{}) int64)(convs[0], convs[1:]...)
}
}
func _overloadSingleIntStrToStr(orig interface{}) func(interface{}, string) string {
return func(a interface{}, s string) string {
if num, ok := a.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
a = int(iv64)
}
}
return orig.(func(int, string) string)(a.(int), s)
}
}
func _overloadDualIntStrToStr(orig interface{}) func(interface{}, interface{}, string) string {
return func(a, b interface{}, s string) string {
vals := make([]int, 0, 2)
for _, v := range []interface{}{a, b} {
if num, ok := v.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
v = int(iv64)
}
}
vals = append(vals, v.(int))
}
return orig.(func(int, int, string) string)(vals[0], vals[1], s)
}
}
func _overloadSingleIntToStr(orig interface{}) func(interface{}) string {
return func(n interface{}) string {
if num, ok := n.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
n = int(iv64)
}
}
return orig.(func(int) string)(n.(int))
}
}
func overloadRound(orig interface{}) func(interface{}, int, ...float64) float64 {
return func(a interface{}, p int, r_opt ...float64) float64 {
if num, ok := a.(json.Number); ok {
if fv64, err := num.Float64(); err == nil {
a = fv64
}
}
return orig.(func(interface{}, int, ...float64) float64)(a, p, r_opt...)
}
}
func overloadUntil(orig interface{}) func(interface{}) []int {
return func(a interface{}) []int {
if num, ok := a.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
a = int(iv64)
}
}
return orig.(func(int) []int)(a.(int))
}
}
func overloadUntilStep(orig interface{}) func(interface{}, interface{}, interface{}) []int {
return func(start, stop, step interface{}) []int {
vals := make([]int, 0, 3)
for _, v := range []interface{}{start, stop, step} {
if num, ok := v.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
v = int(iv64)
}
}
vals = append(vals, v.(int))
}
return orig.(func(int, int, int) []int)(vals[0], vals[1], vals[2])
}
}
func overloadSplitn(orig interface{}) func(string, interface{}, string) map[string]string {
return func(sep string, n interface{}, str string) map[string]string {
if num, ok := n.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
n = int(iv64)
}
}
return orig.(func(string, int, string) map[string]string)(sep, n.(int), str)
}
}
func overloadWrapWith(orig interface{}) func(interface{}, string, string) string {
return func(l interface{}, sep string, str string) string {
if num, ok := l.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
l = int(iv64)
}
}
return orig.(func(int, string, string) string)(l.(int), sep, str)
}
}
func overloadPlural(orig interface{}) func(string, string, interface{}) string {
return func(one, many string, count interface{}) string {
if num, ok := count.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
count = int(iv64)
}
}
return orig.(func(string, string, int) string)(one, many, count.(int))
}
}
func overloadSlice(orig interface{}) func(interface{}, ...interface{}) interface{} {
return func(list interface{}, indices ...interface{}) interface{} {
convs := make([]interface{}, 0, len(indices))
for _, ix := range indices {
if num, ok := ix.(json.Number); ok {
if iv64, err := num.Int64(); err == nil {
ix = int(iv64)
}
}
convs = append(convs, ix)
}
return orig.(func(interface{}, ...interface{}) interface{})(list, convs...)
}
}
Loading…
Cancel
Save