github.com/kunnos/engine@v1.13.1/cli/compose/template/template.go (about) 1 package template 2 3 import ( 4 "fmt" 5 "regexp" 6 "strings" 7 ) 8 9 var delimiter = "\\$" 10 var substitution = "[_a-z][_a-z0-9]*(?::?-[^}]+)?" 11 12 var patternString = fmt.Sprintf( 13 "%s(?i:(?P<escaped>%s)|(?P<named>%s)|{(?P<braced>%s)}|(?P<invalid>))", 14 delimiter, delimiter, substitution, substitution, 15 ) 16 17 var pattern = regexp.MustCompile(patternString) 18 19 // InvalidTemplateError is returned when a variable template is not in a valid 20 // format 21 type InvalidTemplateError struct { 22 Template string 23 } 24 25 func (e InvalidTemplateError) Error() string { 26 return fmt.Sprintf("Invalid template: %#v", e.Template) 27 } 28 29 // Mapping is a user-supplied function which maps from variable names to values. 30 // Returns the value as a string and a bool indicating whether 31 // the value is present, to distinguish between an empty string 32 // and the absence of a value. 33 type Mapping func(string) (string, bool) 34 35 // Substitute variables in the string with their values 36 func Substitute(template string, mapping Mapping) (result string, err *InvalidTemplateError) { 37 result = pattern.ReplaceAllStringFunc(template, func(substring string) string { 38 matches := pattern.FindStringSubmatch(substring) 39 groups := make(map[string]string) 40 for i, name := range pattern.SubexpNames() { 41 if i != 0 { 42 groups[name] = matches[i] 43 } 44 } 45 46 substitution := groups["named"] 47 if substitution == "" { 48 substitution = groups["braced"] 49 } 50 if substitution != "" { 51 // Soft default (fall back if unset or empty) 52 if strings.Contains(substitution, ":-") { 53 name, defaultValue := partition(substitution, ":-") 54 value, ok := mapping(name) 55 if !ok || value == "" { 56 return defaultValue 57 } 58 return value 59 } 60 61 // Hard default (fall back if-and-only-if empty) 62 if strings.Contains(substitution, "-") { 63 name, defaultValue := partition(substitution, "-") 64 value, ok := mapping(name) 65 if !ok { 66 return defaultValue 67 } 68 return value 69 } 70 71 // No default (fall back to empty string) 72 value, ok := mapping(substitution) 73 if !ok { 74 return "" 75 } 76 return value 77 } 78 79 if escaped := groups["escaped"]; escaped != "" { 80 return escaped 81 } 82 83 err = &InvalidTemplateError{Template: template} 84 return "" 85 }) 86 87 return result, err 88 } 89 90 // Split the string at the first occurrence of sep, and return the part before the separator, 91 // and the part after the separator. 92 // 93 // If the separator is not found, return the string itself, followed by an empty string. 94 func partition(s, sep string) (string, string) { 95 if strings.Contains(s, sep) { 96 parts := strings.SplitN(s, sep, 2) 97 return parts[0], parts[1] 98 } 99 return s, "" 100 }