github.com/felipejfc/helm@v2.1.2+incompatible/cmd/helm/strvals/parser.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package strvals 17 18 import ( 19 "bytes" 20 "errors" 21 "fmt" 22 "io" 23 "strconv" 24 "strings" 25 26 "github.com/ghodss/yaml" 27 ) 28 29 // ErrNotList indicates that a non-list was treated as a list. 30 var ErrNotList = errors.New("not a list") 31 32 // ToYAML takes a string of arguments and converts to a YAML document. 33 func ToYAML(s string) (string, error) { 34 m, err := Parse(s) 35 if err != nil { 36 return "", err 37 } 38 d, err := yaml.Marshal(m) 39 return string(d), err 40 } 41 42 // Parse parses a set line. 43 // 44 // A set line is of the form name1=value1,name2=value2 45 func Parse(s string) (map[string]interface{}, error) { 46 vals := map[string]interface{}{} 47 scanner := bytes.NewBufferString(s) 48 t := newParser(scanner, vals) 49 err := t.parse() 50 return vals, err 51 } 52 53 //ParseInto parses a strvals line and merges the result into dest. 54 // 55 // If the strval string has a key that exists in dest, it overwrites the 56 // dest version. 57 func ParseInto(s string, dest map[string]interface{}) error { 58 scanner := bytes.NewBufferString(s) 59 t := newParser(scanner, dest) 60 return t.parse() 61 } 62 63 // parser is a simple parser that takes a strvals line and parses it into a 64 // map representation. 65 type parser struct { 66 sc *bytes.Buffer 67 data map[string]interface{} 68 } 69 70 func newParser(sc *bytes.Buffer, data map[string]interface{}) *parser { 71 return &parser{sc: sc, data: data} 72 } 73 74 func (t *parser) parse() error { 75 for { 76 err := t.key(t.data) 77 if err == nil { 78 continue 79 } 80 if err == io.EOF { 81 return nil 82 } 83 return err 84 } 85 } 86 87 func runeSet(r []rune) map[rune]bool { 88 s := make(map[rune]bool, len(r)) 89 for _, rr := range r { 90 s[rr] = true 91 } 92 return s 93 } 94 95 func (t *parser) key(data map[string]interface{}) error { 96 stop := runeSet([]rune{'=', ',', '.'}) 97 for { 98 switch k, last, err := runesUntil(t.sc, stop); { 99 case err != nil: 100 if len(k) == 0 { 101 return err 102 } 103 return fmt.Errorf("key %q has no value", string(k)) 104 //set(data, string(k), "") 105 //return err 106 case last == '=': 107 //End of key. Consume =, Get value. 108 // FIXME: Get value list first 109 vl, e := t.valList() 110 switch e { 111 case nil: 112 set(data, string(k), vl) 113 return nil 114 case io.EOF: 115 set(data, string(k), "") 116 return e 117 case ErrNotList: 118 v, e := t.val() 119 set(data, string(k), typedVal(v)) 120 return e 121 default: 122 return e 123 } 124 125 case last == ',': 126 // No value given. Set the value to empty string. Return error. 127 set(data, string(k), "") 128 return fmt.Errorf("key %q has no value (cannot end with ,)", string(k)) 129 case last == '.': 130 // First, create or find the target map. 131 inner := map[string]interface{}{} 132 if _, ok := data[string(k)]; ok { 133 inner = data[string(k)].(map[string]interface{}) 134 } 135 136 // Recurse 137 e := t.key(inner) 138 if len(inner) == 0 { 139 return fmt.Errorf("key map %q has no value", string(k)) 140 } 141 set(data, string(k), inner) 142 return e 143 } 144 } 145 } 146 147 func set(data map[string]interface{}, key string, val interface{}) { 148 // If key is empty, don't set it. 149 if len(key) == 0 { 150 return 151 } 152 data[key] = val 153 } 154 155 func (t *parser) val() ([]rune, error) { 156 stop := runeSet([]rune{','}) 157 v, _, err := runesUntil(t.sc, stop) 158 return v, err 159 } 160 161 func (t *parser) valList() ([]interface{}, error) { 162 r, _, e := t.sc.ReadRune() 163 if e != nil { 164 return []interface{}{}, e 165 } 166 167 if r != '{' { 168 t.sc.UnreadRune() 169 return []interface{}{}, ErrNotList 170 } 171 172 list := []interface{}{} 173 stop := runeSet([]rune{',', '}'}) 174 for { 175 switch v, last, err := runesUntil(t.sc, stop); { 176 case err != nil: 177 if err == io.EOF { 178 err = errors.New("list must terminate with '}'") 179 } 180 return list, err 181 case last == '}': 182 // If this is followed by ',', consume it. 183 if r, _, e := t.sc.ReadRune(); e == nil && r != ',' { 184 t.sc.UnreadRune() 185 } 186 list = append(list, typedVal(v)) 187 return list, nil 188 case last == ',': 189 list = append(list, typedVal(v)) 190 } 191 } 192 } 193 194 func runesUntil(in io.RuneReader, stop map[rune]bool) ([]rune, rune, error) { 195 v := []rune{} 196 for { 197 switch r, _, e := in.ReadRune(); { 198 case e != nil: 199 return v, r, e 200 case inMap(r, stop): 201 return v, r, nil 202 case r == '\\': 203 next, _, e := in.ReadRune() 204 if e != nil { 205 return v, next, e 206 } 207 v = append(v, next) 208 default: 209 v = append(v, r) 210 } 211 } 212 } 213 214 func inMap(k rune, m map[rune]bool) bool { 215 _, ok := m[k] 216 return ok 217 } 218 219 func (t *parser) listVal() []rune { 220 v := []rune{} 221 for { 222 switch r, _, e := t.sc.ReadRune(); { 223 case e != nil: 224 // End of input or error with reader stops value parsing. 225 return v 226 case r == '\\': 227 //Escape char. Consume next and append. 228 next, _, e := t.sc.ReadRune() 229 if e != nil { 230 return v 231 } 232 v = append(v, next) 233 case r == ',': 234 //End of key. Consume ',' and return. 235 return v 236 default: 237 v = append(v, r) 238 } 239 } 240 } 241 242 func typedVal(v []rune) interface{} { 243 val := string(v) 244 if strings.EqualFold(val, "true") { 245 return true 246 } 247 248 if strings.EqualFold(val, "false") { 249 return false 250 } 251 252 if iv, err := strconv.ParseInt(val, 10, 64); err == nil { 253 return iv 254 } 255 256 return val 257 }