github.com/lyraproj/hiera@v1.0.0-rc4/api/key.go (about)

     1  package api
     2  
     3  import (
     4  	"bytes"
     5  	"reflect"
     6  	"strconv"
     7  
     8  	"github.com/tada/catch"
     9  
    10  	"github.com/lyraproj/dgo/util"
    11  
    12  	"github.com/lyraproj/dgo/dgo"
    13  	"github.com/lyraproj/dgo/tf"
    14  	"github.com/lyraproj/dgo/vf"
    15  )
    16  
    17  // A Key is a parsed version of the possibly dot-separated key to lookup. The
    18  // parts of a key will be strings or integers
    19  type (
    20  	Key interface {
    21  		dgo.Value
    22  
    23  		// Return the result of using this key to dig into the given value. Nil is returned
    24  		// unless the dig was a success
    25  		Dig(Invocation, dgo.Value) dgo.Value
    26  
    27  		// Bury is the opposite of Dig. It returns the value that represents what would be found
    28  		// using the root of this key. If this key has one part, the value itself is returned, otherwise
    29  		// a nested chain of single entry hashes is returned.
    30  		Bury(dgo.Value) dgo.Value
    31  
    32  		// Return the parts of this key. Each part is either a string or an int value
    33  		Parts() []interface{}
    34  
    35  		// Return the root key, i.e. the first part.
    36  		Root() string
    37  
    38  		// Source returns the string that this key was created from
    39  		Source() string
    40  	}
    41  
    42  	key struct {
    43  		source string
    44  		parts  []interface{}
    45  	}
    46  )
    47  
    48  // NewKey parses the given string into a Key
    49  func NewKey(str string) Key {
    50  	b := bytes.NewBufferString(``)
    51  	return &key{str, parseUnquoted(b, str, str, []interface{}{})}
    52  }
    53  
    54  var keyType = tf.NewNamed(`hiera.key`,
    55  	func(v dgo.Value) dgo.Value {
    56  		return NewKey(v.String())
    57  	},
    58  	func(v dgo.Value) dgo.Value {
    59  		return vf.String(v.(*key).source)
    60  	},
    61  	reflect.TypeOf(&key{}),
    62  	reflect.TypeOf((*Key)(nil)).Elem(), nil)
    63  
    64  func (k *key) Bury(value dgo.Value) dgo.Value {
    65  	for i := len(k.parts) - 1; i > 0; i-- {
    66  		p := k.parts[i]
    67  		var kx dgo.Value
    68  		if ix, ok := p.(int); ok {
    69  			kx = vf.Int64(int64(ix))
    70  		} else {
    71  			kx = vf.String(p.(string))
    72  		}
    73  		value = vf.Map(kx, value)
    74  	}
    75  	return value
    76  }
    77  
    78  func (k *key) Dig(ic Invocation, v dgo.Value) dgo.Value {
    79  	t := len(k.parts)
    80  	if t == 1 {
    81  		return v
    82  	}
    83  
    84  	return ic.WithSubLookup(k, func() dgo.Value {
    85  		for i := 1; i < t; i++ {
    86  			p := k.parts[i]
    87  			v = ic.WithSegment(p, func() dgo.Value {
    88  				switch vc := v.(type) {
    89  				case dgo.Array:
    90  					if ix, ok := p.(int); ok {
    91  						if ix >= 0 && ix < vc.Len() {
    92  							v = vc.Get(ix)
    93  							ic.ReportFound(p, v)
    94  							return v
    95  						}
    96  					}
    97  				case dgo.Map:
    98  					var kx dgo.Value
    99  					if ix, ok := p.(int); ok {
   100  						kx = vf.Int64(int64(ix))
   101  					} else {
   102  						kx = vf.String(p.(string))
   103  					}
   104  					if v := vc.Get(kx); v != nil {
   105  						ic.ReportFound(p, v)
   106  						return v
   107  					}
   108  				}
   109  				ic.ReportNotFound(p)
   110  				return nil
   111  			})
   112  			if v == nil {
   113  				break
   114  			}
   115  		}
   116  		return v
   117  	})
   118  }
   119  
   120  func (k *key) Equals(value interface{}) bool {
   121  	if ov, ok := value.(*key); ok {
   122  		return k.source == ov.source
   123  	}
   124  	return false
   125  }
   126  
   127  func (k *key) HashCode() int32 {
   128  	return util.StringHash(k.source)
   129  }
   130  
   131  func (k *key) Parts() []interface{} {
   132  	return k.parts
   133  }
   134  
   135  func (k *key) Type() dgo.Type {
   136  	return keyType
   137  }
   138  
   139  func (k *key) Root() string {
   140  	return k.parts[0].(string)
   141  }
   142  
   143  func (k *key) Source() string {
   144  	return k.source
   145  }
   146  
   147  func (k *key) String() string {
   148  	return k.Type().(dgo.NamedType).ValueString(k)
   149  }
   150  
   151  func parseUnquoted(b *bytes.Buffer, key, part string, parts []interface{}) []interface{} {
   152  	mungedPart := func(ix int, part string) interface{} {
   153  		if i, err := strconv.ParseInt(part, 10, 32); err == nil {
   154  			if ix == 0 {
   155  				panic(catch.Error(`key '%s' first segment cannot be an index`, key))
   156  			}
   157  			return int(i)
   158  		}
   159  		if part == `` {
   160  			panic(catch.Error(`key '%s' contains an empty segment`, key))
   161  		}
   162  		return part
   163  	}
   164  
   165  	for i, c := range part {
   166  		switch c {
   167  		case '\'', '"':
   168  			return parseQuoted(b, c, key, part[i+1:], parts)
   169  		case '.':
   170  			parts = append(parts, mungedPart(len(parts), b.String()))
   171  			b.Reset()
   172  		default:
   173  			_, _ = b.WriteRune(c)
   174  		}
   175  	}
   176  	return append(parts, mungedPart(len(parts), b.String()))
   177  }
   178  
   179  func parseQuoted(b *bytes.Buffer, q rune, key, part string, parts []interface{}) []interface{} {
   180  	for i, c := range part {
   181  		if c == q {
   182  			if i == len(part)-1 {
   183  				return append(parts, b.String())
   184  			}
   185  			return parseUnquoted(b, key, part[i+1:], parts)
   186  		}
   187  		_, _ = b.WriteRune(c)
   188  	}
   189  	panic(catch.Error(`unterminated quote in key '%s'`, key))
   190  }