summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--funcs.go2
-rw-r--r--funcs/aws.go24
-rw-r--r--funcs/aws_test.go19
-rw-r--r--funcs/base64.go26
-rw-r--r--funcs/base64_test.go17
-rw-r--r--funcs/coll.go75
-rw-r--r--funcs/coll_test.go19
-rw-r--r--funcs/conv.go59
-rw-r--r--funcs/conv_test.go19
-rw-r--r--funcs/crypto.go39
-rw-r--r--funcs/crypto_test.go36
-rw-r--r--funcs/data.go16
-rw-r--r--funcs/data_test.go24
-rw-r--r--funcs/doc.go10
-rw-r--r--funcs/env.go21
-rw-r--r--funcs/env_test.go17
-rw-r--r--funcs/file.go21
-rw-r--r--funcs/file_test.go17
-rw-r--r--funcs/filepath.go19
-rw-r--r--funcs/filepath_test.go34
-rw-r--r--funcs/filepath_unix_test.go30
-rw-r--r--funcs/filepath_windows_test.go2
-rw-r--r--funcs/gcp.go27
-rw-r--r--funcs/gcp_test.go24
-rw-r--r--funcs/math.go51
-rw-r--r--funcs/math_test.go53
-rw-r--r--funcs/net.go34
-rw-r--r--funcs/net_test.go19
-rw-r--r--funcs/path.go34
-rw-r--r--funcs/path_test.go19
-rw-r--r--funcs/random.go32
-rw-r--r--funcs/random_test.go43
-rw-r--r--funcs/regexp.go32
-rw-r--r--funcs/regexp_test.go17
-rw-r--r--funcs/sockaddr.go56
-rw-r--r--funcs/sockaddr_test.go24
-rw-r--r--funcs/strings.go70
-rw-r--r--funcs/strings_test.go17
-rw-r--r--funcs/test.go29
-rw-r--r--funcs/test_test.go17
-rw-r--r--funcs/time.go101
-rw-r--r--funcs/time_test.go17
-rw-r--r--funcs/uuid.go30
-rw-r--r--funcs/uuid_test.go17
44 files changed, 814 insertions, 495 deletions
diff --git a/funcs.go b/funcs.go
index 97c1cc18..53c35568 100644
--- a/funcs.go
+++ b/funcs.go
@@ -5,7 +5,7 @@ import (
"text/template"
"github.com/hairyhenderson/gomplate/v3/data"
- "github.com/hairyhenderson/gomplate/v3/funcs"
+ "github.com/hairyhenderson/gomplate/v3/funcs" //nolint:staticcheck
"github.com/hairyhenderson/gomplate/v3/internal/config"
)
diff --git a/funcs/aws.go b/funcs/aws.go
index 8f5f9380..bc4a573c 100644
--- a/funcs/aws.go
+++ b/funcs/aws.go
@@ -8,22 +8,15 @@ import (
"github.com/hairyhenderson/gomplate/v3/conv"
)
-var (
- af *Funcs
- afInit sync.Once
-)
-
// AWSNS - the aws namespace
+// Deprecated: don't use
+//nolint:golint
func AWSNS() *Funcs {
- afInit.Do(func() {
- af = &Funcs{
- awsopts: aws.GetClientOptions(),
- }
- })
- return af
+ return &Funcs{}
}
// AWSFuncs -
+// Deprecated: use CreateAWSFuncs instead
func AWSFuncs(f map[string]interface{}) {
f2 := CreateAWSFuncs(context.Background())
for k, v := range f2 {
@@ -34,10 +27,13 @@ func AWSFuncs(f map[string]interface{}) {
// CreateAWSFuncs -
func CreateAWSFuncs(ctx context.Context) map[string]interface{} {
f := map[string]interface{}{}
- ns := AWSNS()
- ns.ctx = ctx
- f["aws"] = AWSNS
+ ns := &Funcs{
+ ctx: ctx,
+ awsopts: aws.GetClientOptions(),
+ }
+
+ f["aws"] = func() interface{} { return ns }
// global aliases - for backwards compatibility
f["ec2meta"] = ns.EC2Meta
diff --git a/funcs/aws_test.go b/funcs/aws_test.go
index 22b6da65..79324f70 100644
--- a/funcs/aws_test.go
+++ b/funcs/aws_test.go
@@ -1,16 +1,27 @@
package funcs
import (
+ "context"
+ "strconv"
"testing"
"github.com/hairyhenderson/gomplate/v3/aws"
"github.com/stretchr/testify/assert"
)
-func TestNSIsIdempotent(t *testing.T) {
- left := AWSNS()
- right := AWSNS()
- assert.True(t, left == right)
+func TestCreateAWSFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateAWSFuncs(ctx)
+ actual := fmap["aws"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*Funcs).ctx)
+ })
+ }
}
func TestAWSFuncs(t *testing.T) {
diff --git a/funcs/base64.go b/funcs/base64.go
index 0b913008..17cdf5ae 100644
--- a/funcs/base64.go
+++ b/funcs/base64.go
@@ -2,24 +2,19 @@ package funcs
import (
"context"
- "sync"
"github.com/hairyhenderson/gomplate/v3/base64"
"github.com/hairyhenderson/gomplate/v3/conv"
)
-var (
- bf *Base64Funcs
- bfInit sync.Once
-)
-
// Base64NS - the base64 namespace
+// Deprecated: don't use
func Base64NS() *Base64Funcs {
- bfInit.Do(func() { bf = &Base64Funcs{} })
- return bf
+ return &Base64Funcs{}
}
// AddBase64Funcs -
+// Deprecated: use CreateBase64Funcs instead
func AddBase64Funcs(f map[string]interface{}) {
for k, v := range CreateBase64Funcs(context.Background()) {
f[k] = v
@@ -28,9 +23,12 @@ func AddBase64Funcs(f map[string]interface{}) {
// CreateBase64Funcs -
func CreateBase64Funcs(ctx context.Context) map[string]interface{} {
- ns := Base64NS()
- ns.ctx = ctx
- return map[string]interface{}{"base64": Base64NS}
+ f := map[string]interface{}{}
+
+ ns := &Base64Funcs{ctx}
+ f["base64"] = func() interface{} { return ns }
+
+ return f
}
// Base64Funcs -
@@ -39,19 +37,19 @@ type Base64Funcs struct {
}
// Encode -
-func (f *Base64Funcs) Encode(in interface{}) (string, error) {
+func (Base64Funcs) Encode(in interface{}) (string, error) {
b := toBytes(in)
return base64.Encode(b)
}
// Decode -
-func (f *Base64Funcs) Decode(in interface{}) (string, error) {
+func (Base64Funcs) Decode(in interface{}) (string, error) {
out, err := base64.Decode(conv.ToString(in))
return string(out), err
}
// DecodeBytes -
-func (f *Base64Funcs) DecodeBytes(in interface{}) ([]byte, error) {
+func (Base64Funcs) DecodeBytes(in interface{}) ([]byte, error) {
out, err := base64.Decode(conv.ToString(in))
return out, err
}
diff --git a/funcs/base64_test.go b/funcs/base64_test.go
index 6cfe2b9e..4c611fee 100644
--- a/funcs/base64_test.go
+++ b/funcs/base64_test.go
@@ -2,11 +2,28 @@ package funcs
import (
"bytes"
+ "context"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreateBase64Funcs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateBase64Funcs(ctx)
+ actual := fmap["base64"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*Base64Funcs).ctx)
+ })
+ }
+}
+
func TestBase64Encode(t *testing.T) {
bf := &Base64Funcs{}
assert.Equal(t, "Zm9vYmFy", must(bf.Encode("foobar")))
diff --git a/funcs/coll.go b/funcs/coll.go
index ae36fbd7..8c6e9f47 100644
--- a/funcs/coll.go
+++ b/funcs/coll.go
@@ -2,7 +2,6 @@ package funcs
import (
"context"
- "sync"
"github.com/hairyhenderson/gomplate/v3/conv"
@@ -10,18 +9,14 @@ import (
"github.com/pkg/errors"
)
-var (
- collNS *CollFuncs
- collNSInit sync.Once
-)
-
// CollNS -
+// Deprecated: don't use
func CollNS() *CollFuncs {
- collNSInit.Do(func() { collNS = &CollFuncs{} })
- return collNS
+ return &CollFuncs{}
}
// AddCollFuncs -
+// Deprecated: use CreateCollFuncs instead
func AddCollFuncs(f map[string]interface{}) {
for k, v := range CreateCollFuncs(context.Background()) {
f[k] = v
@@ -30,24 +25,24 @@ func AddCollFuncs(f map[string]interface{}) {
// CreateCollFuncs -
func CreateCollFuncs(ctx context.Context) map[string]interface{} {
- ns := CollNS()
- ns.ctx = ctx
f := map[string]interface{}{}
- f["coll"] = CollNS
-
- f["has"] = CollNS().Has
- f["slice"] = CollNS().Slice
- f["dict"] = CollNS().Dict
- f["keys"] = CollNS().Keys
- f["values"] = CollNS().Values
- f["append"] = CollNS().Append
- f["prepend"] = CollNS().Prepend
- f["uniq"] = CollNS().Uniq
- f["reverse"] = CollNS().Reverse
- f["merge"] = CollNS().Merge
- f["sort"] = CollNS().Sort
- f["jsonpath"] = CollNS().JSONPath
- f["flatten"] = CollNS().Flatten
+
+ ns := &CollFuncs{ctx}
+ f["coll"] = func() interface{} { return ns }
+
+ f["has"] = ns.Has
+ f["slice"] = ns.Slice
+ f["dict"] = ns.Dict
+ f["keys"] = ns.Keys
+ f["values"] = ns.Values
+ f["append"] = ns.Append
+ f["prepend"] = ns.Prepend
+ f["uniq"] = ns.Uniq
+ f["reverse"] = ns.Reverse
+ f["merge"] = ns.Merge
+ f["sort"] = ns.Sort
+ f["jsonpath"] = ns.JSONPath
+ f["flatten"] = ns.Flatten
return f
}
@@ -57,57 +52,57 @@ type CollFuncs struct {
}
// Slice -
-func (f *CollFuncs) Slice(args ...interface{}) []interface{} {
+func (CollFuncs) Slice(args ...interface{}) []interface{} {
return coll.Slice(args...)
}
// Has -
-func (f *CollFuncs) Has(in interface{}, key string) bool {
+func (CollFuncs) Has(in interface{}, key string) bool {
return coll.Has(in, key)
}
// Dict -
-func (f *CollFuncs) Dict(in ...interface{}) (map[string]interface{}, error) {
+func (CollFuncs) Dict(in ...interface{}) (map[string]interface{}, error) {
return coll.Dict(in...)
}
// Keys -
-func (f *CollFuncs) Keys(in ...map[string]interface{}) ([]string, error) {
+func (CollFuncs) Keys(in ...map[string]interface{}) ([]string, error) {
return coll.Keys(in...)
}
// Values -
-func (f *CollFuncs) Values(in ...map[string]interface{}) ([]interface{}, error) {
+func (CollFuncs) Values(in ...map[string]interface{}) ([]interface{}, error) {
return coll.Values(in...)
}
// Append -
-func (f *CollFuncs) Append(v interface{}, list interface{}) ([]interface{}, error) {
+func (CollFuncs) Append(v interface{}, list interface{}) ([]interface{}, error) {
return coll.Append(v, list)
}
// Prepend -
-func (f *CollFuncs) Prepend(v interface{}, list interface{}) ([]interface{}, error) {
+func (CollFuncs) Prepend(v interface{}, list interface{}) ([]interface{}, error) {
return coll.Prepend(v, list)
}
// Uniq -
-func (f *CollFuncs) Uniq(in interface{}) ([]interface{}, error) {
+func (CollFuncs) Uniq(in interface{}) ([]interface{}, error) {
return coll.Uniq(in)
}
// Reverse -
-func (f *CollFuncs) Reverse(in interface{}) ([]interface{}, error) {
+func (CollFuncs) Reverse(in interface{}) ([]interface{}, error) {
return coll.Reverse(in)
}
// Merge -
-func (f *CollFuncs) Merge(dst map[string]interface{}, src ...map[string]interface{}) (map[string]interface{}, error) {
+func (CollFuncs) Merge(dst map[string]interface{}, src ...map[string]interface{}) (map[string]interface{}, error) {
return coll.Merge(dst, src...)
}
// Sort -
-func (f *CollFuncs) Sort(args ...interface{}) ([]interface{}, error) {
+func (CollFuncs) Sort(args ...interface{}) ([]interface{}, error) {
var (
key string
list interface{}
@@ -126,12 +121,12 @@ func (f *CollFuncs) Sort(args ...interface{}) ([]interface{}, error) {
}
// JSONPath -
-func (f *CollFuncs) JSONPath(p string, in interface{}) (interface{}, error) {
+func (CollFuncs) JSONPath(p string, in interface{}) (interface{}, error) {
return coll.JSONPath(p, in)
}
// Flatten -
-func (f *CollFuncs) Flatten(args ...interface{}) ([]interface{}, error) {
+func (CollFuncs) Flatten(args ...interface{}) ([]interface{}, error) {
if len(args) == 0 || len(args) > 2 {
return nil, errors.Errorf("wrong number of args: wanted 1 or 2, got %d", len(args))
}
@@ -166,7 +161,7 @@ func pickOmitArgs(args ...interface{}) (map[string]interface{}, []string, error)
}
// Pick -
-func (f *CollFuncs) Pick(args ...interface{}) (map[string]interface{}, error) {
+func (CollFuncs) Pick(args ...interface{}) (map[string]interface{}, error) {
m, keys, err := pickOmitArgs(args...)
if err != nil {
return nil, err
@@ -175,7 +170,7 @@ func (f *CollFuncs) Pick(args ...interface{}) (map[string]interface{}, error) {
}
// Omit -
-func (f *CollFuncs) Omit(args ...interface{}) (map[string]interface{}, error) {
+func (CollFuncs) Omit(args ...interface{}) (map[string]interface{}, error) {
m, keys, err := pickOmitArgs(args...)
if err != nil {
return nil, err
diff --git a/funcs/coll_test.go b/funcs/coll_test.go
index da554a13..44dcced4 100644
--- a/funcs/coll_test.go
+++ b/funcs/coll_test.go
@@ -1,13 +1,30 @@
package funcs
import (
+ "context"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreateCollFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateCollFuncs(ctx)
+ actual := fmap["coll"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*CollFuncs).ctx)
+ })
+ }
+}
+
func TestFlatten(t *testing.T) {
- c := CollNS()
+ c := CollFuncs{}
_, err := c.Flatten()
assert.Error(t, err)
diff --git a/funcs/conv.go b/funcs/conv.go
index 986d4833..a6bf7c9f 100644
--- a/funcs/conv.go
+++ b/funcs/conv.go
@@ -3,25 +3,20 @@ package funcs
import (
"context"
"net/url"
- "sync"
"text/template"
"github.com/hairyhenderson/gomplate/v3/coll"
"github.com/hairyhenderson/gomplate/v3/conv"
)
-var (
- convNS *ConvFuncs
- convNSInit sync.Once
-)
-
// ConvNS -
+// Deprecated: don't use
func ConvNS() *ConvFuncs {
- convNSInit.Do(func() { convNS = &ConvFuncs{} })
- return convNS
+ return &ConvFuncs{}
}
// AddConvFuncs -
+// Deprecated: use CreateConvFuncs instead
func AddConvFuncs(f map[string]interface{}) {
for k, v := range CreateConvFuncs(context.Background()) {
f[k] = v
@@ -30,10 +25,10 @@ func AddConvFuncs(f map[string]interface{}) {
// CreateConvFuncs -
func CreateConvFuncs(ctx context.Context) map[string]interface{} {
- ns := ConvNS()
- ns.ctx = ctx
+ ns := &ConvFuncs{ctx}
+
f := map[string]interface{}{}
- f["conv"] = ConvNS
+ f["conv"] = func() interface{} { return ns }
f["urlParse"] = ns.URL
f["bool"] = ns.Bool
@@ -48,102 +43,102 @@ type ConvFuncs struct {
}
// Bool -
-func (f *ConvFuncs) Bool(s interface{}) bool {
+func (ConvFuncs) Bool(s interface{}) bool {
return conv.Bool(conv.ToString(s))
}
// ToBool -
-func (f *ConvFuncs) ToBool(in interface{}) bool {
+func (ConvFuncs) ToBool(in interface{}) bool {
return conv.ToBool(in)
}
// ToBools -
-func (f *ConvFuncs) ToBools(in ...interface{}) []bool {
+func (ConvFuncs) ToBools(in ...interface{}) []bool {
return conv.ToBools(in...)
}
// Slice -
-func (f *ConvFuncs) Slice(args ...interface{}) []interface{} {
+func (ConvFuncs) Slice(args ...interface{}) []interface{} {
return coll.Slice(args...)
}
// Join -
-func (f *ConvFuncs) Join(in interface{}, sep string) (string, error) {
+func (ConvFuncs) Join(in interface{}, sep string) (string, error) {
return conv.Join(in, sep)
}
// Has -
-func (f *ConvFuncs) Has(in interface{}, key string) bool {
+func (ConvFuncs) Has(in interface{}, key string) bool {
return coll.Has(in, key)
}
// ParseInt -
-func (f *ConvFuncs) ParseInt(s interface{}, base, bitSize int) int64 {
+func (ConvFuncs) ParseInt(s interface{}, base, bitSize int) int64 {
return conv.MustParseInt(conv.ToString(s), base, bitSize)
}
// ParseFloat -
-func (f *ConvFuncs) ParseFloat(s interface{}, bitSize int) float64 {
+func (ConvFuncs) ParseFloat(s interface{}, bitSize int) float64 {
return conv.MustParseFloat(conv.ToString(s), bitSize)
}
// ParseUint -
-func (f *ConvFuncs) ParseUint(s interface{}, base, bitSize int) uint64 {
+func (ConvFuncs) ParseUint(s interface{}, base, bitSize int) uint64 {
return conv.MustParseUint(conv.ToString(s), base, bitSize)
}
// Atoi -
-func (f *ConvFuncs) Atoi(s interface{}) int {
+func (ConvFuncs) Atoi(s interface{}) int {
return conv.MustAtoi(conv.ToString(s))
}
// URL -
-func (f *ConvFuncs) URL(s interface{}) (*url.URL, error) {
+func (ConvFuncs) URL(s interface{}) (*url.URL, error) {
return url.Parse(conv.ToString(s))
}
// ToInt64 -
-func (f *ConvFuncs) ToInt64(in interface{}) int64 {
+func (ConvFuncs) ToInt64(in interface{}) int64 {
return conv.ToInt64(in)
}
// ToInt -
-func (f *ConvFuncs) ToInt(in interface{}) int {
+func (ConvFuncs) ToInt(in interface{}) int {
return conv.ToInt(in)
}
// ToInt64s -
-func (f *ConvFuncs) ToInt64s(in ...interface{}) []int64 {
+func (ConvFuncs) ToInt64s(in ...interface{}) []int64 {
return conv.ToInt64s(in...)
}
// ToInts -
-func (f *ConvFuncs) ToInts(in ...interface{}) []int {
+func (ConvFuncs) ToInts(in ...interface{}) []int {
return conv.ToInts(in...)
}
// ToFloat64 -
-func (f *ConvFuncs) ToFloat64(in interface{}) float64 {
+func (ConvFuncs) ToFloat64(in interface{}) float64 {
return conv.ToFloat64(in)
}
// ToFloat64s -
-func (f *ConvFuncs) ToFloat64s(in ...interface{}) []float64 {
+func (ConvFuncs) ToFloat64s(in ...interface{}) []float64 {
return conv.ToFloat64s(in...)
}
// ToString -
-func (f *ConvFuncs) ToString(in interface{}) string {
+func (ConvFuncs) ToString(in interface{}) string {
return conv.ToString(in)
}
// ToStrings -
-func (f *ConvFuncs) ToStrings(in ...interface{}) []string {
+func (ConvFuncs) ToStrings(in ...interface{}) []string {
return conv.ToStrings(in...)
}
// Default -
-func (f *ConvFuncs) Default(def, in interface{}) interface{} {
+func (ConvFuncs) Default(def, in interface{}) interface{} {
if truth, ok := template.IsTrue(in); truth && ok {
return in
}
@@ -151,6 +146,6 @@ func (f *ConvFuncs) Default(def, in interface{}) interface{} {
}
// Dict -
-func (f *ConvFuncs) Dict(in ...interface{}) (map[string]interface{}, error) {
+func (ConvFuncs) Dict(in ...interface{}) (map[string]interface{}, error) {
return coll.Dict(in...)
}
diff --git a/funcs/conv_test.go b/funcs/conv_test.go
index 9fbfb8bb..b9aa757e 100644
--- a/funcs/conv_test.go
+++ b/funcs/conv_test.go
@@ -1,15 +1,32 @@
package funcs
import (
+ "context"
"fmt"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreateConvFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateConvFuncs(ctx)
+ actual := fmap["conv"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*ConvFuncs).ctx)
+ })
+ }
+}
+
func TestDefault(t *testing.T) {
s := struct{}{}
- c := ConvNS()
+ c := &ConvFuncs{}
def := "DEFAULT"
data := []struct {
val interface{}
diff --git a/funcs/crypto.go b/funcs/crypto.go
index 0b6049af..9a241fb9 100644
--- a/funcs/crypto.go
+++ b/funcs/crypto.go
@@ -7,7 +7,6 @@ import (
"crypto/sha256"
"crypto/sha512"
"fmt"
- "sync"
"golang.org/x/crypto/bcrypt"
@@ -17,18 +16,14 @@ import (
"github.com/hairyhenderson/gomplate/v3/crypto"
)
-var (
- cryptoNS *CryptoFuncs
- cryptoNSInit sync.Once
-)
-
// CryptoNS - the crypto namespace
+// Deprecated: don't use
func CryptoNS() *CryptoFuncs {
- cryptoNSInit.Do(func() { cryptoNS = &CryptoFuncs{} })
- return cryptoNS
+ return &CryptoFuncs{}
}
// AddCryptoFuncs -
+// Deprecated: use CreateCryptoFuncs instead
func AddCryptoFuncs(f map[string]interface{}) {
for k, v := range CreateCryptoFuncs(context.Background()) {
f[k] = v
@@ -37,10 +32,12 @@ func AddCryptoFuncs(f map[string]interface{}) {
// CreateCryptoFuncs -
func CreateCryptoFuncs(ctx context.Context) map[string]interface{} {
- ns := CryptoNS()
- ns.ctx = ctx
+ f := map[string]interface{}{}
+
+ ns := &CryptoFuncs{ctx}
- return map[string]interface{}{"crypto": CryptoNS}
+ f["crypto"] = func() interface{} { return ns }
+ return f
}
// CryptoFuncs -
@@ -51,7 +48,7 @@ type CryptoFuncs struct {
// PBKDF2 - Run the Password-Based Key Derivation Function #2 as defined in
// RFC 2898 (PKCS #5 v2.0). This function outputs the binary result in hex
// format.
-func (f *CryptoFuncs) PBKDF2(password, salt, iter, keylen interface{}, hashFunc ...string) (k string, err error) {
+func (CryptoFuncs) PBKDF2(password, salt, iter, keylen interface{}, hashFunc ...string) (k string, err error) {
var h gcrypto.Hash
if len(hashFunc) == 0 {
h = gcrypto.SHA1
@@ -71,12 +68,12 @@ func (f *CryptoFuncs) PBKDF2(password, salt, iter, keylen interface{}, hashFunc
}
// WPAPSK - Convert an ASCII passphrase to WPA PSK for a given SSID
-func (f *CryptoFuncs) WPAPSK(ssid, password interface{}) (string, error) {
+func (f CryptoFuncs) WPAPSK(ssid, password interface{}) (string, error) {
return f.PBKDF2(password, ssid, 4096, 32)
}
// SHA1 - Note: SHA-1 is cryptographically broken and should not be used for secure applications.
-func (f *CryptoFuncs) SHA1(input interface{}) string {
+func (CryptoFuncs) SHA1(input interface{}) string {
in := toBytes(input)
// nolint: gosec
out := sha1.Sum(in)
@@ -84,28 +81,28 @@ func (f *CryptoFuncs) SHA1(input interface{}) string {
}
// SHA224 -
-func (f *CryptoFuncs) SHA224(input interface{}) string {
+func (CryptoFuncs) SHA224(input interface{}) string {
in := toBytes(input)
out := sha256.Sum224(in)
return fmt.Sprintf("%02x", out)
}
// SHA256 -
-func (f *CryptoFuncs) SHA256(input interface{}) string {
+func (CryptoFuncs) SHA256(input interface{}) string {
in := toBytes(input)
out := sha256.Sum256(in)
return fmt.Sprintf("%02x", out)
}
// SHA384 -
-func (f *CryptoFuncs) SHA384(input interface{}) string {
+func (CryptoFuncs) SHA384(input interface{}) string {
in := toBytes(input)
out := sha512.Sum384(in)
return fmt.Sprintf("%02x", out)
}
// SHA512 -
-func (f *CryptoFuncs) SHA512(input interface{}) string {
+func (CryptoFuncs) SHA512(input interface{}) string {
in := toBytes(input)
out := sha512.Sum512(in)
return fmt.Sprintf("%02x", out)
@@ -113,7 +110,7 @@ func (f *CryptoFuncs) SHA512(input interface{}) string {
// SHA512_224 -
//nolint: golint,stylecheck
-func (f *CryptoFuncs) SHA512_224(input interface{}) string {
+func (CryptoFuncs) SHA512_224(input interface{}) string {
in := toBytes(input)
out := sha512.Sum512_224(in)
return fmt.Sprintf("%02x", out)
@@ -121,14 +118,14 @@ func (f *CryptoFuncs) SHA512_224(input interface{}) string {
// SHA512_256 -
//nolint: golint,stylecheck
-func (f *CryptoFuncs) SHA512_256(input interface{}) string {
+func (CryptoFuncs) SHA512_256(input interface{}) string {
in := toBytes(input)
out := sha512.Sum512_256(in)
return fmt.Sprintf("%02x", out)
}
// Bcrypt -
-func (f *CryptoFuncs) Bcrypt(args ...interface{}) (string, error) {
+func (CryptoFuncs) Bcrypt(args ...interface{}) (string, error) {
input := ""
cost := bcrypt.DefaultCost
if len(args) == 0 {
diff --git a/funcs/crypto_test.go b/funcs/crypto_test.go
index b61643f7..adcaf731 100644
--- a/funcs/crypto_test.go
+++ b/funcs/crypto_test.go
@@ -2,6 +2,7 @@ package funcs
import (
"context"
+ "strconv"
"strings"
"testing"
@@ -9,19 +10,35 @@ import (
"github.com/stretchr/testify/assert"
)
+func TestCreateCryptoFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateCryptoFuncs(ctx)
+ actual := fmap["crypto"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*CryptoFuncs).ctx)
+ })
+ }
+}
+
func testCryptoNS() *CryptoFuncs {
ctx := context.Background()
cfg := config.FromContext(ctx)
cfg.Experimental = true
ctx = config.ContextWithConfig(ctx, cfg)
- c := CryptoNS()
- c.ctx = ctx
- return c
+
+ return &CryptoFuncs{
+ ctx: ctx,
+ }
}
func TestPBKDF2(t *testing.T) {
c := testCryptoNS()
- dk, err := cryptoNS.PBKDF2("password", []byte("IEEE"), "4096", 32)
+ dk, err := c.PBKDF2("password", []byte("IEEE"), "4096", 32)
assert.Equal(t, "f42c6fc52df0ebef9ebb4b90b38a5f902e83fe1b135a70e23aed762e9710a12e", dk)
assert.NoError(t, err)
@@ -34,7 +51,8 @@ func TestPBKDF2(t *testing.T) {
}
func TestWPAPSK(t *testing.T) {
- dk, err := cryptoNS.WPAPSK("password", "MySSID")
+ c := testCryptoNS()
+ dk, err := c.WPAPSK("password", "MySSID")
assert.Equal(t, "3a98def84b11644a17ebcc9b17955d2360ce8b8a85b8a78413fc551d722a84e7", dk)
assert.NoError(t, err)
}
@@ -59,6 +77,10 @@ func TestSHA(t *testing.T) {
}
func TestBcrypt(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping slow test")
+ }
+
in := "foo"
c := testCryptoNS()
actual, err := c.Bcrypt(in)
@@ -94,6 +116,10 @@ func TestRSAGenerateKey(t *testing.T) {
}
func TestRSACrypt(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping slow test")
+ }
+
c := testCryptoNS()
key, err := c.RSAGenerateKey()
assert.NoError(t, err)
diff --git a/funcs/data.go b/funcs/data.go
index 0a9c4a8a..a6e48ba3 100644
--- a/funcs/data.go
+++ b/funcs/data.go
@@ -2,24 +2,19 @@ package funcs
import (
"context"
- "sync"
"github.com/hairyhenderson/gomplate/v3/conv"
"github.com/hairyhenderson/gomplate/v3/data"
)
-var (
- dataNS *DataFuncs
- dataNSInit sync.Once
-)
-
// DataNS -
+// Deprecated: don't use
func DataNS() *DataFuncs {
- dataNSInit.Do(func() { dataNS = &DataFuncs{} })
- return dataNS
+ return &DataFuncs{}
}
// AddDataFuncs -
+// Deprecated: use CreateDataFuncs instead
func AddDataFuncs(f map[string]interface{}, d *data.Data) {
for k, v := range CreateDataFuncs(context.Background(), d) {
f[k] = v
@@ -36,10 +31,9 @@ func CreateDataFuncs(ctx context.Context, d *data.Data) map[string]interface{} {
f["defineDatasource"] = d.DefineDatasource
f["include"] = d.Include
- ns := DataNS()
- ns.ctx = ctx
+ ns := &DataFuncs{ctx}
- f["data"] = DataNS
+ f["data"] = func() interface{} { return ns }
f["json"] = ns.JSON
f["jsonArray"] = ns.JSONArray
diff --git a/funcs/data_test.go b/funcs/data_test.go
new file mode 100644
index 00000000..fee6494a
--- /dev/null
+++ b/funcs/data_test.go
@@ -0,0 +1,24 @@
+package funcs
+
+import (
+ "context"
+ "strconv"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCreateDataFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateDataFuncs(ctx, nil)
+ actual := fmap["data"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*DataFuncs).ctx)
+ })
+ }
+}
diff --git a/funcs/doc.go b/funcs/doc.go
index bf2d8f20..cb1a5f5c 100644
--- a/funcs/doc.go
+++ b/funcs/doc.go
@@ -5,12 +5,18 @@ functions to be used in 'text/template' templates.
The different namespaces can be added individually:
f := template.FuncMap{}
- funcs.AddMathFuncs(f)
- funcs.AddNetFuncs(f)
+ for k, v := range funcs.CreateMathFuncs(ctx) {
+ f[k] = v
+ }
+ for k, v := range funcs.CreateNetFuncs(ctx) {
+ f[k] = v
+ }
Even though the functions are exported, these are not intended to be called
programmatically by external consumers, but instead only to be used as template
functions.
+Deprecated: This package will be made internal in a future major version.
+
*/
package funcs
diff --git a/funcs/env.go b/funcs/env.go
index 3188eb96..858290bc 100644
--- a/funcs/env.go
+++ b/funcs/env.go
@@ -2,24 +2,19 @@ package funcs
import (
"context"
- "sync"
"github.com/hairyhenderson/gomplate/v3/conv"
"github.com/hairyhenderson/gomplate/v3/env"
)
-var (
- ef *EnvFuncs
- efInit sync.Once
-)
-
// EnvNS - the Env namespace
+// Deprecated: don't use
func EnvNS() *EnvFuncs {
- efInit.Do(func() { ef = &EnvFuncs{} })
- return ef
+ return &EnvFuncs{}
}
// AddEnvFuncs -
+// Deprecated: use CreateEnvFuncs instead
func AddEnvFuncs(f map[string]interface{}) {
for k, v := range CreateEnvFuncs(context.Background()) {
f[k] = v
@@ -28,10 +23,10 @@ func AddEnvFuncs(f map[string]interface{}) {
// CreateEnvFuncs -
func CreateEnvFuncs(ctx context.Context) map[string]interface{} {
- ns := EnvNS()
- ns.ctx = ctx
+ ns := &EnvFuncs{ctx}
+
return map[string]interface{}{
- "env": EnvNS,
+ "env": func() interface{} { return ns },
"getenv": ns.Getenv,
}
}
@@ -42,11 +37,11 @@ type EnvFuncs struct {
}
// Getenv -
-func (f *EnvFuncs) Getenv(key interface{}, def ...string) string {
+func (EnvFuncs) Getenv(key interface{}, def ...string) string {
return env.Getenv(conv.ToString(key), def...)
}
// ExpandEnv -
-func (f *EnvFuncs) ExpandEnv(s interface{}) string {
+func (EnvFuncs) ExpandEnv(s interface{}) string {
return env.ExpandEnv(conv.ToString(s))
}
diff --git a/funcs/env_test.go b/funcs/env_test.go
index 95db230b..6e47d469 100644
--- a/funcs/env_test.go
+++ b/funcs/env_test.go
@@ -1,12 +1,29 @@
package funcs
import (
+ "context"
"os"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreateEnvFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateEnvFuncs(ctx)
+ actual := fmap["env"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*EnvFuncs).ctx)
+ })
+ }
+}
+
func TestEnvGetenv(t *testing.T) {
ef := &EnvFuncs{}
expected := os.Getenv("USER")
diff --git a/funcs/file.go b/funcs/file.go
index 0f063b6e..c503bb04 100644
--- a/funcs/file.go
+++ b/funcs/file.go
@@ -3,25 +3,20 @@ package funcs
import (
"context"
"os"
- "sync"
"github.com/hairyhenderson/gomplate/v3/conv"
"github.com/hairyhenderson/gomplate/v3/file"
"github.com/spf13/afero"
)
-var (
- ff *FileFuncs
- ffInit sync.Once
-)
-
// FileNS - the File namespace
+// Deprecated: don't use
func FileNS() *FileFuncs {
- ffInit.Do(func() { ff = &FileFuncs{fs: afero.NewOsFs()} })
- return ff
+ return &FileFuncs{}
}
// AddFileFuncs -
+// Deprecated: use CreateFileFuncs instead
func AddFileFuncs(f map[string]interface{}) {
for k, v := range CreateFileFuncs(context.Background()) {
f[k] = v
@@ -30,9 +25,13 @@ func AddFileFuncs(f map[string]interface{}) {
// CreateFileFuncs -
func CreateFileFuncs(ctx context.Context) map[string]interface{} {
- ns := FileNS()
- ns.ctx = ctx
- return map[string]interface{}{"file": FileNS}
+ ns := &FileFuncs{
+ ctx: ctx,
+ fs: afero.NewOsFs(),
+ }
+ return map[string]interface{}{
+ "file": func() interface{} { return ns },
+ }
}
// FileFuncs -
diff --git a/funcs/file_test.go b/funcs/file_test.go
index 1202854c..66c08781 100644
--- a/funcs/file_test.go
+++ b/funcs/file_test.go
@@ -1,13 +1,30 @@
package funcs
import (
+ "context"
"path/filepath"
+ "strconv"
"testing"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
)
+func TestCreateFileFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateFileFuncs(ctx)
+ actual := fmap["file"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*FileFuncs).ctx)
+ })
+ }
+}
+
func TestFileExists(t *testing.T) {
fs := afero.NewMemMapFs()
ff := &FileFuncs{fs: fs}
diff --git a/funcs/filepath.go b/funcs/filepath.go
index a86c4742..9c9d59c2 100644
--- a/funcs/filepath.go
+++ b/funcs/filepath.go
@@ -3,23 +3,18 @@ package funcs
import (
"context"
"path/filepath"
- "sync"
"github.com/hairyhenderson/gomplate/v3/conv"
)
-var (
- fpf *FilePathFuncs
- fpfInit sync.Once
-)
-
// FilePathNS - the Path namespace
+// Deprecated: don't use
func FilePathNS() *FilePathFuncs {
- fpfInit.Do(func() { fpf = &FilePathFuncs{} })
- return fpf
+ return &FilePathFuncs{}
}
// AddFilePathFuncs -
+// Deprecated: use CreateFilePathFuncs instead
func AddFilePathFuncs(f map[string]interface{}) {
for k, v := range CreateFilePathFuncs(context.Background()) {
f[k] = v
@@ -28,9 +23,11 @@ func AddFilePathFuncs(f map[string]interface{}) {
// CreateFilePathFuncs -
func CreateFilePathFuncs(ctx context.Context) map[string]interface{} {
- ns := FilePathNS()
- ns.ctx = ctx
- return map[string]interface{}{"filepath": FilePathNS}
+ ns := &FilePathFuncs{ctx}
+
+ return map[string]interface{}{
+ "filepath": func() interface{} { return ns },
+ }
}
// FilePathFuncs -
diff --git a/funcs/filepath_test.go b/funcs/filepath_test.go
index 79b0a481..b2994cbd 100644
--- a/funcs/filepath_test.go
+++ b/funcs/filepath_test.go
@@ -1,30 +1,24 @@
-//+build !windows
-
package funcs
import (
+ "context"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
-func TestFilePathFuncs(t *testing.T) {
- f := FilePathNS()
- assert.Equal(t, "bar", f.Base("foo/bar"))
- assert.Equal(t, "bar", f.Base("/foo/bar"))
+func TestCreateFilePathFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateFilePathFuncs(ctx)
+ actual := fmap["filepath"].(func() interface{})
- assert.Equal(t, "/foo/baz", f.Clean("/foo/bar/../baz"))
- assert.Equal(t, "foo", f.Dir("foo/bar"))
- assert.Equal(t, ".txt", f.Ext("/foo/bar/baz.txt"))
- assert.False(t, f.IsAbs("foo/bar"))
- assert.True(t, f.IsAbs("/foo/bar"))
- assert.Equal(t, "foo/bar/qux", f.Join("foo", "bar", "baz", "..", "qux"))
- m, _ := f.Match("*.txt", "foo.json")
- assert.False(t, m)
- m, _ = f.Match("*.txt", "foo.txt")
- assert.True(t, m)
- r, _ := f.Rel("/foo/bar", "/foo/bar/baz")
- assert.Equal(t, "baz", r)
- assert.Equal(t, []string{"/foo/bar/", "baz"}, f.Split("/foo/bar/baz"))
- assert.Equal(t, "", f.VolumeName("/foo/bar"))
+ assert.Same(t, ctx, actual().(*FilePathFuncs).ctx)
+ })
+ }
}
diff --git a/funcs/filepath_unix_test.go b/funcs/filepath_unix_test.go
new file mode 100644
index 00000000..71266642
--- /dev/null
+++ b/funcs/filepath_unix_test.go
@@ -0,0 +1,30 @@
+//+build !windows
+
+package funcs
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestFilePathFuncs(t *testing.T) {
+ f := &FilePathFuncs{}
+ assert.Equal(t, "bar", f.Base("foo/bar"))
+ assert.Equal(t, "bar", f.Base("/foo/bar"))
+
+ assert.Equal(t, "/foo/baz", f.Clean("/foo/bar/../baz"))
+ assert.Equal(t, "foo", f.Dir("foo/bar"))
+ assert.Equal(t, ".txt", f.Ext("/foo/bar/baz.txt"))
+ assert.False(t, f.IsAbs("foo/bar"))
+ assert.True(t, f.IsAbs("/foo/bar"))
+ assert.Equal(t, "foo/bar/qux", f.Join("foo", "bar", "baz", "..", "qux"))
+ m, _ := f.Match("*.txt", "foo.json")
+ assert.False(t, m)
+ m, _ = f.Match("*.txt", "foo.txt")
+ assert.True(t, m)
+ r, _ := f.Rel("/foo/bar", "/foo/bar/baz")
+ assert.Equal(t, "baz", r)
+ assert.Equal(t, []string{"/foo/bar/", "baz"}, f.Split("/foo/bar/baz"))
+ assert.Equal(t, "", f.VolumeName("/foo/bar"))
+}
diff --git a/funcs/filepath_windows_test.go b/funcs/filepath_windows_test.go
index 26cba948..6bfc1cb0 100644
--- a/funcs/filepath_windows_test.go
+++ b/funcs/filepath_windows_test.go
@@ -9,7 +9,7 @@ import (
)
func TestFilePathFuncs(t *testing.T) {
- f := FilePathNS()
+ f := &FilePathFuncs{}
assert.Equal(t, "bar", f.Base(`foo\bar`))
assert.Equal(t, "bar", f.Base("C:/foo/bar"))
assert.Equal(t, "bar", f.Base(`C:\foo\bar`))
diff --git a/funcs/gcp.go b/funcs/gcp.go
index 8b479506..41c507b6 100644
--- a/funcs/gcp.go
+++ b/funcs/gcp.go
@@ -7,23 +7,14 @@ import (
"github.com/hairyhenderson/gomplate/v3/gcp"
)
-var (
- gcpf *GcpFuncs
- gcpfInit sync.Once
-)
-
// GCPNS - the gcp namespace
+// Deprecated: don't use
func GCPNS() *GcpFuncs {
- gcpfInit.Do(func() {
- gcpf = &GcpFuncs{
- gcpopts: gcp.GetClientOptions(),
- }
- })
- return gcpf
+ return &GcpFuncs{gcpopts: gcp.GetClientOptions()}
}
// AddGCPFuncs -
-// Deprecated: use CreateGCPFuncs
+// Deprecated: use CreateGCPFuncs instead
func AddGCPFuncs(f map[string]interface{}) {
for k, v := range CreateGCPFuncs(context.Background()) {
f[k] = v
@@ -32,11 +23,13 @@ func AddGCPFuncs(f map[string]interface{}) {
// CreateGCPFuncs -
func CreateGCPFuncs(ctx context.Context) map[string]interface{} {
- f := map[string]interface{}{}
- ns := GCPNS()
- ns.ctx = ctx
- f["gcp"] = GCPNS
- return f
+ ns := &GcpFuncs{
+ ctx: ctx,
+ gcpopts: gcp.GetClientOptions(),
+ }
+ return map[string]interface{}{
+ "gcp": func() interface{} { return ns },
+ }
}
// GcpFuncs -
diff --git a/funcs/gcp_test.go b/funcs/gcp_test.go
new file mode 100644
index 00000000..b79af6d7
--- /dev/null
+++ b/funcs/gcp_test.go
@@ -0,0 +1,24 @@
+package funcs
+
+import (
+ "context"
+ "strconv"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCreateGCPFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateGCPFuncs(ctx)
+ actual := fmap["gcp"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*GcpFuncs).ctx)
+ })
+ }
+}
diff --git a/funcs/math.go b/funcs/math.go
index b3ec70b8..890aa5b4 100644
--- a/funcs/math.go
+++ b/funcs/math.go
@@ -5,25 +5,20 @@ import (
"fmt"
gmath "math"
"strconv"
- "sync"
"github.com/hairyhenderson/gomplate/v3/conv"
"github.com/hairyhenderson/gomplate/v3/math"
)
-var (
- mathNS *MathFuncs
- mathNSInit sync.Once
-)
-
// MathNS - the math namespace
+// Deprecated: don't use
func MathNS() *MathFuncs {
- mathNSInit.Do(func() { mathNS = &MathFuncs{} })
- return mathNS
+ return &MathFuncs{}
}
// AddMathFuncs -
+// Deprecated: use CreateMathFuncs instead
func AddMathFuncs(f map[string]interface{}) {
for k, v := range CreateMathFuncs(context.Background()) {
f[k] = v
@@ -33,9 +28,9 @@ func AddMathFuncs(f map[string]interface{}) {
// CreateMathFuncs -
func CreateMathFuncs(ctx context.Context) map[string]interface{} {
f := map[string]interface{}{}
- ns := MathNS()
- ns.ctx = ctx
- f["math"] = MathNS
+
+ ns := &MathFuncs{ctx}
+ f["math"] = func() interface{} { return ns }
f["add"] = ns.Add
f["sub"] = ns.Sub
@@ -53,7 +48,7 @@ type MathFuncs struct {
}
// IsInt -
-func (f *MathFuncs) IsInt(n interface{}) bool {
+func (f MathFuncs) IsInt(n interface{}) bool {
switch i := n.(type) {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
return true
@@ -65,7 +60,7 @@ func (f *MathFuncs) IsInt(n interface{}) bool {
}
// IsFloat -
-func (f *MathFuncs) IsFloat(n interface{}) bool {
+func (f MathFuncs) IsFloat(n interface{}) bool {
switch i := n.(type) {
case float32, float64:
return true
@@ -82,7 +77,7 @@ func (f *MathFuncs) IsFloat(n interface{}) bool {
return false
}
-func (f *MathFuncs) containsFloat(n ...interface{}) bool {
+func (f MathFuncs) containsFloat(n ...interface{}) bool {
c := false
for _, v := range n {
if f.IsFloat(v) {
@@ -93,12 +88,12 @@ func (f *MathFuncs) containsFloat(n ...interface{}) bool {
}
// IsNum -
-func (f *MathFuncs) IsNum(n interface{}) bool {
+func (f MathFuncs) IsNum(n interface{}) bool {
return f.IsInt(n) || f.IsFloat(n)
}
// Abs -
-func (f *MathFuncs) Abs(n interface{}) interface{} {
+func (f MathFuncs) Abs(n interface{}) interface{} {
m := gmath.Abs(conv.ToFloat64(n))
if f.IsInt(n) {
return conv.ToInt64(m)
@@ -107,7 +102,7 @@ func (f *MathFuncs) Abs(n interface{}) interface{} {
}
// Add -
-func (f *MathFuncs) Add(n ...interface{}) interface{} {
+func (f MathFuncs) Add(n ...interface{}) interface{} {
if f.containsFloat(n...) {
nums := conv.ToFloat64s(n...)
var x float64
@@ -125,7 +120,7 @@ func (f *MathFuncs) Add(n ...interface{}) interface{} {
}
// Mul -
-func (f *MathFuncs) Mul(n ...interface{}) interface{} {
+func (f MathFuncs) Mul(n ...interface{}) interface{} {
if f.containsFloat(n...) {
nums := conv.ToFloat64s(n...)
x := 1.
@@ -143,7 +138,7 @@ func (f *MathFuncs) Mul(n ...interface{}) interface{} {
}
// Sub -
-func (f *MathFuncs) Sub(a, b interface{}) interface{} {
+func (f MathFuncs) Sub(a, b interface{}) interface{} {
if f.containsFloat(a, b) {
return conv.ToFloat64(a) - conv.ToFloat64(b)
}
@@ -151,7 +146,7 @@ func (f *MathFuncs) Sub(a, b interface{}) interface{} {
}
// Div -
-func (f *MathFuncs) Div(a, b interface{}) (interface{}, error) {
+func (f MathFuncs) Div(a, b interface{}) (interface{}, error) {
divisor := conv.ToFloat64(a)
dividend := conv.ToFloat64(b)
if dividend == 0 {
@@ -161,12 +156,12 @@ func (f *MathFuncs) Div(a, b interface{}) (interface{}, error) {
}
// Rem -
-func (f *MathFuncs) Rem(a, b interface{}) interface{} {
+func (f MathFuncs) Rem(a, b interface{}) interface{} {
return conv.ToInt64(a) % conv.ToInt64(b)
}
// Pow -
-func (f *MathFuncs) Pow(a, b interface{}) interface{} {
+func (f MathFuncs) Pow(a, b interface{}) interface{} {
r := gmath.Pow(conv.ToFloat64(a), conv.ToFloat64(b))
if f.IsFloat(a) {
return r
@@ -176,7 +171,7 @@ func (f *MathFuncs) Pow(a, b interface{}) interface{} {
// Seq - return a sequence from `start` to `end`, in steps of `step`
// start and step are optional, and default to 1.
-func (f *MathFuncs) Seq(n ...interface{}) ([]int64, error) {
+func (f MathFuncs) Seq(n ...interface{}) ([]int64, error) {
start := int64(1)
end := int64(0)
step := int64(1)
@@ -199,7 +194,7 @@ func (f *MathFuncs) Seq(n ...interface{}) ([]int64, error) {
}
// Max -
-func (f *MathFuncs) Max(a interface{}, b ...interface{}) (interface{}, error) {
+func (f MathFuncs) Max(a interface{}, b ...interface{}) (interface{}, error) {
if f.IsFloat(a) || f.containsFloat(b...) {
m := conv.ToFloat64(a)
for _, n := range conv.ToFloat64s(b...) {
@@ -217,7 +212,7 @@ func (f *MathFuncs) Max(a interface{}, b ...interface{}) (interface{}, error) {
}
// Min -
-func (f *MathFuncs) Min(a interface{}, b ...interface{}) (interface{}, error) {
+func (f MathFuncs) Min(a interface{}, b ...interface{}) (interface{}, error) {
if f.IsFloat(a) || f.containsFloat(b...) {
m := conv.ToFloat64(a)
for _, n := range conv.ToFloat64s(b...) {
@@ -235,16 +230,16 @@ func (f *MathFuncs) Min(a interface{}, b ...interface{}) (interface{}, error) {
}
// Ceil -
-func (f *MathFuncs) Ceil(n interface{}) interface{} {
+func (f MathFuncs) Ceil(n interface{}) interface{} {
return gmath.Ceil(conv.ToFloat64(n))
}
// Floor -
-func (f *MathFuncs) Floor(n interface{}) interface{} {
+func (f MathFuncs) Floor(n interface{}) interface{} {
return gmath.Floor(conv.ToFloat64(n))
}
// Round -
-func (f *MathFuncs) Round(n interface{}) interface{} {
+func (f MathFuncs) Round(n interface{}) interface{} {
return gmath.Round(conv.ToFloat64(n))
}
diff --git a/funcs/math_test.go b/funcs/math_test.go
index 7643ac79..ca0d6aff 100644
--- a/funcs/math_test.go
+++ b/funcs/math_test.go
@@ -1,15 +1,32 @@
package funcs
import (
+ "context"
"fmt"
gmath "math"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreateMathFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateMathFuncs(ctx)
+ actual := fmap["math"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*MathFuncs).ctx)
+ })
+ }
+}
+
func TestAdd(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
assert.Equal(t, int64(12), m.Add(1, 1, 2, 3, 5))
assert.Equal(t, int64(2), m.Add(1, 1))
assert.Equal(t, int64(1), m.Add(1))
@@ -18,7 +35,7 @@ func TestAdd(t *testing.T) {
}
func TestMul(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
assert.Equal(t, int64(30), m.Mul(1, 1, 2, 3, 5))
assert.Equal(t, int64(1), m.Mul(1, 1))
assert.Equal(t, int64(1), m.Mul(1))
@@ -28,7 +45,7 @@ func TestMul(t *testing.T) {
}
func TestSub(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
assert.Equal(t, int64(0), m.Sub(1, 1))
assert.Equal(t, int64(-10), m.Sub(-5, 5))
assert.Equal(t, int64(-41), m.Sub(true, "42"))
@@ -36,7 +53,7 @@ func TestSub(t *testing.T) {
}
func mustDiv(a, b interface{}) interface{} {
- m := MathNS()
+ m := MathFuncs{}
r, err := m.Div(a, b)
if err != nil {
return -1
@@ -45,7 +62,7 @@ func mustDiv(a, b interface{}) interface{} {
}
func TestDiv(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
_, err := m.Div(1, 0)
assert.Error(t, err)
assert.Equal(t, 1., mustDiv(1, 1))
@@ -55,19 +72,19 @@ func TestDiv(t *testing.T) {
}
func TestRem(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
assert.Equal(t, int64(0), m.Rem(1, 1))
assert.Equal(t, int64(2), m.Rem(5, 3.0))
}
func TestPow(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
assert.Equal(t, int64(4), m.Pow(2, "2"))
assert.Equal(t, 2.25, m.Pow(1.5, 2))
}
func mustSeq(n ...interface{}) []int64 {
- m := MathNS()
+ m := MathFuncs{}
s, err := m.Seq(n...)
if err != nil {
panic(err)
@@ -75,7 +92,7 @@ func mustSeq(n ...interface{}) []int64 {
return s
}
func TestSeq(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
assert.EqualValues(t, []int64{0, 1, 2, 3}, mustSeq(0, 3))
assert.EqualValues(t, []int64{1, 0}, mustSeq(0))
assert.EqualValues(t, []int64{0, 2, 4}, mustSeq(0, 4, 2))
@@ -124,7 +141,7 @@ func TestIsIntFloatNum(t *testing.T) {
{nil, false, false},
{true, false, false},
}
- m := MathNS()
+ m := MathFuncs{}
for _, tt := range tests {
tt := tt
t.Run(fmt.Sprintf("%T(%#v)", tt.in, tt.in), func(t *testing.T) {
@@ -139,7 +156,7 @@ func BenchmarkIsFloat(b *testing.B) {
data := []interface{}{
0, 1, -1, uint(42), uint8(255), uint16(42), uint32(42), uint64(42), int(42), int8(127), int16(42), int32(42), int64(42), float32(18.3), float64(18.3), 1.5, -18.6, "42", "052", "0xff", "-42", "-0", "3.14", "-3.14", "0.00", "NaN", "-Inf", "+Inf", "", "foo", nil, true,
}
- m := MathNS()
+ m := MathFuncs{}
for _, n := range data {
n := n
b.Run(fmt.Sprintf("%T(%v)", n, n), func(b *testing.B) {
@@ -151,7 +168,7 @@ func BenchmarkIsFloat(b *testing.B) {
}
func TestMax(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
data := []struct {
expected interface{}
n []interface{}
@@ -180,7 +197,7 @@ func TestMax(t *testing.T) {
}
func TestMin(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
data := []struct {
expected interface{}
n []interface{}
@@ -209,7 +226,7 @@ func TestMin(t *testing.T) {
}
func TestContainsFloat(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
data := []struct {
n []interface{}
expected bool
@@ -239,7 +256,7 @@ func TestContainsFloat(t *testing.T) {
}
func TestCeil(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
data := []struct {
n interface{}
a float64
@@ -261,7 +278,7 @@ func TestCeil(t *testing.T) {
}
func TestFloor(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
data := []struct {
n interface{}
a float64
@@ -283,7 +300,7 @@ func TestFloor(t *testing.T) {
}
func TestRound(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
data := []struct {
n interface{}
a float64
@@ -309,7 +326,7 @@ func TestRound(t *testing.T) {
}
func TestAbs(t *testing.T) {
- m := MathNS()
+ m := MathFuncs{}
data := []struct {
n interface{}
a interface{}
diff --git a/funcs/net.go b/funcs/net.go
index 922289b7..73481fe2 100644
--- a/funcs/net.go
+++ b/funcs/net.go
@@ -3,34 +3,32 @@ package funcs
import (
"context"
stdnet "net"
- "sync"
"github.com/hairyhenderson/gomplate/v3/conv"
"github.com/hairyhenderson/gomplate/v3/net"
)
-var (
- netNS *NetFuncs
- netNSInit sync.Once
-)
-
// NetNS - the net namespace
+// Deprecated: don't use
func NetNS() *NetFuncs {
- netNSInit.Do(func() { netNS = &NetFuncs{} })
- return netNS
+ return &NetFuncs{}
}
// AddNetFuncs -
+// Deprecated: use CreateNetFuncs instead
func AddNetFuncs(f map[string]interface{}) {
- f["net"] = NetNS
+ for k, v := range CreateNetFuncs(context.Background()) {
+ f[k] = v
+ }
}
// CreateNetFuncs -
func CreateNetFuncs(ctx context.Context) map[string]interface{} {
- ns := NetNS()
- ns.ctx = ctx
- return map[string]interface{}{"net": NetNS}
+ ns := &NetFuncs{ctx}
+ return map[string]interface{}{
+ "net": func() interface{} { return ns },
+ }
}
// NetFuncs -
@@ -39,31 +37,31 @@ type NetFuncs struct {
}
// LookupIP -
-func (f *NetFuncs) LookupIP(name interface{}) (string, error) {
+func (f NetFuncs) LookupIP(name interface{}) (string, error) {
return net.LookupIP(conv.ToString(name))
}
// LookupIPs -
-func (f *NetFuncs) LookupIPs(name interface{}) ([]string, error) {
+func (f NetFuncs) LookupIPs(name interface{}) ([]string, error) {
return net.LookupIPs(conv.ToString(name))
}
// LookupCNAME -
-func (f *NetFuncs) LookupCNAME(name interface{}) (string, error) {
+func (f NetFuncs) LookupCNAME(name interface{}) (string, error) {
return net.LookupCNAME(conv.ToString(name))
}
// LookupSRV -
-func (f *NetFuncs) LookupSRV(name interface{}) (*stdnet.SRV, error) {
+func (f NetFuncs) LookupSRV(name interface{}) (*stdnet.SRV, error) {
return net.LookupSRV(conv.ToString(name))
}
// LookupSRVs -
-func (f *NetFuncs) LookupSRVs(name interface{}) ([]*stdnet.SRV, error) {
+func (f NetFuncs) LookupSRVs(name interface{}) ([]*stdnet.SRV, error) {
return net.LookupSRVs(conv.ToString(name))
}
// LookupTXT -
-func (f *NetFuncs) LookupTXT(name interface{}) ([]string, error) {
+func (f NetFuncs) LookupTXT(name interface{}) ([]string, error) {
return net.LookupTXT(conv.ToString(name))
}
diff --git a/funcs/net_test.go b/funcs/net_test.go
index 2a4eb951..54f032ae 100644
--- a/funcs/net_test.go
+++ b/funcs/net_test.go
@@ -1,12 +1,29 @@
package funcs
import (
+ "context"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreateNetFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateNetFuncs(ctx)
+ actual := fmap["net"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*NetFuncs).ctx)
+ })
+ }
+}
+
func TestNetLookupIP(t *testing.T) {
- n := &NetFuncs{}
+ n := NetFuncs{}
assert.Equal(t, "127.0.0.1", must(n.LookupIP("localhost")))
}
diff --git a/funcs/path.go b/funcs/path.go
index 419114cf..556eb7e6 100644
--- a/funcs/path.go
+++ b/funcs/path.go
@@ -3,23 +3,18 @@ package funcs
import (
"context"
"path"
- "sync"
"github.com/hairyhenderson/gomplate/v3/conv"
)
-var (
- pf *PathFuncs
- pfInit sync.Once
-)
-
// PathNS - the Path namespace
+// Deprecated: don't use
func PathNS() *PathFuncs {
- pfInit.Do(func() { pf = &PathFuncs{} })
- return pf
+ return &PathFuncs{}
}
// AddPathFuncs -
+// Deprecated: use CreatePathFuncs instead
func AddPathFuncs(f map[string]interface{}) {
for k, v := range CreatePathFuncs(context.Background()) {
f[k] = v
@@ -28,9 +23,10 @@ func AddPathFuncs(f map[string]interface{}) {
// CreatePathFuncs -
func CreatePathFuncs(ctx context.Context) map[string]interface{} {
- ns := PathNS()
- ns.ctx = ctx
- return map[string]interface{}{"path": PathNS}
+ ns := &PathFuncs{ctx}
+ return map[string]interface{}{
+ "path": func() interface{} { return ns },
+ }
}
// PathFuncs -
@@ -39,43 +35,43 @@ type PathFuncs struct {
}
// Base -
-func (f *PathFuncs) Base(in interface{}) string {
+func (PathFuncs) Base(in interface{}) string {
return path.Base(conv.ToString(in))
}
// Clean -
-func (f *PathFuncs) Clean(in interface{}) string {
+func (PathFuncs) Clean(in interface{}) string {
return path.Clean(conv.ToString(in))
}
// Dir -
-func (f *PathFuncs) Dir(in interface{}) string {
+func (PathFuncs) Dir(in interface{}) string {
return path.Dir(conv.ToString(in))
}
// Ext -
-func (f *PathFuncs) Ext(in interface{}) string {
+func (PathFuncs) Ext(in interface{}) string {
return path.Ext(conv.ToString(in))
}
// IsAbs -
-func (f *PathFuncs) IsAbs(in interface{}) bool {
+func (PathFuncs) IsAbs(in interface{}) bool {
return path.IsAbs(conv.ToString(in))
}
// Join -
-func (f *PathFuncs) Join(elem ...interface{}) string {
+func (PathFuncs) Join(elem ...interface{}) string {
s := conv.ToStrings(elem...)
return path.Join(s...)
}
// Match -
-func (f *PathFuncs) Match(pattern, name interface{}) (matched bool, err error) {
+func (PathFuncs) Match(pattern, name interface{}) (matched bool, err error) {
return path.Match(conv.ToString(pattern), conv.ToString(name))
}
// Split -
-func (f *PathFuncs) Split(in interface{}) []string {
+func (PathFuncs) Split(in interface{}) []string {
dir, file := path.Split(conv.ToString(in))
return []string{dir, file}
}
diff --git a/funcs/path_test.go b/funcs/path_test.go
index 0ccfc648..62fec664 100644
--- a/funcs/path_test.go
+++ b/funcs/path_test.go
@@ -1,13 +1,30 @@
package funcs
import (
+ "context"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreatePathFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreatePathFuncs(ctx)
+ actual := fmap["path"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*PathFuncs).ctx)
+ })
+ }
+}
+
func TestPathFuncs(t *testing.T) {
- p := PathNS()
+ p := PathFuncs{}
assert.Equal(t, "bar", p.Base("foo/bar"))
assert.Equal(t, "bar", p.Base("/foo/bar"))
diff --git a/funcs/random.go b/funcs/random.go
index 51d0ce7e..6c571bf2 100644
--- a/funcs/random.go
+++ b/funcs/random.go
@@ -3,7 +3,6 @@ package funcs
import (
"context"
"strconv"
- "sync"
"unicode/utf8"
"github.com/hairyhenderson/gomplate/v3/conv"
@@ -12,18 +11,14 @@ import (
"github.com/pkg/errors"
)
-var (
- randomNS *RandomFuncs
- randomNSInit sync.Once
-)
-
// RandomNS -
+// Deprecated: don't use
func RandomNS() *RandomFuncs {
- randomNSInit.Do(func() { randomNS = &RandomFuncs{} })
- return randomNS
+ return &RandomFuncs{}
}
// AddRandomFuncs -
+// Deprecated: use CreateRandomFuncs instead
func AddRandomFuncs(f map[string]interface{}) {
for k, v := range CreateRandomFuncs(context.Background()) {
f[k] = v
@@ -32,9 +27,10 @@ func AddRandomFuncs(f map[string]interface{}) {
// CreateRandomFuncs -
func CreateRandomFuncs(ctx context.Context) map[string]interface{} {
- ns := RandomNS()
- ns.ctx = ctx
- return map[string]interface{}{"random": RandomNS}
+ ns := &RandomFuncs{ctx}
+ return map[string]interface{}{
+ "random": func() interface{} { return ns },
+ }
}
// RandomFuncs -
@@ -43,22 +39,22 @@ type RandomFuncs struct {
}
// ASCII -
-func (f *RandomFuncs) ASCII(count interface{}) (string, error) {
+func (RandomFuncs) ASCII(count interface{}) (string, error) {
return random.StringBounds(conv.ToInt(count), ' ', '~')
}
// Alpha -
-func (f *RandomFuncs) Alpha(count interface{}) (string, error) {
+func (RandomFuncs) Alpha(count interface{}) (string, error) {
return random.StringRE(conv.ToInt(count), "[[:alpha:]]")
}
// AlphaNum -
-func (f *RandomFuncs) AlphaNum(count interface{}) (string, error) {
+func (RandomFuncs) AlphaNum(count interface{}) (string, error) {
return random.StringRE(conv.ToInt(count), "[[:alnum:]]")
}
// String -
-func (f *RandomFuncs) String(count interface{}, args ...interface{}) (s string, err error) {
+func (RandomFuncs) String(count interface{}, args ...interface{}) (s string, err error) {
c := conv.ToInt(count)
if c == 0 {
return "", errors.New("count must be greater than 0")
@@ -120,7 +116,7 @@ func toCodePoints(l, u string) (rune, rune, error) {
}
// Item -
-func (f *RandomFuncs) Item(items interface{}) (interface{}, error) {
+func (RandomFuncs) Item(items interface{}) (interface{}, error) {
i, err := iconv.InterfaceSlice(items)
if err != nil {
return nil, err
@@ -129,7 +125,7 @@ func (f *RandomFuncs) Item(items interface{}) (interface{}, error) {
}
// Number -
-func (f *RandomFuncs) Number(args ...interface{}) (int64, error) {
+func (RandomFuncs) Number(args ...interface{}) (int64, error) {
var min, max int64
min, max = 0, 100
switch len(args) {
@@ -144,7 +140,7 @@ func (f *RandomFuncs) Number(args ...interface{}) (int64, error) {
}
// Float -
-func (f *RandomFuncs) Float(args ...interface{}) (float64, error) {
+func (RandomFuncs) Float(args ...interface{}) (float64, error) {
var min, max float64
min, max = 0, 1.0
switch len(args) {
diff --git a/funcs/random_test.go b/funcs/random_test.go
index 6466c11a..f2a3ac2d 100644
--- a/funcs/random_test.go
+++ b/funcs/random_test.go
@@ -1,14 +1,31 @@
package funcs
import (
+ "context"
+ "strconv"
"testing"
"unicode/utf8"
"github.com/stretchr/testify/assert"
)
+func TestCreateRandomFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateRandomFuncs(ctx)
+ actual := fmap["random"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*RandomFuncs).ctx)
+ })
+ }
+}
+
func TestASCII(t *testing.T) {
- f := &RandomFuncs{}
+ f := RandomFuncs{}
s, err := f.ASCII(0)
assert.NoError(t, err)
assert.Empty(t, s)
@@ -20,7 +37,11 @@ func TestASCII(t *testing.T) {
}
func TestAlpha(t *testing.T) {
- f := &RandomFuncs{}
+ if testing.Short() {
+ t.Skip("skipping slow test")
+ }
+
+ f := RandomFuncs{}
s, err := f.Alpha(0)
assert.NoError(t, err)
assert.Empty(t, s)
@@ -32,7 +53,11 @@ func TestAlpha(t *testing.T) {
}
func TestAlphaNum(t *testing.T) {
- f := &RandomFuncs{}
+ if testing.Short() {
+ t.Skip("skipping slow test")
+ }
+
+ f := RandomFuncs{}
s, err := f.AlphaNum(0)
assert.NoError(t, err)
assert.Empty(t, s)
@@ -72,7 +97,11 @@ func TestToCodePoints(t *testing.T) {
}
func TestString(t *testing.T) {
- f := &RandomFuncs{}
+ if testing.Short() {
+ t.Skip("skipping slow test")
+ }
+
+ f := RandomFuncs{}
out, err := f.String(1)
assert.NoError(t, err)
assert.Len(t, out, 1)
@@ -111,7 +140,7 @@ func TestString(t *testing.T) {
}
func TestItem(t *testing.T) {
- f := &RandomFuncs{}
+ f := RandomFuncs{}
_, err := f.Item(nil)
assert.Error(t, err)
@@ -134,7 +163,7 @@ func TestItem(t *testing.T) {
}
func TestNumber(t *testing.T) {
- f := &RandomFuncs{}
+ f := RandomFuncs{}
n, err := f.Number()
assert.NoError(t, err)
assert.True(t, 0 <= n && n <= 100, n)
@@ -156,7 +185,7 @@ func TestNumber(t *testing.T) {
}
func TestFloat(t *testing.T) {
- f := &RandomFuncs{}
+ f := RandomFuncs{}
n, err := f.Float()
assert.NoError(t, err)
assert.InDelta(t, 0.5, n, 0.5)
diff --git a/funcs/regexp.go b/funcs/regexp.go
index 5aa1bdfb..6fb4e690 100644
--- a/funcs/regexp.go
+++ b/funcs/regexp.go
@@ -2,7 +2,6 @@ package funcs
import (
"context"
- "sync"
"github.com/pkg/errors"
@@ -10,18 +9,14 @@ import (
"github.com/hairyhenderson/gomplate/v3/regexp"
)
-var (
- reNS *ReFuncs
- reNSInit sync.Once
-)
-
// ReNS -
+// Deprecated: don't use
func ReNS() *ReFuncs {
- reNSInit.Do(func() { reNS = &ReFuncs{} })
- return reNS
+ return &ReFuncs{}
}
// AddReFuncs -
+// Deprecated: use CreateReFuncs instead
func AddReFuncs(f map[string]interface{}) {
for k, v := range CreateReFuncs(context.Background()) {
f[k] = v
@@ -30,9 +25,10 @@ func AddReFuncs(f map[string]interface{}) {
// CreateReFuncs -
func CreateReFuncs(ctx context.Context) map[string]interface{} {
- ns := ReNS()
- ns.ctx = ctx
- return map[string]interface{}{"regexp": ReNS}
+ ns := &ReFuncs{ctx}
+ return map[string]interface{}{
+ "regexp": func() interface{} { return ns },
+ }
}
// ReFuncs -
@@ -41,12 +37,12 @@ type ReFuncs struct {
}
// Find -
-func (f *ReFuncs) Find(re, input interface{}) (string, error) {
+func (ReFuncs) Find(re, input interface{}) (string, error) {
return regexp.Find(conv.ToString(re), conv.ToString(input))
}
// FindAll -
-func (f *ReFuncs) FindAll(args ...interface{}) ([]string, error) {
+func (ReFuncs) FindAll(args ...interface{}) ([]string, error) {
re := ""
n := 0
input := ""
@@ -66,31 +62,31 @@ func (f *ReFuncs) FindAll(args ...interface{}) ([]string, error) {
}
// Match -
-func (f *ReFuncs) Match(re, input interface{}) bool {
+func (ReFuncs) Match(re, input interface{}) bool {
return regexp.Match(conv.ToString(re), conv.ToString(input))
}
// QuoteMeta -
-func (f *ReFuncs) QuoteMeta(in interface{}) string {
+func (ReFuncs) QuoteMeta(in interface{}) string {
return regexp.QuoteMeta(conv.ToString(in))
}
// Replace -
-func (f *ReFuncs) Replace(re, replacement, input interface{}) string {
+func (ReFuncs) Replace(re, replacement, input interface{}) string {
return regexp.Replace(conv.ToString(re),
conv.ToString(replacement),
conv.ToString(input))
}
// ReplaceLiteral -
-func (f *ReFuncs) ReplaceLiteral(re, replacement, input interface{}) (string, error) {
+func (ReFuncs) ReplaceLiteral(re, replacement, input interface{}) (string, error) {
return regexp.ReplaceLiteral(conv.ToString(re),
conv.ToString(replacement),
conv.ToString(input))
}
// Split -
-func (f *ReFuncs) Split(args ...interface{}) ([]string, error) {
+func (ReFuncs) Split(args ...interface{}) ([]string, error) {
re := ""
n := -1
input := ""
diff --git a/funcs/regexp_test.go b/funcs/regexp_test.go
index 905c4146..869cbc9f 100644
--- a/funcs/regexp_test.go
+++ b/funcs/regexp_test.go
@@ -1,11 +1,28 @@
package funcs
import (
+ "context"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreateReFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateReFuncs(ctx)
+ actual := fmap["regexp"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*ReFuncs).ctx)
+ })
+ }
+}
+
func TestReplace(t *testing.T) {
re := &ReFuncs{}
assert.Equal(t, "hello world", re.Replace("i", "ello", "hi world"))
diff --git a/funcs/sockaddr.go b/funcs/sockaddr.go
index f1bb4561..c4554a77 100644
--- a/funcs/sockaddr.go
+++ b/funcs/sockaddr.go
@@ -2,33 +2,29 @@ package funcs
import (
"context"
- "sync"
"github.com/hashicorp/go-sockaddr"
"github.com/hashicorp/go-sockaddr/template"
)
-var (
- sockaddrNS *SockaddrFuncs
- sockaddrNSInit sync.Once
-)
-
// SockaddrNS - the sockaddr namespace
+// Deprecated: don't use
func SockaddrNS() *SockaddrFuncs {
- sockaddrNSInit.Do(func() { sockaddrNS = &SockaddrFuncs{} })
- return sockaddrNS
+ return &SockaddrFuncs{}
}
// AddSockaddrFuncs -
+// Deprecated: use CreateSockaddrFuncs instead
func AddSockaddrFuncs(f map[string]interface{}) {
f["sockaddr"] = SockaddrNS
}
// CreateSockaddrFuncs -
func CreateSockaddrFuncs(ctx context.Context) map[string]interface{} {
- ns := SockaddrNS()
- ns.ctx = ctx
- return map[string]interface{}{"sockaddr": SockaddrNS}
+ ns := &SockaddrFuncs{ctx}
+ return map[string]interface{}{
+ "sockaddr": func() interface{} { return ns },
+ }
}
// SockaddrFuncs -
@@ -37,96 +33,96 @@ type SockaddrFuncs struct {
}
// GetAllInterfaces -
-func (f *SockaddrFuncs) GetAllInterfaces() (sockaddr.IfAddrs, error) {
+func (SockaddrFuncs) GetAllInterfaces() (sockaddr.IfAddrs, error) {
return sockaddr.GetAllInterfaces()
}
// GetDefaultInterfaces -
-func (f *SockaddrFuncs) GetDefaultInterfaces() (sockaddr.IfAddrs, error) {
+func (SockaddrFuncs) GetDefaultInterfaces() (sockaddr.IfAddrs, error) {
return sockaddr.GetDefaultInterfaces()
}
// GetPrivateInterfaces -
-func (f *SockaddrFuncs) GetPrivateInterfaces() (sockaddr.IfAddrs, error) {
+func (SockaddrFuncs) GetPrivateInterfaces() (sockaddr.IfAddrs, error) {
return sockaddr.GetPrivateInterfaces()
}
// GetPublicInterfaces -
-func (f *SockaddrFuncs) GetPublicInterfaces() (sockaddr.IfAddrs, error) {
+func (SockaddrFuncs) GetPublicInterfaces() (sockaddr.IfAddrs, error) {
return sockaddr.GetPublicInterfaces()
}
// Sort -
-func (f *SockaddrFuncs) Sort(selectorParam string, inputIfAddrs sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
+func (SockaddrFuncs) Sort(selectorParam string, inputIfAddrs sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
return sockaddr.SortIfBy(selectorParam, inputIfAddrs)
}
// Exclude -
-func (f *SockaddrFuncs) Exclude(selectorName, selectorParam string, inputIfAddrs sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
+func (SockaddrFuncs) Exclude(selectorName, selectorParam string, inputIfAddrs sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
return sockaddr.ExcludeIfs(selectorName, selectorParam, inputIfAddrs)
}
// Include -
-func (f *SockaddrFuncs) Include(selectorName, selectorParam string, inputIfAddrs sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
+func (SockaddrFuncs) Include(selectorName, selectorParam string, inputIfAddrs sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
return sockaddr.IncludeIfs(selectorName, selectorParam, inputIfAddrs)
}
// Attr -
-func (f *SockaddrFuncs) Attr(selectorName string, ifAddrsRaw interface{}) (string, error) {
+func (SockaddrFuncs) Attr(selectorName string, ifAddrsRaw interface{}) (string, error) {
return template.Attr(selectorName, ifAddrsRaw)
}
// Join -
-func (f *SockaddrFuncs) Join(selectorName, joinString string, inputIfAddrs sockaddr.IfAddrs) (string, error) {
+func (SockaddrFuncs) Join(selectorName, joinString string, inputIfAddrs sockaddr.IfAddrs) (string, error) {
return sockaddr.JoinIfAddrs(selectorName, joinString, inputIfAddrs)
}
// Limit -
-func (f *SockaddrFuncs) Limit(lim uint, in sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
+func (SockaddrFuncs) Limit(lim uint, in sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
return sockaddr.LimitIfAddrs(lim, in)
}
// Offset -
-func (f *SockaddrFuncs) Offset(off int, in sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
+func (SockaddrFuncs) Offset(off int, in sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
return sockaddr.OffsetIfAddrs(off, in)
}
// Unique -
-func (f *SockaddrFuncs) Unique(selectorName string, inputIfAddrs sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
+func (SockaddrFuncs) Unique(selectorName string, inputIfAddrs sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
return sockaddr.UniqueIfAddrsBy(selectorName, inputIfAddrs)
}
// Math -
-func (f *SockaddrFuncs) Math(operation, value string, inputIfAddrs sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
+func (SockaddrFuncs) Math(operation, value string, inputIfAddrs sockaddr.IfAddrs) (sockaddr.IfAddrs, error) {
return sockaddr.IfAddrsMath(operation, value, inputIfAddrs)
}
// GetPrivateIP -
-func (f *SockaddrFuncs) GetPrivateIP() (string, error) {
+func (SockaddrFuncs) GetPrivateIP() (string, error) {
return sockaddr.GetPrivateIP()
}
// GetPrivateIPs -
-func (f *SockaddrFuncs) GetPrivateIPs() (string, error) {
+func (SockaddrFuncs) GetPrivateIPs() (string, error) {
return sockaddr.GetPrivateIPs()
}
// GetPublicIP -
-func (f *SockaddrFuncs) GetPublicIP() (string, error) {
+func (SockaddrFuncs) GetPublicIP() (string, error) {
return sockaddr.GetPublicIP()
}
// GetPublicIPs -
-func (f *SockaddrFuncs) GetPublicIPs() (string, error) {
+func (SockaddrFuncs) GetPublicIPs() (string, error) {
return sockaddr.GetPublicIPs()
}
// GetInterfaceIP -
-func (f *SockaddrFuncs) GetInterfaceIP(namedIfRE string) (string, error) {
+func (SockaddrFuncs) GetInterfaceIP(namedIfRE string) (string, error) {
return sockaddr.GetInterfaceIP(namedIfRE)
}
// GetInterfaceIPs -
-func (f *SockaddrFuncs) GetInterfaceIPs(namedIfRE string) (string, error) {
+func (SockaddrFuncs) GetInterfaceIPs(namedIfRE string) (string, error) {
return sockaddr.GetInterfaceIPs(namedIfRE)
}
diff --git a/funcs/sockaddr_test.go b/funcs/sockaddr_test.go
new file mode 100644
index 00000000..b50228f7
--- /dev/null
+++ b/funcs/sockaddr_test.go
@@ -0,0 +1,24 @@
+package funcs
+
+import (
+ "context"
+ "strconv"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCreateSockaddrFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateSockaddrFuncs(ctx)
+ actual := fmap["sockaddr"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*SockaddrFuncs).ctx)
+ })
+ }
+}
diff --git a/funcs/strings.go b/funcs/strings.go
index 7a18c96e..2e0582c2 100644
--- a/funcs/strings.go
+++ b/funcs/strings.go
@@ -9,7 +9,6 @@ import (
"context"
"fmt"
"reflect"
- "sync"
"unicode/utf8"
"github.com/Masterminds/goutils"
@@ -22,18 +21,14 @@ import (
gompstrings "github.com/hairyhenderson/gomplate/v3/strings"
)
-var (
- strNS *StringFuncs
- strNSInit sync.Once
-)
-
// StrNS -
+// Deprecated: don't use
func StrNS() *StringFuncs {
- strNSInit.Do(func() { strNS = &StringFuncs{} })
- return strNS
+ return &StringFuncs{}
}
// AddStringFuncs -
+// Deprecated: use CreateStringFuncs instead
func AddStringFuncs(f map[string]interface{}) {
for k, v := range CreateStringFuncs(context.Background()) {
f[k] = v
@@ -43,10 +38,9 @@ func AddStringFuncs(f map[string]interface{}) {
// CreateStringFuncs -
func CreateStringFuncs(ctx context.Context) map[string]interface{} {
f := map[string]interface{}{}
- ns := StrNS()
- ns.ctx = ctx
- f["strings"] = StrNS
+ ns := &StringFuncs{ctx}
+ f["strings"] = func() interface{} { return ns }
f["replaceAll"] = ns.ReplaceAll
f["title"] = ns.Title
@@ -75,7 +69,7 @@ type StringFuncs struct {
}
// Abbrev -
-func (f *StringFuncs) Abbrev(args ...interface{}) (string, error) {
+func (StringFuncs) Abbrev(args ...interface{}) (string, error) {
str := ""
offset := 0
maxWidth := 0
@@ -98,27 +92,27 @@ func (f *StringFuncs) Abbrev(args ...interface{}) (string, error) {
}
// ReplaceAll -
-func (f *StringFuncs) ReplaceAll(old, new string, s interface{}) string {
+func (StringFuncs) ReplaceAll(old, new string, s interface{}) string {
return strings.ReplaceAll(conv.ToString(s), old, new)
}
// Contains -
-func (f *StringFuncs) Contains(substr string, s interface{}) bool {
+func (StringFuncs) Contains(substr string, s interface{}) bool {
return strings.Contains(conv.ToString(s), substr)
}
// HasPrefix -
-func (f *StringFuncs) HasPrefix(prefix string, s interface{}) bool {
+func (StringFuncs) HasPrefix(prefix string, s interface{}) bool {
return strings.HasPrefix(conv.ToString(s), prefix)
}
// HasSuffix -
-func (f *StringFuncs) HasSuffix(suffix string, s interface{}) bool {
+func (StringFuncs) HasSuffix(suffix string, s interface{}) bool {
return strings.HasSuffix(conv.ToString(s), suffix)
}
// Repeat -
-func (f *StringFuncs) Repeat(count int, s interface{}) (string, error) {
+func (StringFuncs) Repeat(count int, s interface{}) (string, error) {
if count < 0 {
return "", errors.Errorf("negative count %d", count)
}
@@ -132,7 +126,7 @@ func (f *StringFuncs) Repeat(count int, s interface{}) (string, error) {
// Sort -
//
// Deprecated: use coll.Sort instead
-func (f *StringFuncs) Sort(list interface{}) ([]string, error) {
+func (StringFuncs) Sort(list interface{}) ([]string, error) {
switch v := list.(type) {
case []string:
return gompstrings.Sort(v), nil
@@ -149,57 +143,57 @@ func (f *StringFuncs) Sort(list interface{}) ([]string, error) {
}
// Split -
-func (f *StringFuncs) Split(sep string, s interface{}) []string {
+func (StringFuncs) Split(sep string, s interface{}) []string {
return strings.Split(conv.ToString(s), sep)
}
// SplitN -
-func (f *StringFuncs) SplitN(sep string, n int, s interface{}) []string {
+func (StringFuncs) SplitN(sep string, n int, s interface{}) []string {
return strings.SplitN(conv.ToString(s), sep, n)
}
// Trim -
-func (f *StringFuncs) Trim(cutset string, s interface{}) string {
+func (StringFuncs) Trim(cutset string, s interface{}) string {
return strings.Trim(conv.ToString(s), cutset)
}
// TrimPrefix -
-func (f *StringFuncs) TrimPrefix(cutset string, s interface{}) string {
+func (StringFuncs) TrimPrefix(cutset string, s interface{}) string {
return strings.TrimPrefix(conv.ToString(s), cutset)
}
// TrimSuffix -
-func (f *StringFuncs) TrimSuffix(cutset string, s interface{}) string {
+func (StringFuncs) TrimSuffix(cutset string, s interface{}) string {
return strings.TrimSuffix(conv.ToString(s), cutset)
}
// Title -
-func (f *StringFuncs) Title(s interface{}) string {
+func (StringFuncs) Title(s interface{}) string {
return strings.Title(conv.ToString(s))
}
// ToUpper -
-func (f *StringFuncs) ToUpper(s interface{}) string {
+func (StringFuncs) ToUpper(s interface{}) string {
return strings.ToUpper(conv.ToString(s))
}
// ToLower -
-func (f *StringFuncs) ToLower(s interface{}) string {
+func (StringFuncs) ToLower(s interface{}) string {
return strings.ToLower(conv.ToString(s))
}
// TrimSpace -
-func (f *StringFuncs) TrimSpace(s interface{}) string {
+func (StringFuncs) TrimSpace(s interface{}) string {
return strings.TrimSpace(conv.ToString(s))
}
// Trunc -
-func (f *StringFuncs) Trunc(length int, s interface{}) string {
+func (StringFuncs) Trunc(length int, s interface{}) string {
return gompstrings.Trunc(length, conv.ToString(s))
}
// Indent -
-func (f *StringFuncs) Indent(args ...interface{}) (string, error) {
+func (StringFuncs) Indent(args ...interface{}) (string, error) {
input := conv.ToString(args[len(args)-1])
indent := " "
width := 1
@@ -228,17 +222,17 @@ func (f *StringFuncs) Indent(args ...interface{}) (string, error) {
}
// Slug -
-func (f *StringFuncs) Slug(in interface{}) string {
+func (StringFuncs) Slug(in interface{}) string {
return slug.Make(conv.ToString(in))
}
// Quote -
-func (f *StringFuncs) Quote(in interface{}) string {
+func (StringFuncs) Quote(in interface{}) string {
return fmt.Sprintf("%q", conv.ToString(in))
}
// ShellQuote -
-func (f *StringFuncs) ShellQuote(in interface{}) string {
+func (StringFuncs) ShellQuote(in interface{}) string {
val := reflect.ValueOf(in)
switch val.Kind() {
case reflect.Array, reflect.Slice:
@@ -256,29 +250,29 @@ func (f *StringFuncs) ShellQuote(in interface{}) string {
}
// Squote -
-func (f *StringFuncs) Squote(in interface{}) string {
+func (StringFuncs) Squote(in interface{}) string {
s := conv.ToString(in)
s = strings.ReplaceAll(s, `'`, `''`)
return fmt.Sprintf("'%s'", s)
}
// SnakeCase -
-func (f *StringFuncs) SnakeCase(in interface{}) (string, error) {
+func (StringFuncs) SnakeCase(in interface{}) (string, error) {
return gompstrings.SnakeCase(conv.ToString(in)), nil
}
// CamelCase -
-func (f *StringFuncs) CamelCase(in interface{}) (string, error) {
+func (StringFuncs) CamelCase(in interface{}) (string, error) {
return gompstrings.CamelCase(conv.ToString(in)), nil
}
// KebabCase -
-func (f *StringFuncs) KebabCase(in interface{}) (string, error) {
+func (StringFuncs) KebabCase(in interface{}) (string, error) {
return gompstrings.KebabCase(conv.ToString(in)), nil
}
// WordWrap -
-func (f *StringFuncs) WordWrap(args ...interface{}) (string, error) {
+func (StringFuncs) WordWrap(args ...interface{}) (string, error) {
if len(args) == 0 || len(args) > 3 {
return "", errors.Errorf("expected 1, 2, or 3 args, got %d", len(args))
}
@@ -301,7 +295,7 @@ func (f *StringFuncs) WordWrap(args ...interface{}) (string, error) {
}
// RuneCount - like len(s), but for runes
-func (f *StringFuncs) RuneCount(args ...interface{}) (int, error) {
+func (StringFuncs) RuneCount(args ...interface{}) (int, error) {
s := ""
for _, arg := range args {
s += conv.ToString(arg)
diff --git a/funcs/strings_test.go b/funcs/strings_test.go
index 0975c6d1..94b72d60 100644
--- a/funcs/strings_test.go
+++ b/funcs/strings_test.go
@@ -1,11 +1,28 @@
package funcs
import (
+ "context"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreateStringFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateStringFuncs(ctx)
+ actual := fmap["strings"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*StringFuncs).ctx)
+ })
+ }
+}
+
func TestReplaceAll(t *testing.T) {
sf := &StringFuncs{}
diff --git a/funcs/test.go b/funcs/test.go
index 1ceef651..88546018 100644
--- a/funcs/test.go
+++ b/funcs/test.go
@@ -3,7 +3,6 @@ package funcs
import (
"context"
"reflect"
- "sync"
"github.com/hairyhenderson/gomplate/v3/conv"
"github.com/pkg/errors"
@@ -11,18 +10,14 @@ import (
"github.com/hairyhenderson/gomplate/v3/test"
)
-var (
- testNS *TestFuncs
- testNSInit sync.Once
-)
-
// TestNS -
+// Deprecated: don't use
func TestNS() *TestFuncs {
- testNSInit.Do(func() { testNS = &TestFuncs{} })
- return testNS
+ return &TestFuncs{}
}
// AddTestFuncs -
+// Deprecated: use CreateTestFuncs instead
func AddTestFuncs(f map[string]interface{}) {
for k, v := range CreateTestFuncs(context.Background()) {
f[k] = v
@@ -32,9 +27,9 @@ func AddTestFuncs(f map[string]interface{}) {
// CreateTestFuncs -
func CreateTestFuncs(ctx context.Context) map[string]interface{} {
f := map[string]interface{}{}
- ns := TestNS()
- ns.ctx = ctx
- f["test"] = TestNS
+
+ ns := &TestFuncs{ctx}
+ f["test"] = func() interface{} { return ns }
f["assert"] = ns.Assert
f["fail"] = ns.Fail
@@ -51,7 +46,7 @@ type TestFuncs struct {
}
// Assert -
-func (f *TestFuncs) Assert(args ...interface{}) (string, error) {
+func (TestFuncs) Assert(args ...interface{}) (string, error) {
input := conv.ToBool(args[len(args)-1])
switch len(args) {
case 1:
@@ -68,7 +63,7 @@ func (f *TestFuncs) Assert(args ...interface{}) (string, error) {
}
// Fail -
-func (f *TestFuncs) Fail(args ...interface{}) (string, error) {
+func (TestFuncs) Fail(args ...interface{}) (string, error) {
switch len(args) {
case 0:
return "", test.Fail("")
@@ -80,7 +75,7 @@ func (f *TestFuncs) Fail(args ...interface{}) (string, error) {
}
// Required -
-func (f *TestFuncs) Required(args ...interface{}) (interface{}, error) {
+func (TestFuncs) Required(args ...interface{}) (interface{}, error) {
switch len(args) {
case 1:
return test.Required("", args[0])
@@ -96,7 +91,7 @@ func (f *TestFuncs) Required(args ...interface{}) (interface{}, error) {
}
// Ternary -
-func (f *TestFuncs) Ternary(tval, fval, b interface{}) interface{} {
+func (TestFuncs) Ternary(tval, fval, b interface{}) interface{} {
if conv.ToBool(b) {
return tval
}
@@ -104,12 +99,12 @@ func (f *TestFuncs) Ternary(tval, fval, b interface{}) interface{} {
}
// Kind - return the kind of the argument
-func (f *TestFuncs) Kind(arg interface{}) string {
+func (TestFuncs) Kind(arg interface{}) string {
return reflect.ValueOf(arg).Kind().String()
}
// IsKind - return whether or not the argument is of the given kind
-func (f *TestFuncs) IsKind(kind string, arg interface{}) bool {
+func (f TestFuncs) IsKind(kind string, arg interface{}) bool {
k := f.Kind(arg)
if kind == "number" {
switch k {
diff --git a/funcs/test_test.go b/funcs/test_test.go
index 5fa672a5..fc5bbb19 100644
--- a/funcs/test_test.go
+++ b/funcs/test_test.go
@@ -1,11 +1,28 @@
package funcs
import (
+ "context"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreateTestFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateTestFuncs(ctx)
+ actual := fmap["test"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*TestFuncs).ctx)
+ })
+ }
+}
+
func TestAssert(t *testing.T) {
f := TestNS()
_, err := f.Assert(false)
diff --git a/funcs/time.go b/funcs/time.go
index d6b24acb..fb84f587 100644
--- a/funcs/time.go
+++ b/funcs/time.go
@@ -5,7 +5,6 @@ import (
"fmt"
"strconv"
"strings"
- "sync"
gotime "time"
"github.com/hairyhenderson/gomplate/v3/conv"
@@ -13,36 +12,30 @@ import (
"github.com/hairyhenderson/gomplate/v3/time"
)
-var (
- timeNS *TimeFuncs
- timeNSInit sync.Once
-)
-
// TimeNS -
+// Deprecated: don't use
func TimeNS() *TimeFuncs {
- timeNSInit.Do(func() {
- timeNS = &TimeFuncs{
- ANSIC: gotime.ANSIC,
- UnixDate: gotime.UnixDate,
- RubyDate: gotime.RubyDate,
- RFC822: gotime.RFC822,
- RFC822Z: gotime.RFC822Z,
- RFC850: gotime.RFC850,
- RFC1123: gotime.RFC1123,
- RFC1123Z: gotime.RFC1123Z,
- RFC3339: gotime.RFC3339,
- RFC3339Nano: gotime.RFC3339Nano,
- Kitchen: gotime.Kitchen,
- Stamp: gotime.Stamp,
- StampMilli: gotime.StampMilli,
- StampMicro: gotime.StampMicro,
- StampNano: gotime.StampNano,
- }
- })
- return timeNS
+ return &TimeFuncs{
+ ANSIC: gotime.ANSIC,
+ UnixDate: gotime.UnixDate,
+ RubyDate: gotime.RubyDate,
+ RFC822: gotime.RFC822,
+ RFC822Z: gotime.RFC822Z,
+ RFC850: gotime.RFC850,
+ RFC1123: gotime.RFC1123,
+ RFC1123Z: gotime.RFC1123Z,
+ RFC3339: gotime.RFC3339,
+ RFC3339Nano: gotime.RFC3339Nano,
+ Kitchen: gotime.Kitchen,
+ Stamp: gotime.Stamp,
+ StampMilli: gotime.StampMilli,
+ StampMicro: gotime.StampMicro,
+ StampNano: gotime.StampNano,
+ }
}
// AddTimeFuncs -
+// Deprecated: use CreateTimeFuncs instead
func AddTimeFuncs(f map[string]interface{}) {
for k, v := range CreateTimeFuncs(context.Background()) {
f[k] = v
@@ -51,10 +44,28 @@ func AddTimeFuncs(f map[string]interface{}) {
// CreateTimeFuncs -
func CreateTimeFuncs(ctx context.Context) map[string]interface{} {
- ns := TimeNS()
- ns.ctx = ctx
+ ns := &TimeFuncs{
+ ctx: ctx,
+ ANSIC: gotime.ANSIC,
+ UnixDate: gotime.UnixDate,
+ RubyDate: gotime.RubyDate,
+ RFC822: gotime.RFC822,
+ RFC822Z: gotime.RFC822Z,
+ RFC850: gotime.RFC850,
+ RFC1123: gotime.RFC1123,
+ RFC1123Z: gotime.RFC1123Z,
+ RFC3339: gotime.RFC3339,
+ RFC3339Nano: gotime.RFC3339Nano,
+ Kitchen: gotime.Kitchen,
+ Stamp: gotime.Stamp,
+ StampMilli: gotime.StampMilli,
+ StampMicro: gotime.StampMicro,
+ StampNano: gotime.StampNano,
+ }
- return map[string]interface{}{"time": TimeNS}
+ return map[string]interface{}{
+ "time": func() interface{} { return ns },
+ }
}
// TimeFuncs -
@@ -78,28 +89,28 @@ type TimeFuncs struct {
}
// ZoneName - return the local system's time zone's name
-func (f *TimeFuncs) ZoneName() string {
+func (TimeFuncs) ZoneName() string {
return time.ZoneName()
}
// ZoneOffset - return the local system's time zone's name
-func (f *TimeFuncs) ZoneOffset() int {
+func (TimeFuncs) ZoneOffset() int {
return time.ZoneOffset()
}
// Parse -
-func (f *TimeFuncs) Parse(layout string, value interface{}) (gotime.Time, error) {
+func (TimeFuncs) Parse(layout string, value interface{}) (gotime.Time, error) {
return gotime.Parse(layout, conv.ToString(value))
}
// ParseLocal -
-func (f *TimeFuncs) ParseLocal(layout string, value interface{}) (gotime.Time, error) {
+func (f TimeFuncs) ParseLocal(layout string, value interface{}) (gotime.Time, error) {
tz := env.Getenv("TZ", "Local")
return f.ParseInLocation(layout, tz, value)
}
// ParseInLocation -
-func (f *TimeFuncs) ParseInLocation(layout, location string, value interface{}) (gotime.Time, error) {
+func (TimeFuncs) ParseInLocation(layout, location string, value interface{}) (gotime.Time, error) {
loc, err := gotime.LoadLocation(location)
if err != nil {
return gotime.Time{}, err
@@ -108,13 +119,13 @@ func (f *TimeFuncs) ParseInLocation(layout, location string, value interface{})
}
// Now -
-func (f *TimeFuncs) Now() gotime.Time {
+func (TimeFuncs) Now() gotime.Time {
return gotime.Now()
}
// Unix - convert UNIX time (in seconds since the UNIX epoch) into a time.Time for further processing
// Takes a string or number (int or float)
-func (f *TimeFuncs) Unix(in interface{}) (gotime.Time, error) {
+func (TimeFuncs) Unix(in interface{}) (gotime.Time, error) {
sec, nsec, err := parseNum(in)
if err != nil {
return gotime.Time{}, err
@@ -123,47 +134,47 @@ func (f *TimeFuncs) Unix(in interface{}) (gotime.Time, error) {
}
// Nanosecond -
-func (f *TimeFuncs) Nanosecond(n interface{}) gotime.Duration {
+func (TimeFuncs) Nanosecond(n interface{}) gotime.Duration {
return gotime.Nanosecond * gotime.Duration(conv.ToInt64(n))
}
// Microsecond -
-func (f *TimeFuncs) Microsecond(n interface{}) gotime.Duration {
+func (TimeFuncs) Microsecond(n interface{}) gotime.Duration {
return gotime.Microsecond * gotime.Duration(conv.ToInt64(n))
}
// Millisecond -
-func (f *TimeFuncs) Millisecond(n interface{}) gotime.Duration {
+func (TimeFuncs) Millisecond(n interface{}) gotime.Duration {
return gotime.Millisecond * gotime.Duration(conv.ToInt64(n))
}
// Second -
-func (f *TimeFuncs) Second(n interface{}) gotime.Duration {
+func (TimeFuncs) Second(n interface{}) gotime.Duration {
return gotime.Second * gotime.Duration(conv.ToInt64(n))
}
// Minute -
-func (f *TimeFuncs) Minute(n interface{}) gotime.Duration {
+func (TimeFuncs) Minute(n interface{}) gotime.Duration {
return gotime.Minute * gotime.Duration(conv.ToInt64(n))
}
// Hour -
-func (f *TimeFuncs) Hour(n interface{}) gotime.Duration {
+func (TimeFuncs) Hour(n interface{}) gotime.Duration {
return gotime.Hour * gotime.Duration(conv.ToInt64(n))
}
// ParseDuration -
-func (f *TimeFuncs) ParseDuration(n interface{}) (gotime.Duration, error) {
+func (TimeFuncs) ParseDuration(n interface{}) (gotime.Duration, error) {
return gotime.ParseDuration(conv.ToString(n))
}
// Since -
-func (f *TimeFuncs) Since(n gotime.Time) gotime.Duration {
+func (TimeFuncs) Since(n gotime.Time) gotime.Duration {
return gotime.Since(n)
}
// Until -
-func (f *TimeFuncs) Until(n gotime.Time) gotime.Duration {
+func (TimeFuncs) Until(n gotime.Time) gotime.Duration {
return gotime.Until(n)
}
diff --git a/funcs/time_test.go b/funcs/time_test.go
index 99ab1e34..63c5f259 100644
--- a/funcs/time_test.go
+++ b/funcs/time_test.go
@@ -1,13 +1,30 @@
package funcs
import (
+ "context"
"math"
"math/big"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreateTimeFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateTimeFuncs(ctx)
+ actual := fmap["time"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*TimeFuncs).ctx)
+ })
+ }
+}
+
func TestParseNum(t *testing.T) {
i, f, _ := parseNum("42")
assert.Equal(t, int64(42), i)
diff --git a/funcs/uuid.go b/funcs/uuid.go
index 3799334e..bab667d0 100644
--- a/funcs/uuid.go
+++ b/funcs/uuid.go
@@ -2,27 +2,20 @@ package funcs
import (
"context"
- "sync"
"github.com/hairyhenderson/gomplate/v3/conv"
"github.com/google/uuid"
)
-var (
- uuidNS *UUIDFuncs
- uuidNSInit sync.Once
-)
-
// UUIDNS -
+// Deprecated: don't use
func UUIDNS() *UUIDFuncs {
- uuidNSInit.Do(func() {
- uuidNS = &UUIDFuncs{}
- })
- return uuidNS
+ return &UUIDFuncs{}
}
// AddUUIDFuncs -
+// Deprecated: use CreateUUIDFuncs instead
func AddUUIDFuncs(f map[string]interface{}) {
for k, v := range CreateUUIDFuncs(context.Background()) {
f[k] = v
@@ -31,9 +24,10 @@ func AddUUIDFuncs(f map[string]interface{}) {
// CreateUUIDFuncs -
func CreateUUIDFuncs(ctx context.Context) map[string]interface{} {
- ns := UUIDNS()
- ns.ctx = ctx
- return map[string]interface{}{"uuid": UUIDNS}
+ ns := &UUIDFuncs{ctx}
+ return map[string]interface{}{
+ "uuid": func() interface{} { return ns },
+ }
}
// UUIDFuncs -
@@ -43,7 +37,7 @@ type UUIDFuncs struct {
// V1 - return a version 1 UUID (based on the current MAC Address and the
// current date/time). Use V4 instead in most cases.
-func (f *UUIDFuncs) V1() (string, error) {
+func (UUIDFuncs) V1() (string, error) {
u, err := uuid.NewUUID()
if err != nil {
return "", err
@@ -52,7 +46,7 @@ func (f *UUIDFuncs) V1() (string, error) {
}
// V4 - return a version 4 (random) UUID
-func (f *UUIDFuncs) V4() (string, error) {
+func (UUIDFuncs) V4() (string, error) {
u, err := uuid.NewRandom()
if err != nil {
return "", err
@@ -61,13 +55,13 @@ func (f *UUIDFuncs) V4() (string, error) {
}
// Nil -
-func (f *UUIDFuncs) Nil() (string, error) {
+func (UUIDFuncs) Nil() (string, error) {
return uuid.Nil.String(), nil
}
// IsValid - checks if the given UUID is in the correct format. It does not
// validate whether the version or variant are correct.
-func (f *UUIDFuncs) IsValid(in interface{}) (bool, error) {
+func (f UUIDFuncs) IsValid(in interface{}) (bool, error) {
_, err := f.Parse(in)
return err == nil, nil
}
@@ -78,7 +72,7 @@ func (f *UUIDFuncs) IsValid(in interface{}) (bool, error) {
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
-func (f *UUIDFuncs) Parse(in interface{}) (uuid.UUID, error) {
+func (UUIDFuncs) Parse(in interface{}) (uuid.UUID, error) {
u, err := uuid.Parse(conv.ToString(in))
if err != nil {
return uuid.Nil, err
diff --git a/funcs/uuid_test.go b/funcs/uuid_test.go
index ef6cb4e8..974741d7 100644
--- a/funcs/uuid_test.go
+++ b/funcs/uuid_test.go
@@ -1,12 +1,29 @@
package funcs
import (
+ "context"
"net/url"
+ "strconv"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestCreateUUIDFuncs(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ // Run this a bunch to catch race conditions
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+
+ ctx := context.Background()
+ fmap := CreateUUIDFuncs(ctx)
+ actual := fmap["uuid"].(func() interface{})
+
+ assert.Same(t, ctx, actual().(*UUIDFuncs).ctx)
+ })
+ }
+}
+
const (
uuidV1Pattern = "^[[:xdigit:]]{8}-[[:xdigit:]]{4}-1[[:xdigit:]]{3}-[89ab][[:xdigit:]]{3}-[[:xdigit:]]{12}$"
uuidV4Pattern = "^[[:xdigit:]]{8}-[[:xdigit:]]{4}-4[[:xdigit:]]{3}-[89ab][[:xdigit:]]{3}-[[:xdigit:]]{12}$"