github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/strvals/parser.go (about) 1 /* 2 Copyright The Helm Authors. 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 "encoding/json" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "strconv" 25 "strings" 26 "unicode" 27 28 "github.com/pkg/errors" 29 "sigs.k8s.io/yaml" 30 ) 31 32 // ErrNotList indicates that a non-list was treated as a list. 33 var ErrNotList = errors.New("not a list") 34 35 // MaxIndex is the maximum index that will be allowed by setIndex. 36 // The default value 65536 = 1024 * 64 37 var MaxIndex = 65536 38 39 // ToYAML takes a string of arguments and converts to a YAML document. 40 func ToYAML(s string) (string, error) { 41 m, err := Parse(s) 42 if err != nil { 43 return "", err 44 } 45 d, err := yaml.Marshal(m) 46 return strings.TrimSuffix(string(d), "\n"), err 47 } 48 49 // Parse parses a set line. 50 // 51 // A set line is of the form name1=value1,name2=value2 52 func Parse(s string) (map[string]interface{}, error) { 53 vals := map[string]interface{}{} 54 scanner := bytes.NewBufferString(s) 55 t := newParser(scanner, vals, false) 56 err := t.parse() 57 return vals, err 58 } 59 60 // ParseString parses a set line and forces a string value. 61 // 62 // A set line is of the form name1=value1,name2=value2 63 func ParseString(s string) (map[string]interface{}, error) { 64 vals := map[string]interface{}{} 65 scanner := bytes.NewBufferString(s) 66 t := newParser(scanner, vals, true) 67 err := t.parse() 68 return vals, err 69 } 70 71 // ParseInto parses a strvals line and merges the result into dest. 72 // 73 // If the strval string has a key that exists in dest, it overwrites the 74 // dest version. 75 func ParseInto(s string, dest map[string]interface{}) error { 76 scanner := bytes.NewBufferString(s) 77 t := newParser(scanner, dest, false) 78 return t.parse() 79 } 80 81 // ParseFile parses a set line, but its final value is loaded from the file at the path specified by the original value. 82 // 83 // A set line is of the form name1=path1,name2=path2 84 // 85 // When the files at path1 and path2 contained "val1" and "val2" respectively, the set line is consumed as 86 // name1=val1,name2=val2 87 func ParseFile(s string, reader RunesValueReader) (map[string]interface{}, error) { 88 vals := map[string]interface{}{} 89 scanner := bytes.NewBufferString(s) 90 t := newFileParser(scanner, vals, reader) 91 err := t.parse() 92 return vals, err 93 } 94 95 // ParseIntoString parses a strvals line and merges the result into dest. 96 // 97 // This method always returns a string as the value. 98 func ParseIntoString(s string, dest map[string]interface{}) error { 99 scanner := bytes.NewBufferString(s) 100 t := newParser(scanner, dest, true) 101 return t.parse() 102 } 103 104 // ParseJSON parses a string with format key1=val1, key2=val2, ... 105 // where values are json strings (null, or scalars, or arrays, or objects). 106 // An empty val is treated as null. 107 // 108 // If a key exists in dest, the new value overwrites the dest version. 109 // 110 func ParseJSON(s string, dest map[string]interface{}) error { 111 scanner := bytes.NewBufferString(s) 112 t := newJSONParser(scanner, dest) 113 return t.parse() 114 } 115 116 // ParseIntoFile parses a filevals line and merges the result into dest. 117 // 118 // This method always returns a string as the value. 119 func ParseIntoFile(s string, dest map[string]interface{}, reader RunesValueReader) error { 120 scanner := bytes.NewBufferString(s) 121 t := newFileParser(scanner, dest, reader) 122 return t.parse() 123 } 124 125 // RunesValueReader is a function that takes the given value (a slice of runes) 126 // and returns the parsed value 127 type RunesValueReader func([]rune) (interface{}, error) 128 129 // parser is a simple parser that takes a strvals line and parses it into a 130 // map representation. 131 // 132 // where sc is the source of the original data being parsed 133 // where data is the final parsed data from the parses with correct types 134 type parser struct { 135 sc *bytes.Buffer 136 data map[string]interface{} 137 reader RunesValueReader 138 isjsonval bool 139 } 140 141 func newParser(sc *bytes.Buffer, data map[string]interface{}, stringBool bool) *parser { 142 stringConverter := func(rs []rune) (interface{}, error) { 143 return typedVal(rs, stringBool), nil 144 } 145 return &parser{sc: sc, data: data, reader: stringConverter} 146 } 147 148 func newJSONParser(sc *bytes.Buffer, data map[string]interface{}) *parser { 149 return &parser{sc: sc, data: data, reader: nil, isjsonval: true} 150 } 151 152 func newFileParser(sc *bytes.Buffer, data map[string]interface{}, reader RunesValueReader) *parser { 153 return &parser{sc: sc, data: data, reader: reader} 154 } 155 156 func (t *parser) parse() error { 157 for { 158 err := t.key(t.data) 159 if err == nil { 160 continue 161 } 162 if err == io.EOF { 163 return nil 164 } 165 return err 166 } 167 } 168 169 func runeSet(r []rune) map[rune]bool { 170 s := make(map[rune]bool, len(r)) 171 for _, rr := range r { 172 s[rr] = true 173 } 174 return s 175 } 176 177 func (t *parser) key(data map[string]interface{}) (reterr error) { 178 defer func() { 179 if r := recover(); r != nil { 180 reterr = fmt.Errorf("unable to parse key: %s", r) 181 } 182 }() 183 stop := runeSet([]rune{'=', '[', ',', '.'}) 184 for { 185 switch k, last, err := runesUntil(t.sc, stop); { 186 case err != nil: 187 if len(k) == 0 { 188 return err 189 } 190 return errors.Errorf("key %q has no value", string(k)) 191 //set(data, string(k), "") 192 //return err 193 case last == '[': 194 // We are in a list index context, so we need to set an index. 195 i, err := t.keyIndex() 196 if err != nil { 197 return errors.Wrap(err, "error parsing index") 198 } 199 kk := string(k) 200 // Find or create target list 201 list := []interface{}{} 202 if _, ok := data[kk]; ok { 203 list = data[kk].([]interface{}) 204 } 205 206 // Now we need to get the value after the ]. 207 list, err = t.listItem(list, i) 208 set(data, kk, list) 209 return err 210 case last == '=': 211 if t.isjsonval { 212 empval, err := t.emptyVal() 213 if err != nil { 214 return err 215 } 216 if empval { 217 set(data, string(k), nil) 218 return nil 219 } 220 // parse jsonvals by using Go’s JSON standard library 221 // Decode is preferred to Unmarshal in order to parse just the json parts of the list key1=jsonval1,key2=jsonval2,... 222 // Since Decode has its own buffer that consumes more characters (from underlying t.sc) than the ones actually decoded, 223 // we invoke Decode on a separate reader built with a copy of what is left in t.sc. After Decode is executed, we 224 // discard in t.sc the chars of the decoded json value (the number of those characters is returned by InputOffset). 225 var jsonval interface{} 226 dec := json.NewDecoder(strings.NewReader(t.sc.String())) 227 if err = dec.Decode(&jsonval); err != nil { 228 return err 229 } 230 set(data, string(k), jsonval) 231 if _, err = io.CopyN(ioutil.Discard, t.sc, dec.InputOffset()); err != nil { 232 return err 233 } 234 // skip possible blanks and comma 235 _, err = t.emptyVal() 236 return err 237 } 238 //End of key. Consume =, Get value. 239 // FIXME: Get value list first 240 vl, e := t.valList() 241 switch e { 242 case nil: 243 set(data, string(k), vl) 244 return nil 245 case io.EOF: 246 set(data, string(k), "") 247 return e 248 case ErrNotList: 249 rs, e := t.val() 250 if e != nil && e != io.EOF { 251 return e 252 } 253 v, e := t.reader(rs) 254 set(data, string(k), v) 255 return e 256 default: 257 return e 258 } 259 case last == ',': 260 // No value given. Set the value to empty string. Return error. 261 set(data, string(k), "") 262 return errors.Errorf("key %q has no value (cannot end with ,)", string(k)) 263 case last == '.': 264 // First, create or find the target map. 265 inner := map[string]interface{}{} 266 if _, ok := data[string(k)]; ok { 267 inner = data[string(k)].(map[string]interface{}) 268 } 269 270 // Recurse 271 e := t.key(inner) 272 if len(inner) == 0 { 273 return errors.Errorf("key map %q has no value", string(k)) 274 } 275 set(data, string(k), inner) 276 return e 277 } 278 } 279 } 280 281 func set(data map[string]interface{}, key string, val interface{}) { 282 // If key is empty, don't set it. 283 if len(key) == 0 { 284 return 285 } 286 data[key] = val 287 } 288 289 func setIndex(list []interface{}, index int, val interface{}) (l2 []interface{}, err error) { 290 // There are possible index values that are out of range on a target system 291 // causing a panic. This will catch the panic and return an error instead. 292 // The value of the index that causes a panic varies from system to system. 293 defer func() { 294 if r := recover(); r != nil { 295 err = fmt.Errorf("error processing index %d: %s", index, r) 296 } 297 }() 298 299 if index < 0 { 300 return list, fmt.Errorf("negative %d index not allowed", index) 301 } 302 if index > MaxIndex { 303 return list, fmt.Errorf("index of %d is greater than maximum supported index of %d", index, MaxIndex) 304 } 305 if len(list) <= index { 306 newlist := make([]interface{}, index+1) 307 copy(newlist, list) 308 list = newlist 309 } 310 list[index] = val 311 return list, nil 312 } 313 314 func (t *parser) keyIndex() (int, error) { 315 // First, get the key. 316 stop := runeSet([]rune{']'}) 317 v, _, err := runesUntil(t.sc, stop) 318 if err != nil { 319 return 0, err 320 } 321 // v should be the index 322 return strconv.Atoi(string(v)) 323 324 } 325 func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { 326 if i < 0 { 327 return list, fmt.Errorf("negative %d index not allowed", i) 328 } 329 stop := runeSet([]rune{'[', '.', '='}) 330 switch k, last, err := runesUntil(t.sc, stop); { 331 case len(k) > 0: 332 return list, errors.Errorf("unexpected data at end of array index: %q", k) 333 case err != nil: 334 return list, err 335 case last == '=': 336 if t.isjsonval { 337 empval, err := t.emptyVal() 338 if err != nil { 339 return list, err 340 } 341 if empval { 342 return setIndex(list, i, nil) 343 } 344 // parse jsonvals by using Go’s JSON standard library 345 // Decode is preferred to Unmarshal in order to parse just the json parts of the list key1=jsonval1,key2=jsonval2,... 346 // Since Decode has its own buffer that consumes more characters (from underlying t.sc) than the ones actually decoded, 347 // we invoke Decode on a separate reader built with a copy of what is left in t.sc. After Decode is executed, we 348 // discard in t.sc the chars of the decoded json value (the number of those characters is returned by InputOffset). 349 var jsonval interface{} 350 dec := json.NewDecoder(strings.NewReader(t.sc.String())) 351 if err = dec.Decode(&jsonval); err != nil { 352 return list, err 353 } 354 if list, err = setIndex(list, i, jsonval); err != nil { 355 return list, err 356 } 357 if _, err = io.CopyN(ioutil.Discard, t.sc, dec.InputOffset()); err != nil { 358 return list, err 359 } 360 // skip possible blanks and comma 361 _, err = t.emptyVal() 362 return list, err 363 } 364 vl, e := t.valList() 365 switch e { 366 case nil: 367 return setIndex(list, i, vl) 368 case io.EOF: 369 return setIndex(list, i, "") 370 case ErrNotList: 371 rs, e := t.val() 372 if e != nil && e != io.EOF { 373 return list, e 374 } 375 v, e := t.reader(rs) 376 if e != nil { 377 return list, e 378 } 379 return setIndex(list, i, v) 380 default: 381 return list, e 382 } 383 case last == '[': 384 // now we have a nested list. Read the index and handle. 385 nextI, err := t.keyIndex() 386 if err != nil { 387 return list, errors.Wrap(err, "error parsing index") 388 } 389 var crtList []interface{} 390 if len(list) > i { 391 // If nested list already exists, take the value of list to next cycle. 392 existed := list[i] 393 if existed != nil { 394 crtList = list[i].([]interface{}) 395 } 396 } 397 // Now we need to get the value after the ]. 398 list2, err := t.listItem(crtList, nextI) 399 if err != nil { 400 return list, err 401 } 402 return setIndex(list, i, list2) 403 case last == '.': 404 // We have a nested object. Send to t.key 405 inner := map[string]interface{}{} 406 if len(list) > i { 407 var ok bool 408 inner, ok = list[i].(map[string]interface{}) 409 if !ok { 410 // We have indices out of order. Initialize empty value. 411 list[i] = map[string]interface{}{} 412 inner = list[i].(map[string]interface{}) 413 } 414 } 415 416 // Recurse 417 e := t.key(inner) 418 if e != nil { 419 return list, e 420 } 421 return setIndex(list, i, inner) 422 default: 423 return nil, errors.Errorf("parse error: unexpected token %v", last) 424 } 425 } 426 427 // check for an empty value 428 // read and consume optional spaces until comma or EOF (empty val) or any other char (not empty val) 429 // comma and spaces are consumed, while any other char is not cosumed 430 func (t *parser) emptyVal() (bool, error) { 431 for { 432 r, _, e := t.sc.ReadRune() 433 if e == io.EOF { 434 return true, nil 435 } 436 if e != nil { 437 return false, e 438 } 439 if r == ',' { 440 return true, nil 441 } 442 if !unicode.IsSpace(r) { 443 t.sc.UnreadRune() 444 return false, nil 445 } 446 } 447 } 448 449 func (t *parser) val() ([]rune, error) { 450 stop := runeSet([]rune{','}) 451 v, _, err := runesUntil(t.sc, stop) 452 return v, err 453 } 454 455 func (t *parser) valList() ([]interface{}, error) { 456 r, _, e := t.sc.ReadRune() 457 if e != nil { 458 return []interface{}{}, e 459 } 460 461 if r != '{' { 462 t.sc.UnreadRune() 463 return []interface{}{}, ErrNotList 464 } 465 466 list := []interface{}{} 467 stop := runeSet([]rune{',', '}'}) 468 for { 469 switch rs, last, err := runesUntil(t.sc, stop); { 470 case err != nil: 471 if err == io.EOF { 472 err = errors.New("list must terminate with '}'") 473 } 474 return list, err 475 case last == '}': 476 // If this is followed by ',', consume it. 477 if r, _, e := t.sc.ReadRune(); e == nil && r != ',' { 478 t.sc.UnreadRune() 479 } 480 v, e := t.reader(rs) 481 list = append(list, v) 482 return list, e 483 case last == ',': 484 v, e := t.reader(rs) 485 if e != nil { 486 return list, e 487 } 488 list = append(list, v) 489 } 490 } 491 } 492 493 func runesUntil(in io.RuneReader, stop map[rune]bool) ([]rune, rune, error) { 494 v := []rune{} 495 for { 496 switch r, _, e := in.ReadRune(); { 497 case e != nil: 498 return v, r, e 499 case inMap(r, stop): 500 return v, r, nil 501 case r == '\\': 502 next, _, e := in.ReadRune() 503 if e != nil { 504 return v, next, e 505 } 506 v = append(v, next) 507 default: 508 v = append(v, r) 509 } 510 } 511 } 512 513 func inMap(k rune, m map[rune]bool) bool { 514 _, ok := m[k] 515 return ok 516 } 517 518 func typedVal(v []rune, st bool) interface{} { 519 val := string(v) 520 521 if st { 522 return val 523 } 524 525 if strings.EqualFold(val, "true") { 526 return true 527 } 528 529 if strings.EqualFold(val, "false") { 530 return false 531 } 532 533 if strings.EqualFold(val, "null") { 534 return nil 535 } 536 537 if strings.EqualFold(val, "0") { 538 return int64(0) 539 } 540 541 // If this value does not start with zero, try parsing it to an int 542 if len(val) != 0 && val[0] != '0' { 543 if iv, err := strconv.ParseInt(val, 10, 64); err == nil { 544 return iv 545 } 546 } 547 548 return val 549 }