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