github.com/valdemarpavesi/helm@v2.9.1+incompatible/pkg/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, false) 49 err := t.parse() 50 return vals, err 51 } 52 53 // ParseString parses a set line and forces a string value. 54 // 55 // A set line is of the form name1=value1,name2=value2 56 func ParseString(s string) (map[string]interface{}, error) { 57 vals := map[string]interface{}{} 58 scanner := bytes.NewBufferString(s) 59 t := newParser(scanner, vals, true) 60 err := t.parse() 61 return vals, err 62 } 63 64 // ParseInto parses a strvals line and merges the result into dest. 65 // 66 // If the strval string has a key that exists in dest, it overwrites the 67 // dest version. 68 func ParseInto(s string, dest map[string]interface{}) error { 69 scanner := bytes.NewBufferString(s) 70 t := newParser(scanner, dest, false) 71 return t.parse() 72 } 73 74 // ParseIntoString parses a strvals line nad merges the result into dest. 75 // 76 // This method always returns a string as the value. 77 func ParseIntoString(s string, dest map[string]interface{}) error { 78 scanner := bytes.NewBufferString(s) 79 t := newParser(scanner, dest, true) 80 return t.parse() 81 } 82 83 // parser is a simple parser that takes a strvals line and parses it into a 84 // map representation. 85 type parser struct { 86 sc *bytes.Buffer 87 data map[string]interface{} 88 st bool 89 } 90 91 func newParser(sc *bytes.Buffer, data map[string]interface{}, stringBool bool) *parser { 92 return &parser{sc: sc, data: data, st: stringBool} 93 } 94 95 func (t *parser) parse() error { 96 for { 97 err := t.key(t.data) 98 if err == nil { 99 continue 100 } 101 if err == io.EOF { 102 return nil 103 } 104 return err 105 } 106 } 107 108 func runeSet(r []rune) map[rune]bool { 109 s := make(map[rune]bool, len(r)) 110 for _, rr := range r { 111 s[rr] = true 112 } 113 return s 114 } 115 116 func (t *parser) key(data map[string]interface{}) error { 117 stop := runeSet([]rune{'=', '[', ',', '.'}) 118 for { 119 switch k, last, err := runesUntil(t.sc, stop); { 120 case err != nil: 121 if len(k) == 0 { 122 return err 123 } 124 return fmt.Errorf("key %q has no value", string(k)) 125 //set(data, string(k), "") 126 //return err 127 case last == '[': 128 // We are in a list index context, so we need to set an index. 129 i, err := t.keyIndex() 130 if err != nil { 131 return fmt.Errorf("error parsing index: %s", err) 132 } 133 kk := string(k) 134 // Find or create target list 135 list := []interface{}{} 136 if _, ok := data[kk]; ok { 137 list = data[kk].([]interface{}) 138 } 139 140 // Now we need to get the value after the ]. 141 list, err = t.listItem(list, i) 142 set(data, kk, list) 143 return err 144 case last == '=': 145 //End of key. Consume =, Get value. 146 // FIXME: Get value list first 147 vl, e := t.valList() 148 switch e { 149 case nil: 150 set(data, string(k), vl) 151 return nil 152 case io.EOF: 153 set(data, string(k), "") 154 return e 155 case ErrNotList: 156 v, e := t.val() 157 set(data, string(k), typedVal(v, t.st)) 158 return e 159 default: 160 return e 161 } 162 163 case last == ',': 164 // No value given. Set the value to empty string. Return error. 165 set(data, string(k), "") 166 return fmt.Errorf("key %q has no value (cannot end with ,)", string(k)) 167 case last == '.': 168 // First, create or find the target map. 169 inner := map[string]interface{}{} 170 if _, ok := data[string(k)]; ok { 171 inner = data[string(k)].(map[string]interface{}) 172 } 173 174 // Recurse 175 e := t.key(inner) 176 if len(inner) == 0 { 177 return fmt.Errorf("key map %q has no value", string(k)) 178 } 179 set(data, string(k), inner) 180 return e 181 } 182 } 183 } 184 185 func set(data map[string]interface{}, key string, val interface{}) { 186 // If key is empty, don't set it. 187 if len(key) == 0 { 188 return 189 } 190 data[key] = val 191 } 192 193 func setIndex(list []interface{}, index int, val interface{}) []interface{} { 194 if len(list) <= index { 195 newlist := make([]interface{}, index+1) 196 copy(newlist, list) 197 list = newlist 198 } 199 list[index] = val 200 return list 201 } 202 203 func (t *parser) keyIndex() (int, error) { 204 // First, get the key. 205 stop := runeSet([]rune{']'}) 206 v, _, err := runesUntil(t.sc, stop) 207 if err != nil { 208 return 0, err 209 } 210 // v should be the index 211 return strconv.Atoi(string(v)) 212 213 } 214 func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { 215 stop := runeSet([]rune{'[', '.', '='}) 216 switch k, last, err := runesUntil(t.sc, stop); { 217 case len(k) > 0: 218 return list, fmt.Errorf("unexpected data at end of array index: %q", k) 219 case err != nil: 220 return list, err 221 case last == '=': 222 vl, e := t.valList() 223 switch e { 224 case nil: 225 return setIndex(list, i, vl), nil 226 case io.EOF: 227 return setIndex(list, i, ""), err 228 case ErrNotList: 229 v, e := t.val() 230 return setIndex(list, i, typedVal(v, t.st)), e 231 default: 232 return list, e 233 } 234 case last == '[': 235 // now we have a nested list. Read the index and handle. 236 i, err := t.keyIndex() 237 if err != nil { 238 return list, fmt.Errorf("error parsing index: %s", err) 239 } 240 // Now we need to get the value after the ]. 241 list2, err := t.listItem(list, i) 242 return setIndex(list, i, list2), err 243 case last == '.': 244 // We have a nested object. Send to t.key 245 inner := map[string]interface{}{} 246 if len(list) > i { 247 inner = list[i].(map[string]interface{}) 248 } 249 250 // Recurse 251 e := t.key(inner) 252 return setIndex(list, i, inner), e 253 default: 254 return nil, fmt.Errorf("parse error: unexpected token %v", last) 255 } 256 } 257 258 func (t *parser) val() ([]rune, error) { 259 stop := runeSet([]rune{','}) 260 v, _, err := runesUntil(t.sc, stop) 261 return v, err 262 } 263 264 func (t *parser) valList() ([]interface{}, error) { 265 r, _, e := t.sc.ReadRune() 266 if e != nil { 267 return []interface{}{}, e 268 } 269 270 if r != '{' { 271 t.sc.UnreadRune() 272 return []interface{}{}, ErrNotList 273 } 274 275 list := []interface{}{} 276 stop := runeSet([]rune{',', '}'}) 277 for { 278 switch v, last, err := runesUntil(t.sc, stop); { 279 case err != nil: 280 if err == io.EOF { 281 err = errors.New("list must terminate with '}'") 282 } 283 return list, err 284 case last == '}': 285 // If this is followed by ',', consume it. 286 if r, _, e := t.sc.ReadRune(); e == nil && r != ',' { 287 t.sc.UnreadRune() 288 } 289 list = append(list, typedVal(v, t.st)) 290 return list, nil 291 case last == ',': 292 list = append(list, typedVal(v, t.st)) 293 } 294 } 295 } 296 297 func runesUntil(in io.RuneReader, stop map[rune]bool) ([]rune, rune, error) { 298 v := []rune{} 299 for { 300 switch r, _, e := in.ReadRune(); { 301 case e != nil: 302 return v, r, e 303 case inMap(r, stop): 304 return v, r, nil 305 case r == '\\': 306 next, _, e := in.ReadRune() 307 if e != nil { 308 return v, next, e 309 } 310 v = append(v, next) 311 default: 312 v = append(v, r) 313 } 314 } 315 } 316 317 func inMap(k rune, m map[rune]bool) bool { 318 _, ok := m[k] 319 return ok 320 } 321 322 func typedVal(v []rune, st bool) interface{} { 323 val := string(v) 324 if strings.EqualFold(val, "true") { 325 return true 326 } 327 328 if strings.EqualFold(val, "false") { 329 return false 330 } 331 332 // If this value does not start with zero, and not returnString, try parsing it to an int 333 if !st && len(val) != 0 && val[0] != '0' { 334 if iv, err := strconv.ParseInt(val, 10, 64); err == nil { 335 return iv 336 } 337 } 338 339 return val 340 }