package envsubst import ( "strconv" "strings" "unicode" "unicode/utf8" "github.com/drone/envsubst/path" ) // defines a parameter substitution function. type substituteFunc func(string, ...string) string // toLen returns the length of string s. func toLen(s string, args ...string) string { return strconv.Itoa(len(s)) } // toLower returns a copy of the string s with all characters // mapped to their lower case. func toLower(s string, args ...string) string { return strings.ToLower(s) } // toUpper returns a copy of the string s with all characters // mapped to their upper case. func toUpper(s string, args ...string) string { return strings.ToUpper(s) } // toLowerFirst returns a copy of the string s with the first // character mapped to its lower case. func toLowerFirst(s string, args ...string) string { if s == "" { return s } r, n := utf8.DecodeRuneInString(s) return string(unicode.ToLower(r)) + s[n:] } // toUpperFirst returns a copy of the string s with the first // character mapped to its upper case. func toUpperFirst(s string, args ...string) string { if s == "" { return s } r, n := utf8.DecodeRuneInString(s) return string(unicode.ToUpper(r)) + s[n:] } // toDefault returns a copy of the string s if not empty, else // returns a concatenation of the args without a separator. func toDefault(s string, args ...string) string { if len(s) == 0 && len(args) > 0 { // don't use any separator s = strings.Join(args, "") } return s } // toSubstr returns a slice of the string s at the specified // length and position. func toSubstr(s string, args ...string) string { if len(args) == 0 { return s // should never happen } pos, err := strconv.Atoi(args[0]) if err != nil { // bash returns the string if the position // cannot be parsed. return s } if pos < 0 { // if pos is negative (counts from the end) add it // to length to get first character offset pos = len(s) + pos // if negative offset exceeds the length of the string // start from 0 if pos < 0 { pos = 0 } } if len(args) == 1 { if pos < len(s) { return s[pos:] } // if the position exceeds the length of the // string an empty string is returned return "" } length, err := strconv.Atoi(args[1]) if err != nil { // bash returns the string if the length // cannot be parsed. return s } if pos+length >= len(s) { if pos < len(s) { // if the position exceeds the length of the // string just return the rest of it like bash return s[pos:] } // if the position exceeds the length of the // string an empty string is returned return "" } return s[pos : pos+length] } // replaceAll returns a copy of the string s with all instances // of the substring replaced with the replacement string. func replaceAll(s string, args ...string) string { switch len(args) { case 0: return s case 1: return strings.Replace(s, args[0], "", -1) default: return strings.Replace(s, args[0], args[1], -1) } } // replaceFirst returns a copy of the string s with the first // instance of the substring replaced with the replacement string. func replaceFirst(s string, args ...string) string { switch len(args) { case 0: return s case 1: return strings.Replace(s, args[0], "", 1) default: return strings.Replace(s, args[0], args[1], 1) } } // replacePrefix returns a copy of the string s with the matching // prefix replaced with the replacement string. func replacePrefix(s string, args ...string) string { if len(args) != 2 { return s } if strings.HasPrefix(s, args[0]) { return strings.Replace(s, args[0], args[1], 1) } return s } // replaceSuffix returns a copy of the string s with the matching // suffix replaced with the replacement string. func replaceSuffix(s string, args ...string) string { if len(args) != 2 { return s } if strings.HasSuffix(s, args[0]) { s = strings.TrimSuffix(s, args[0]) s = s + args[1] } return s } // TODO func trimShortestPrefix(s string, args ...string) string { if len(args) != 0 { s = trimShortest(s, args[0]) } return s } func trimShortestSuffix(s string, args ...string) string { if len(args) != 0 { r := reverse(s) rarg := reverse(args[0]) s = reverse(trimShortest(r, rarg)) } return s } func trimLongestPrefix(s string, args ...string) string { if len(args) != 0 { s = trimLongest(s, args[0]) } return s } func trimLongestSuffix(s string, args ...string) string { if len(args) != 0 { r := reverse(s) rarg := reverse(args[0]) s = reverse(trimLongest(r, rarg)) } return s } func trimShortest(s, arg string) string { var shortestMatch string for i := 0; i < len(s); i++ { match, err := path.Match(arg, s[0:len(s)-i]) if err != nil { return s } if match { shortestMatch = s[0 : len(s)-i] } } if shortestMatch != "" { return strings.TrimPrefix(s, shortestMatch) } return s } func trimLongest(s, arg string) string { for i := 0; i < len(s); i++ { match, err := path.Match(arg, s[0:len(s)-i]) if err != nil { return s } if match { return strings.TrimPrefix(s, s[0:len(s)-i]) } } return s } func reverse(s string) string { r := []rune(s) for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] } return string(r) }