aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFrancis Lavoie <[email protected]>2023-10-15 22:27:46 -0400
committerFrancis Lavoie <[email protected]>2024-04-24 14:17:27 -0400
commit5af105fa9e97323fb0671b8d8adc7b5ebc55fc1c (patch)
tree971bc542098b1d79ee5605f5927d7451b48e75ed
parentf999d903f868182d1a941ad4e691e64398afdd07 (diff)
downloadcaddy-5af105fa9e97323fb0671b8d8adc7b5ebc55fc1c.tar.gz
caddy-5af105fa9e97323fb0671b8d8adc7b5ebc55fc1c.zip
Rework replacer types to allow filtering out the file replacer
Co-authored-by: Mohammed Al Sahaf <[email protected]>
-rw-r--r--replacer.go73
-rw-r--r--replacer_test.go36
2 files changed, 58 insertions, 51 deletions
diff --git a/replacer.go b/replacer.go
index 7cbf64bdf..175339560 100644
--- a/replacer.go
+++ b/replacer.go
@@ -35,10 +35,10 @@ func NewReplacer() *Replacer {
static: make(map[string]any),
mapMutex: &sync.RWMutex{},
}
- rep.providers = []ReplacerFunc{
- globalDefaultReplacements,
- fileReplacements,
- rep.fromStatic,
+ rep.providers = []replacementProvider{
+ globalDefaultReplacementProvider{},
+ fileReplacementProvider{},
+ ReplacerFunc(rep.fromStatic),
}
return rep
}
@@ -50,8 +50,8 @@ func NewEmptyReplacer() *Replacer {
static: make(map[string]any),
mapMutex: &sync.RWMutex{},
}
- rep.providers = []ReplacerFunc{
- rep.fromStatic,
+ rep.providers = []replacementProvider{
+ ReplacerFunc(rep.fromStatic),
}
return rep
}
@@ -60,9 +60,8 @@ func NewEmptyReplacer() *Replacer {
// A default/empty Replacer is not valid;
// use NewReplacer to make one.
type Replacer struct {
- providers []ReplacerFunc
-
- static map[string]any
+ providers []replacementProvider
+ static map[string]any
mapMutex *sync.RWMutex
}
@@ -71,16 +70,12 @@ type Replacer struct {
// may be unsafe in some contexts.
func (r *Replacer) WithoutFile() *Replacer {
rep := &Replacer{static: r.static}
-
- // Add a func at the front of the list that
- // always skips file placeholders
- rep.providers = append(
- []ReplacerFunc{func(key string) (any, bool) {
- return nil, strings.HasPrefix(key, filePrefix)
- }},
- r.providers...,
- )
-
+ for _, v := range r.providers {
+ if _, ok := v.(fileReplacementProvider); ok {
+ continue
+ }
+ rep.providers = append(rep.providers, v)
+ }
return rep
}
@@ -101,7 +96,7 @@ func (r *Replacer) Set(variable string, value any) {
// the value and whether the variable was known.
func (r *Replacer) Get(variable string) (any, bool) {
for _, mapFunc := range r.providers {
- if val, ok := mapFunc(variable); ok {
+ if val, ok := mapFunc.replace(variable); ok {
return val, true
}
}
@@ -320,16 +315,28 @@ func ToString(val any) string {
}
}
-// ReplacerFunc is a function that returns a replacement
-// for the given key along with true if the function is able
-// to service that key (even if the value is blank). If the
-// function does not recognize the key, false should be
-// returned.
+// ReplacerFunc is a function that returns a replacement for the
+// given key along with true if the function is able to service
+// that key (even if the value is blank). If the function does
+// not recognize the key, false should be returned.
type ReplacerFunc func(key string) (any, bool)
-// fileReplacements handles {file.*} replacements, reading
-// a file from disk and replacing with its contents.
-func fileReplacements(key string) (any, bool) {
+func (f ReplacerFunc) replace(key string) (any, bool) {
+ return f(key)
+}
+
+// replacementProvider is a type that can provide replacements
+// for placeholders. Allows for type assertion to determine
+// which type of provider it is.
+type replacementProvider interface {
+ replace(key string) (any, bool)
+}
+
+// fileReplacementsProvider handles {file.*} replacements,
+// reading a file from disk and replacing with its contents.
+type fileReplacementProvider struct{}
+
+func (f fileReplacementProvider) replace(key string) (any, bool) {
if !strings.HasPrefix(key, filePrefix) {
return nil, false
}
@@ -348,10 +355,12 @@ func fileReplacements(key string) (any, bool) {
return string(body), true
}
-// globalDefaultReplacements handles replacements that can
-// be used in any context, such as system variables, time,
-// or environment variables.
-func globalDefaultReplacements(key string) (any, bool) {
+// globalDefaultReplacementsProvider handles replacements
+// that can be used in any context, such as system variables,
+// time, or environment variables.
+type globalDefaultReplacementProvider struct{}
+
+func (f globalDefaultReplacementProvider) replace(key string) (any, bool) {
// check environment variable
const envPrefix = "env."
if strings.HasPrefix(key, envPrefix) {
diff --git a/replacer_test.go b/replacer_test.go
index 56b2f500e..cf4d321b6 100644
--- a/replacer_test.go
+++ b/replacer_test.go
@@ -240,9 +240,9 @@ func TestReplacerSet(t *testing.T) {
func TestReplacerReplaceKnown(t *testing.T) {
rep := Replacer{
mapMutex: &sync.RWMutex{},
- providers: []ReplacerFunc{
+ providers: []replacementProvider{
// split our possible vars to two functions (to test if both functions are called)
- func(key string) (val any, ok bool) {
+ ReplacerFunc(func(key string) (val any, ok bool) {
switch key {
case "test1":
return "val1", true
@@ -255,8 +255,8 @@ func TestReplacerReplaceKnown(t *testing.T) {
default:
return "NOOO", false
}
- },
- func(key string) (val any, ok bool) {
+ }),
+ ReplacerFunc(func(key string) (val any, ok bool) {
switch key {
case "1":
return "test-123", true
@@ -267,7 +267,7 @@ func TestReplacerReplaceKnown(t *testing.T) {
default:
return "NOOO", false
}
- },
+ }),
},
}
@@ -413,7 +413,7 @@ func TestReplacerNew(t *testing.T) {
value: "envtest",
},
} {
- if val, ok := repl.providers[0](tc.variable); ok {
+ if val, ok := repl.providers[0].replace(tc.variable); ok {
if val != tc.value {
t.Errorf("Expected value '%s' for key '%s' got '%s'", tc.value, tc.variable, val)
}
@@ -432,7 +432,7 @@ func TestReplacerNew(t *testing.T) {
value: "foo",
},
} {
- if val, ok := repl.providers[1](tc.variable); ok {
+ if val, ok := repl.providers[1].replace(tc.variable); ok {
if val != tc.value {
t.Errorf("Expected value '%s' for key '%s' got '%s'", tc.value, tc.variable, val)
}
@@ -446,27 +446,25 @@ func TestReplacerNewWithoutFile(t *testing.T) {
repl := NewReplacer().WithoutFile()
for _, tc := range []struct {
- variable string
- value string
- expectNil bool
+ variable string
+ value string
+ notFound bool
}{
{
- variable: "file.caddytest/integration/testdata/foo.txt",
- expectNil: true,
+ variable: "file.caddytest/integration/testdata/foo.txt",
+ notFound: true,
},
{
variable: "system.os",
value: runtime.GOOS,
},
} {
- if val, ok := repl.Get(tc.variable); ok {
- if tc.expectNil && val != nil {
- t.Errorf("Expected nil for key '%s' got '%v'", tc.variable, val)
- } else if !tc.expectNil && val != tc.value {
+ if val, ok := repl.Get(tc.variable); ok && !tc.notFound {
+ if val != tc.value {
t.Errorf("Expected value '%s' for key '%s' got '%s'", tc.value, tc.variable, val)
}
- } else {
- t.Errorf("Expected key '%s' to be recognized by second provider", tc.variable)
+ } else if !tc.notFound {
+ t.Errorf("Expected key '%s' to be recognized", tc.variable)
}
}
}
@@ -512,7 +510,7 @@ func BenchmarkReplacer(b *testing.B) {
func testReplacer() Replacer {
return Replacer{
- providers: make([]ReplacerFunc, 0),
+ providers: make([]replacementProvider, 0),
static: make(map[string]any),
mapMutex: &sync.RWMutex{},
}