From af38aaa5e4377c3e6c92b366994d293094926865 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Sun, 2 Oct 2022 15:13:49 +0200 Subject: [PATCH] [templating] Add sprig functions, replace some built-ins Signed-off-by: Knut Ahlers --- functions.go | 29 +++++++-- functions_test.go | 7 +++ go.mod | 9 +++ go.sum | 18 ++++++ internal/template/numeric/numeric.go | 12 ---- wiki/Templating.md | 94 +++++----------------------- 6 files changed, 71 insertions(+), 98 deletions(-) create mode 100644 functions_test.go diff --git a/functions.go b/functions.go index 09458ec..5fec1ce 100644 --- a/functions.go +++ b/functions.go @@ -7,14 +7,20 @@ import ( "text/template" "time" + "github.com/Masterminds/sprig/v3" "github.com/go-irc/irc" log "github.com/sirupsen/logrus" + "github.com/Luzifer/go_helpers/v2/str" korvike "github.com/Luzifer/korvike/functions" "github.com/Luzifer/twitch-bot/plugins" ) -var tplFuncs = newTemplateFuncProvider() +var ( + korvikeBlacklist = []string{"now"} + sprigBlacklist = []string{"env"} + tplFuncs = newTemplateFuncProvider() +) type templateFuncProvider struct { funcs map[string]plugins.TemplateFuncGetter @@ -36,7 +42,20 @@ func (t *templateFuncProvider) GetFuncMap(m *irc.Message, r *plugins.Rule, field out := make(template.FuncMap) + for n, fn := range sprig.TxtFuncMap() { + if str.StringInSlice(n, sprigBlacklist) { + continue + } + if out[n] != nil { + panic(fmt.Sprintf("duplicate function: %s (add in sprig)", n)) + } + out[n] = fn + } + for n, fg := range t.funcs { + if out[n] != nil { + panic(fmt.Sprintf("duplicate function: %s (add in registration)", n)) + } out[n] = fg(m, r, fields) } @@ -70,11 +89,12 @@ func (t *templateFuncProvider) Register(name string, fg plugins.TemplateFuncGett func init() { // Register Korvike functions for n, f := range korvike.GetFunctionMap() { + if str.StringInSlice(n, korvikeBlacklist) { + continue + } tplFuncs.Register(n, plugins.GenericTemplateFunctionGetter(f)) } - tplFuncs.Register("concat", plugins.GenericTemplateFunctionGetter(func(delim string, parts ...string) string { return strings.Join(parts, delim) })) - tplFuncs.Register("formatDuration", plugins.GenericTemplateFunctionGetter(func(dur time.Duration, units ...string) string { dLeft := dur @@ -96,7 +116,4 @@ func init() { return strings.Join(parts, ", ") })) - - tplFuncs.Register("toLower", plugins.GenericTemplateFunctionGetter(strings.ToLower)) - tplFuncs.Register("toUpper", plugins.GenericTemplateFunctionGetter(strings.ToUpper)) } diff --git a/functions_test.go b/functions_test.go new file mode 100644 index 0000000..996dabb --- /dev/null +++ b/functions_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func TestNoFuncCollisions(t *testing.T) { + _ = tplFuncs.GetFuncMap(nil, nil, nil) +} diff --git a/go.mod b/go.mod index 938339e..2388ce7 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/Luzifer/go_helpers/v2 v2.12.2 github.com/Luzifer/korvike/functions v0.6.1 github.com/Luzifer/rconfig/v2 v2.4.0 + github.com/Masterminds/sprig/v3 v3.2.2 github.com/glebarez/go-sqlite v1.18.1 github.com/go-irc/irc v2.1.0+incompatible github.com/gofrs/uuid v4.2.0+incompatible @@ -26,6 +27,8 @@ require ( ) require ( + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.1.1 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/emirpasic/gods v1.12.0 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -39,16 +42,22 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/vault/api v1.1.1 // indirect github.com/hashicorp/vault/sdk v0.2.1 // indirect + github.com/huandu/xstrings v1.3.1 // indirect + github.com/imdario/mergo v0.3.11 // indirect github.com/itchyny/timefmt-go v0.1.4 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sergi/go-diff v1.0.0 // indirect + github.com/shopspring/decimal v1.2.0 // indirect + github.com/spf13/cast v1.3.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/src-d/gcfg v1.4.0 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect diff --git a/go.sum b/go.sum index 66068ca..2b6bdf2 100644 --- a/go.sum +++ b/go.sum @@ -11,6 +11,12 @@ github.com/Luzifer/korvike/functions v0.6.1 h1:OGDaEciVzQh0NUMUxcEK1/vmHLIn4lmne github.com/Luzifer/korvike/functions v0.6.1/go.mod h1:D7C4XN3++eXL3MH87sRPBDEDgL9ylYdEav3Wdp3HCfU= github.com/Luzifer/rconfig/v2 v2.4.0 h1:MAdymTlExAZ8mx5VG8xOFAtFQSpWBipKYQHPOmYTn9o= github.com/Luzifer/rconfig/v2 v2.4.0/go.mod h1:hWF3ZVSusbYlg5bEvCwalEyUSY+0JPJWUiIu7rBmav8= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8= +github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= @@ -135,6 +141,7 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= @@ -195,6 +202,10 @@ github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/r github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/itchyny/gojq v0.12.9 h1:biKpbKwMxVYhCU1d6mR7qMr3f0Hn9F5k5YykCVb3gmM= @@ -247,6 +258,7 @@ github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLv github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -259,6 +271,7 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -326,6 +339,8 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= @@ -333,6 +348,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -365,6 +382,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/internal/template/numeric/numeric.go b/internal/template/numeric/numeric.go index 28d33d3..22f9102 100644 --- a/internal/template/numeric/numeric.go +++ b/internal/template/numeric/numeric.go @@ -7,18 +7,6 @@ import ( ) func Register(args plugins.RegistrationArguments) error { - args.RegisterTemplateFunction("add", plugins.GenericTemplateFunctionGetter(add)) - args.RegisterTemplateFunction("div", plugins.GenericTemplateFunctionGetter(div)) - args.RegisterTemplateFunction("mod", plugins.GenericTemplateFunctionGetter(mod)) - args.RegisterTemplateFunction("mul", plugins.GenericTemplateFunctionGetter(mul)) - args.RegisterTemplateFunction("multiply", plugins.GenericTemplateFunctionGetter(mul)) // DEPRECATED args.RegisterTemplateFunction("pow", plugins.GenericTemplateFunctionGetter(math.Pow)) - args.RegisterTemplateFunction("sub", plugins.GenericTemplateFunctionGetter(sub)) return nil } - -func add(m1, m2 float64) float64 { return m1 + m2 } -func div(m1, m2 float64) float64 { return m1 / m2 } -func mod(m1, m2 int64) int64 { return m1 % m2 } -func mul(m1, m2 float64) float64 { return m1 * m2 } -func sub(m1, m2 float64) float64 { return m1 - m2 } diff --git a/wiki/Templating.md b/wiki/Templating.md index bb90397..47b90cc 100644 --- a/wiki/Templating.md +++ b/wiki/Templating.md @@ -14,7 +14,11 @@ There are certain variables available in the strings with templating enabled: ### Functions -Additionally to the built-in functions there are extra functions available in the templates: +Within templates following functions can be used: + +- built-in functions in `text/template` engine +- functions from [sprig](https://masterminds.github.io/sprig/) function collection +- functions mentioned below Examples below are using this syntax in the code block: @@ -25,19 +29,6 @@ Examples below are using this syntax in the code block: < Output from the template ``` -#### `add` - -Returns float from calculation: `float1 + float2` - -Syntax: `add ` - -Example: - -``` -# {{ printf "%.0f" (add 1 2) }}% -< 3 -``` - #### `arg` Takes the message sent to the channel, splits by space and returns the Nth element @@ -80,7 +71,7 @@ Example: #### `concat` -Join the given string parts with delimiter +Join the given string parts with delimiter (⚠ this replaces the sprig `concat` function: sprig `concat` is not available!) Syntax: `concat <...parts>` @@ -117,19 +108,6 @@ Example: < Luziferus - foobar ``` -#### `div` - -Returns float from calculation: `float1 / float2` - -Syntax: `div ` - -Example: - -``` -# {{ printf "%.0f" (div 27 9) }}% -< 3 -``` - #### `fixUsername` Ensures the username no longer contains the `@` or `#` prefix @@ -227,32 +205,6 @@ Example: < Last Quote: #32 ``` -#### `mod` - -Returns int from calculation: `int1 % int2` - -Syntax: `mod ` - -Example: - -``` -# {{ mod 12 10 }} -< 2 -``` - -#### `mul` (deprecated: `multiply`) - -Returns float from calculation: `float1 * float2` - -Syntax: `mul ` - -Example: - -``` -# {{ printf "%.0f" (mul 100 (seededRandom "test")) }}% -< 35% -``` - #### `pow` Returns float from calculation: `float1 ** float2` @@ -318,19 +270,6 @@ Example: < Your int this hour: 17% ``` -#### `sub` - -Returns float from calculation: `float1 - float2` - -Syntax: `sub ` - -Example: - -``` -# {{ printf "%.0f" (sub 10 4) }}% -< 6 -``` - #### `tag` Takes the message sent to the channel, returns the value of the tag specified @@ -344,19 +283,6 @@ Example: < luziferus ``` -#### `toLower` / `toUpper` - -Converts the given string to lower-case / upper-case - -Syntax: `toLower ` / `toUpper ` - -Example: - -``` -# {{ toLower "Test" }} - {{ toUpper "Test" }} -< test - TEST -``` - #### `variable` Returns the variable value or default in case it is empty @@ -370,3 +296,11 @@ Example: < test - fallback ``` +### Upgrade from `v2.x` to `v3.x` + +When adding [sprig](https://masterminds.github.io/sprig/) function collection some functions collided and needed replacement. You need to adapt your templates accordingly: + +- Math functions (`add`, `div`, `mod`, `mul`, `multiply`, `sub`) were replaced with their sprig-equivalent and are now working with integers instead of floats. If you need them to continue to work with floats you need to use their [float-variants](https://masterminds.github.io/sprig/mathf.html). +- `now` does no longer format the current date as a string but return the current date. You need to replace this: `now "2006-01-02"` becomes `now | date "2006-01-02"`. +- `concat` is now used to concat arrays. To join strings you will need to modify your code: `concat ":" "string1" "string2"` becomes `lists "string1" "string2" | join ":"`. +- `toLower` / `toUpper` need to be replaced with their sprig equivalent `lower` and `upper`.