@@ -19,6 +19,7 @@ package template
1919import (
2020 "fmt"
2121 "regexp"
22+ "sort"
2223 "strings"
2324
2425 "github.com/sirupsen/logrus"
@@ -60,10 +61,12 @@ type SubstituteFunc func(string, Mapping) (string, bool, error)
6061// SubstituteWith substitute variables in the string with their values.
6162// It accepts additional substitute function.
6263func SubstituteWith (template string , mapping Mapping , pattern * regexp.Regexp , subsFuncs ... SubstituteFunc ) (string , error ) {
64+ var err error
65+
6366 if len (subsFuncs ) == 0 {
64- subsFuncs = getDefaultSortedSubstitutionFunctions (template )
67+ _ , subsFunc := getSubstitutionFunctionForTemplate (template )
68+ subsFuncs = []SubstituteFunc {subsFunc }
6569 }
66- var err error
6770 result := pattern .ReplaceAllStringFunc (template , func (substring string ) string {
6871 closingBraceIndex := getFirstBraceClosingIndex (substring )
6972 rest := ""
@@ -121,23 +124,31 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su
121124 return result , err
122125}
123126
124- func getDefaultSortedSubstitutionFunctions (template string , fns ... SubstituteFunc ) []SubstituteFunc {
125- hyphenIndex := strings .IndexByte (template , '-' )
126- questionIndex := strings .IndexByte (template , '?' )
127- if hyphenIndex < 0 || hyphenIndex > questionIndex {
128- return []SubstituteFunc {
129- requiredNonEmpty ,
130- required ,
131- softDefault ,
132- hardDefault ,
133- }
134- }
135- return []SubstituteFunc {
136- softDefault ,
137- hardDefault ,
138- requiredNonEmpty ,
139- required ,
127+ func getSubstitutionFunctionForTemplate (template string ) (string , SubstituteFunc ) {
128+ interpolationMapping := []struct {
129+ string
130+ SubstituteFunc
131+ }{
132+ {":?" , requiredErrorWhenEmptyOrUnset },
133+ {"?" , requiredErrorWhenUnset },
134+ {":-" , defaultWhenEmptyOrUnset },
135+ {"-" , defaultWhenUnset },
136+ {":+" , defaultWhenNotEmpty },
137+ {"+" , defaultWhenSet },
140138 }
139+ sort .Slice (interpolationMapping , func (i , j int ) bool {
140+ idxI := strings .Index (template , interpolationMapping [i ].string )
141+ idxJ := strings .Index (template , interpolationMapping [j ].string )
142+ if idxI < 0 {
143+ return false
144+ }
145+ if idxJ < 0 {
146+ return true
147+ }
148+ return idxI < idxJ
149+ })
150+
151+ return interpolationMapping [0 ].string , interpolationMapping [0 ].SubstituteFunc
141152}
142153
143154func getFirstBraceClosingIndex (s string ) int {
@@ -252,26 +263,36 @@ func extractVariable(value interface{}, pattern *regexp.Regexp) ([]Variable, boo
252263}
253264
254265// Soft default (fall back if unset or empty)
255- func softDefault (substitution string , mapping Mapping ) (string , bool , error ) {
256- sep := ":-"
257- if ! strings .Contains (substitution , sep ) {
258- return "" , false , nil
259- }
260- name , defaultValue := partition (substitution , sep )
261- defaultValue , err := Substitute (defaultValue , mapping )
262- if err != nil {
263- return "" , false , err
264- }
265- value , ok := mapping (name )
266- if ! ok || value == "" {
267- return defaultValue , true , nil
268- }
269- return value , true , nil
266+ func defaultWhenEmptyOrUnset (substitution string , mapping Mapping ) (string , bool , error ) {
267+ return withDefaultWhenAbsence (substitution , mapping , true )
270268}
271269
272270// Hard default (fall back if-and-only-if empty)
273- func hardDefault (substitution string , mapping Mapping ) (string , bool , error ) {
271+ func defaultWhenUnset (substitution string , mapping Mapping ) (string , bool , error ) {
272+ return withDefaultWhenAbsence (substitution , mapping , false )
273+ }
274+
275+ func defaultWhenNotEmpty (substitution string , mapping Mapping ) (string , bool , error ) {
276+ return "" , false , nil // TODO Implement ":+"
277+ }
278+
279+ func defaultWhenSet (substitution string , mapping Mapping ) (string , bool , error ) {
280+ return "" , false , nil // TODO Implement "+"
281+ }
282+
283+ func requiredErrorWhenEmptyOrUnset (substitution string , mapping Mapping ) (string , bool , error ) {
284+ return withRequired (substitution , mapping , ":?" , func (v string ) bool { return v != "" })
285+ }
286+
287+ func requiredErrorWhenUnset (substitution string , mapping Mapping ) (string , bool , error ) {
288+ return withRequired (substitution , mapping , "?" , func (_ string ) bool { return true })
289+ }
290+
291+ func withDefaultWhenAbsence (substitution string , mapping Mapping , emptyOrUnset bool ) (string , bool , error ) {
274292 sep := "-"
293+ if emptyOrUnset {
294+ sep = ":-"
295+ }
275296 if ! strings .Contains (substitution , sep ) {
276297 return "" , false , nil
277298 }
@@ -281,20 +302,12 @@ func hardDefault(substitution string, mapping Mapping) (string, bool, error) {
281302 return "" , false , err
282303 }
283304 value , ok := mapping (name )
284- if ! ok {
305+ if ! ok || ( emptyOrUnset && value == "" ) {
285306 return defaultValue , true , nil
286307 }
287308 return value , true , nil
288309}
289310
290- func requiredNonEmpty (substitution string , mapping Mapping ) (string , bool , error ) {
291- return withRequired (substitution , mapping , ":?" , func (v string ) bool { return v != "" })
292- }
293-
294- func required (substitution string , mapping Mapping ) (string , bool , error ) {
295- return withRequired (substitution , mapping , "?" , func (_ string ) bool { return true })
296- }
297-
298311func withRequired (substitution string , mapping Mapping , sep string , valid func (string ) bool ) (string , bool , error ) {
299312 if ! strings .Contains (substitution , sep ) {
300313 return "" , false , nil
0 commit comments