github.com/enmand/kubernetes@v1.2.0-alpha.0/third_party/golang/expansion/expand.go (about) 1 package expansion 2 3 import ( 4 "bytes" 5 ) 6 7 const ( 8 operator = '$' 9 referenceOpener = '(' 10 referenceCloser = ')' 11 ) 12 13 // syntaxWrap returns the input string wrapped the expansion syntax. 14 func syntaxWrap(input string) string { 15 return string(operator) + string(referenceOpener) + input + string(referenceCloser) 16 } 17 18 // MappingFuncFor returns a mapping function for use with Expand that 19 // implements the expansion semantics defined in the expansion spec; it 20 // returns the input string wrapped in the expansion syntax if no mapping 21 // for the input is found. 22 func MappingFuncFor(context ...map[string]string) func(string) string { 23 return func(input string) string { 24 for _, vars := range context { 25 val, ok := vars[input] 26 if ok { 27 return val 28 } 29 } 30 31 return syntaxWrap(input) 32 } 33 } 34 35 // Expand replaces variable references in the input string according to 36 // the expansion spec using the given mapping function to resolve the 37 // values of variables. 38 func Expand(input string, mapping func(string) string) string { 39 var buf bytes.Buffer 40 checkpoint := 0 41 for cursor := 0; cursor < len(input); cursor++ { 42 if input[cursor] == operator && cursor+1 < len(input) { 43 // Copy the portion of the input string since the last 44 // checkpoint into the buffer 45 buf.WriteString(input[checkpoint:cursor]) 46 47 // Attempt to read the variable name as defined by the 48 // syntax from the input string 49 read, isVar, advance := tryReadVariableName(input[cursor+1:]) 50 51 if isVar { 52 // We were able to read a variable name correctly; 53 // apply the mapping to the variable name and copy the 54 // bytes into the buffer 55 buf.WriteString(mapping(read)) 56 } else { 57 // Not a variable name; copy the read bytes into the buffer 58 buf.WriteString(read) 59 } 60 61 // Advance the cursor in the input string to account for 62 // bytes consumed to read the variable name expression 63 cursor += advance 64 65 // Advance the checkpoint in the input string 66 checkpoint = cursor + 1 67 } 68 } 69 70 // Return the buffer and any remaining unwritten bytes in the 71 // input string. 72 return buf.String() + input[checkpoint:] 73 } 74 75 // tryReadVariableName attempts to read a variable name from the input 76 // string and returns the content read from the input, whether that content 77 // represents a variable name to perform mapping on, and the number of bytes 78 // consumed in the input string. 79 // 80 // The input string is assumed not to contain the initial operator. 81 func tryReadVariableName(input string) (string, bool, int) { 82 switch input[0] { 83 case operator: 84 // Escaped operator; return it. 85 return input[0:1], false, 1 86 case referenceOpener: 87 // Scan to expression closer 88 for i := 1; i < len(input); i++ { 89 if input[i] == referenceCloser { 90 return input[1:i], true, i + 1 91 } 92 } 93 94 // Incomplete reference; return it. 95 return string(operator) + string(referenceOpener), false, 1 96 default: 97 // Not the beginning of an expression, ie, an operator 98 // that doesn't begin an expression. Return the operator 99 // and the first rune in the string. 100 return (string(operator) + string(input[0])), false, 1 101 } 102 }