github.com/zoumo/helm@v2.5.0+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)
    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  			// We are in a list index context, so we need to set an index.
   108  			i, err := t.keyIndex()
   109  			if err != nil {
   110  				return fmt.Errorf("error parsing index: %s", err)
   111  			}
   112  			kk := string(k)
   113  			// Find or create target list
   114  			list := []interface{}{}
   115  			if _, ok := data[kk]; ok {
   116  				list = data[kk].([]interface{})
   117  			}
   118  
   119  			// Now we need to get the value after the ].
   120  			list, err = t.listItem(list, i)
   121  			set(data, kk, list)
   122  			return err
   123  		case last == '=':
   124  			//End of key. Consume =, Get value.
   125  			// FIXME: Get value list first
   126  			vl, e := t.valList()
   127  			switch e {
   128  			case nil:
   129  				set(data, string(k), vl)
   130  				return nil
   131  			case io.EOF:
   132  				set(data, string(k), "")
   133  				return e
   134  			case ErrNotList:
   135  				v, e := t.val()
   136  				set(data, string(k), typedVal(v))
   137  				return e
   138  			default:
   139  				return e
   140  			}
   141  
   142  		case last == ',':
   143  			// No value given. Set the value to empty string. Return error.
   144  			set(data, string(k), "")
   145  			return fmt.Errorf("key %q has no value (cannot end with ,)", string(k))
   146  		case last == '.':
   147  			// First, create or find the target map.
   148  			inner := map[string]interface{}{}
   149  			if _, ok := data[string(k)]; ok {
   150  				inner = data[string(k)].(map[string]interface{})
   151  			}
   152  
   153  			// Recurse
   154  			e := t.key(inner)
   155  			if len(inner) == 0 {
   156  				return fmt.Errorf("key map %q has no value", string(k))
   157  			}
   158  			set(data, string(k), inner)
   159  			return e
   160  		}
   161  	}
   162  }
   163  
   164  func set(data map[string]interface{}, key string, val interface{}) {
   165  	// If key is empty, don't set it.
   166  	if len(key) == 0 {
   167  		return
   168  	}
   169  	data[key] = val
   170  }
   171  
   172  func setIndex(list []interface{}, index int, val interface{}) []interface{} {
   173  	if len(list) <= index {
   174  		newlist := make([]interface{}, index+1)
   175  		copy(newlist, list)
   176  		list = newlist
   177  	}
   178  	list[index] = val
   179  	return list
   180  }
   181  
   182  func (t *parser) keyIndex() (int, error) {
   183  	// First, get the key.
   184  	stop := runeSet([]rune{']'})
   185  	v, _, err := runesUntil(t.sc, stop)
   186  	if err != nil {
   187  		return 0, err
   188  	}
   189  	// v should be the index
   190  	return strconv.Atoi(string(v))
   191  
   192  }
   193  func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) {
   194  	stop := runeSet([]rune{'[', '.', '='})
   195  	switch k, last, err := runesUntil(t.sc, stop); {
   196  	case len(k) > 0:
   197  		return list, fmt.Errorf("unexpected data at end of array index: %q", k)
   198  	case err != nil:
   199  		return list, err
   200  	case last == '=':
   201  		vl, e := t.valList()
   202  		switch e {
   203  		case nil:
   204  			return setIndex(list, i, vl), nil
   205  		case io.EOF:
   206  			return setIndex(list, i, ""), err
   207  		case ErrNotList:
   208  			v, e := t.val()
   209  			return setIndex(list, i, typedVal(v)), e
   210  		default:
   211  			return list, e
   212  		}
   213  	case last == '[':
   214  		// now we have a nested list. Read the index and handle.
   215  		i, err := t.keyIndex()
   216  		if err != nil {
   217  			return list, fmt.Errorf("error parsing index: %s", err)
   218  		}
   219  		// Now we need to get the value after the ].
   220  		list2, err := t.listItem(list, i)
   221  		return setIndex(list, i, list2), err
   222  	case last == '.':
   223  		// We have a nested object. Send to t.key
   224  		inner := map[string]interface{}{}
   225  		if len(list) > i {
   226  			inner = list[i].(map[string]interface{})
   227  		}
   228  
   229  		// Recurse
   230  		e := t.key(inner)
   231  		return setIndex(list, i, inner), e
   232  	default:
   233  		return nil, fmt.Errorf("parse error: unexpected token %v", last)
   234  	}
   235  }
   236  
   237  func (t *parser) val() ([]rune, error) {
   238  	stop := runeSet([]rune{','})
   239  	v, _, err := runesUntil(t.sc, stop)
   240  	return v, err
   241  }
   242  
   243  func (t *parser) valList() ([]interface{}, error) {
   244  	r, _, e := t.sc.ReadRune()
   245  	if e != nil {
   246  		return []interface{}{}, e
   247  	}
   248  
   249  	if r != '{' {
   250  		t.sc.UnreadRune()
   251  		return []interface{}{}, ErrNotList
   252  	}
   253  
   254  	list := []interface{}{}
   255  	stop := runeSet([]rune{',', '}'})
   256  	for {
   257  		switch v, last, err := runesUntil(t.sc, stop); {
   258  		case err != nil:
   259  			if err == io.EOF {
   260  				err = errors.New("list must terminate with '}'")
   261  			}
   262  			return list, err
   263  		case last == '}':
   264  			// If this is followed by ',', consume it.
   265  			if r, _, e := t.sc.ReadRune(); e == nil && r != ',' {
   266  				t.sc.UnreadRune()
   267  			}
   268  			list = append(list, typedVal(v))
   269  			return list, nil
   270  		case last == ',':
   271  			list = append(list, typedVal(v))
   272  		}
   273  	}
   274  }
   275  
   276  func runesUntil(in io.RuneReader, stop map[rune]bool) ([]rune, rune, error) {
   277  	v := []rune{}
   278  	for {
   279  		switch r, _, e := in.ReadRune(); {
   280  		case e != nil:
   281  			return v, r, e
   282  		case inMap(r, stop):
   283  			return v, r, nil
   284  		case r == '\\':
   285  			next, _, e := in.ReadRune()
   286  			if e != nil {
   287  				return v, next, e
   288  			}
   289  			v = append(v, next)
   290  		default:
   291  			v = append(v, r)
   292  		}
   293  	}
   294  }
   295  
   296  func inMap(k rune, m map[rune]bool) bool {
   297  	_, ok := m[k]
   298  	return ok
   299  }
   300  
   301  func typedVal(v []rune) interface{} {
   302  	val := string(v)
   303  	if strings.EqualFold(val, "true") {
   304  		return true
   305  	}
   306  
   307  	if strings.EqualFold(val, "false") {
   308  		return false
   309  	}
   310  
   311  	if iv, err := strconv.ParseInt(val, 10, 64); err == nil {
   312  		return iv
   313  	}
   314  
   315  	return val
   316  }