Refactor substitute to reduce cyclo complexity

Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
Daniel Nephin 2018-02-21 15:16:12 -05:00 committed by Arash Deshmeh
parent 99ecf57c6c
commit ce544823b6
2 changed files with 53 additions and 49 deletions

View File

@ -37,77 +37,74 @@ func Substitute(template string, mapping Mapping) (string, error) {
var err error var err error
result := pattern.ReplaceAllStringFunc(template, func(substring string) string { result := pattern.ReplaceAllStringFunc(template, func(substring string) string {
matches := pattern.FindStringSubmatch(substring) matches := pattern.FindStringSubmatch(substring)
groups := make(map[string]string) groups := matchGroups(matches)
for i, name := range pattern.SubexpNames() { if escaped := groups["escaped"]; escaped != "" {
if i != 0 { return escaped
groups[name] = matches[i]
}
} }
substitution := groups["named"] substitution := groups["named"]
if substitution == "" { if substitution == "" {
substitution = groups["braced"] substitution = groups["braced"]
} }
if substitution != "" {
// Soft default (fall back if unset or empty)
if strings.Contains(substitution, ":-") {
name, defaultValue := partition(substitution, ":-")
value, ok := mapping(name)
if !ok || value == "" {
return defaultValue
}
return value
}
// Hard default (fall back if-and-only-if empty) switch {
if strings.Contains(substitution, "-") {
name, defaultValue := partition(substitution, "-")
value, ok := mapping(name)
if !ok {
return defaultValue
}
return value
}
if strings.Contains(substitution, ":?") { case substitution == "":
name, errorMessage := partition(substitution, ":?") err = &InvalidTemplateError{Template: template}
value, ok := mapping(name) return ""
if !ok || value == "" {
err = &InvalidTemplateError{Template: errorMessage}
return ""
}
return value
}
if strings.Contains(substitution, "?") { // Soft default (fall back if unset or empty)
name, errorMessage := partition(substitution, "?") case strings.Contains(substitution, ":-"):
value, ok := mapping(name) name, defaultValue := partition(substitution, ":-")
if !ok { value, ok := mapping(name)
err = &InvalidTemplateError{Template: errorMessage} if !ok || value == "" {
return "" return defaultValue
}
return value
} }
return value
// No default (fall back to empty string) // Hard default (fall back if-and-only-if empty)
value, ok := mapping(substitution) case strings.Contains(substitution, "-"):
name, defaultValue := partition(substitution, "-")
value, ok := mapping(name)
if !ok { if !ok {
return defaultValue
}
return value
case strings.Contains(substitution, ":?"):
name, errorMessage := partition(substitution, ":?")
value, ok := mapping(name)
if !ok || value == "" {
err = &InvalidTemplateError{Template: errorMessage}
return ""
}
return value
case strings.Contains(substitution, "?"):
name, errorMessage := partition(substitution, "?")
value, ok := mapping(name)
if !ok {
err = &InvalidTemplateError{Template: errorMessage}
return "" return ""
} }
return value return value
} }
if escaped := groups["escaped"]; escaped != "" { value, _ := mapping(substitution)
return escaped return value
}
err = &InvalidTemplateError{Template: template}
return ""
}) })
return result, err return result, err
} }
func matchGroups(matches []string) map[string]string {
groups := make(map[string]string)
for i, name := range pattern.SubexpNames()[1:] {
groups[name] = matches[i+1]
}
return groups
}
// Split the string at the first occurrence of sep, and return the part before the separator, // Split the string at the first occurrence of sep, and return the part before the separator,
// and the part after the separator. // and the part after the separator.
// //

View File

@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
var defaults = map[string]string{ var defaults = map[string]string{
@ -22,6 +23,12 @@ func TestEscaped(t *testing.T) {
assert.Equal(t, "${foo}", result) assert.Equal(t, "${foo}", result)
} }
func TestSubstituteNoMatch(t *testing.T) {
result, err := Substitute("foo", defaultMapping)
require.NoError(t, err)
require.Equal(t, "foo", result)
}
func TestInvalid(t *testing.T) { func TestInvalid(t *testing.T) {
invalidTemplates := []string{ invalidTemplates := []string{
"${", "${",