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  }