text: package template
  
  import (
  	"fmt"
  	"regexp"
  	"strings"
  )
  
  var delimiter = "\\$"
  var substitution = "[_a-z][_a-z0-9]*(?::?-[^}]+)?"
  
  var patternString = fmt.Sprintf(
  	"%s(?i:(?P<escaped>%s)|(?P<named>%s)|{(?P<braced>%s)}|(?P<invalid>))",
  	delimiter, delimiter, substitution, substitution,
  )
  
  var pattern = regexp.MustCompile(patternString)
  
  // InvalidTemplateError is returned when a variable template is not in a valid
  // format
  type InvalidTemplateError struct {
  	Template string
  }
  
  func (e InvalidTemplateError) Error() string {
  	return fmt.Sprintf("Invalid template: %#v", e.Template)
  }
  
  // Mapping is a user-supplied function which maps from variable names to values.
  // Returns the value as a string and a bool indicating whether
  // the value is present, to distinguish between an empty string
  // and the absence of a value.
  type Mapping func(string) (string, bool)
  
  // Substitute variables in the string with their values
  func Substitute(template string, mapping Mapping) (result string, err *InvalidTemplateError) {
  	result = pattern.ReplaceAllStringFunc(template, func(substring string) string {
  		matches := pattern.FindStringSubmatch(substring)
  		groups := make(map[string]string)
  		for i, name := range pattern.SubexpNames() {
  			if i != 0 {
  				groups[name] = matches[i]
  			}
  		}
  
  		substitution := groups["named"]
  		if substitution == "" {
  			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)
  			if strings.Contains(substitution, "-") {
  				name, defaultValue := partition(substitution, "-")
  				value, ok := mapping(name)
  				if !ok {
  					return defaultValue
  				}
  				return value
  			}
  
  			// No default (fall back to empty string)
  			value, ok := mapping(substitution)
  			if !ok {
  				return ""
  			}
  			return value
  		}
  
  		if escaped := groups["escaped"]; escaped != "" {
  			return escaped
  		}
  
  		err = &InvalidTemplateError{Template: template}
  		return ""
  	})
  
  	return result, err
  }
  
  // Split the string at the first occurrence of sep, and return the part before the separator,
  // and the part after the separator.
  //
  // If the separator is not found, return the string itself, followed by an empty string.
  func partition(s, sep string) (string, string) {
  	if strings.Contains(s, sep) {
  		parts := strings.SplitN(s, sep, 2)
  		return parts[0], parts[1]
  	}
  	return s, ""
  }
  