Repository: compose-spec/compose-go
Pull request #431 (2023-07-07 07:01:13)

Extend default replacement function with applied flag
In some circumstances it is important to know if an interpolation happened or the variable was actually mapped, e.g. to solve https://github.com/influxdata/telegraf/issues/13565. Extend the `DefaultReplacementFunc` by the additional flag staying compatible to the previous API...


Patch:
diff --git a/template/template.go b/template/template.go
index 141b56d..cce4c62 100644
--- a/template/template.go
+++ b/template/template.go
@@ -144,6 +144,11 @@ func SubstituteWithOptions(template string, mapping Mapping, options ...Option)
 }
 
 func DefaultReplacementFunc(substring string, mapping Mapping, cfg *Config) (string, error) {
+	value, _, err := DefaultReplacementAppliedFunc(substring, mapping, cfg)
+	return value, err
+}
+
+func DefaultReplacementAppliedFunc(substring string, mapping Mapping, cfg *Config) (string, bool, error) {
 	pattern := cfg.pattern
 	subsFunc := cfg.substituteFunc
 	if subsFunc == nil {
@@ -160,7 +165,7 @@ func DefaultReplacementFunc(substring string, mapping Mapping, cfg *Config) (str
 	matches := pattern.FindStringSubmatch(substring)
 	groups := matchGroups(matches, pattern)
 	if escaped := groups["escaped"]; escaped != "" {
-		return escaped, nil
+		return escaped, true, nil
 	}
 
 	braced := false
@@ -171,20 +176,20 @@ func DefaultReplacementFunc(substring string, mapping Mapping, cfg *Config) (str
 	}
 
 	if substitution == "" {
-		return "", &InvalidTemplateError{}
+		return "", false, &InvalidTemplateError{}
 	}
 
 	if braced {
 		value, applied, err := subsFunc(substitution, mapping)
 		if err != nil {
-			return "", err
+			return "", false, err
 		}
 		if applied {
 			interpolatedNested, err := SubstituteWith(rest, mapping, pattern)
 			if err != nil {
-				return "", err
+				return "", false, err
 			}
-			return value + interpolatedNested, nil
+			return value + interpolatedNested, true, nil
 		}
 	}
 
@@ -193,7 +198,7 @@ func DefaultReplacementFunc(substring string, mapping Mapping, cfg *Config) (str
 		logrus.Warnf("The %q variable is not set. Defaulting to a blank string.", substitution)
 	}
 
-	return value, nil
+	return value, ok, nil
 }
 
 // SubstituteWith substitute variables in the string with their values.


Test patch:
diff --git a/template/template_test.go b/template/template_test.go
index f314e92..dbd27de 100644
--- a/template/template_test.go
+++ b/template/template_test.go
@@ -398,6 +398,39 @@ func TestSubstituteWithReplacementFunc(t *testing.T) {
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal("ok foobar", result))
 
+	result, err = SubstituteWithOptions("ok ${UNSET}", defaultMapping, options...)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal("ok foobar", result))
+
+	_, err = SubstituteWithOptions("ok ${NOTHERE}", defaultMapping, options...)
+	assert.Check(t, is.ErrorContains(err, "bad choice"))
+}
+
+func TestSubstituteWithReplacementAppliedFunc(t *testing.T) {
+	options := []Option{
+		WithReplacementFunction(func(s string, m Mapping, c *Config) (string, error) {
+			if s == "${NOTHERE}" {
+				return "", fmt.Errorf("bad choice: %q", s)
+			}
+			r, applied, err := DefaultReplacementAppliedFunc(s, m, c)
+			if err == nil && applied {
+				return r, nil
+			}
+			return "foobar", nil
+		}),
+	}
+	result, err := SubstituteWithOptions("ok ${FOO}", defaultMapping, options...)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal("ok first", result))
+
+	result, err = SubstituteWithOptions("ok ${BAR}", defaultMapping, options...)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal("ok ", result))
+
+	result, err = SubstituteWithOptions("ok ${UNSET}", defaultMapping, options...)
+	assert.NilError(t, err)
+	assert.Check(t, is.Equal("ok foobar", result))
+
 	_, err = SubstituteWithOptions("ok ${NOTHERE}", defaultMapping, options...)
 	assert.Check(t, is.ErrorContains(err, "bad choice"))
 }