github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/cmds/core/elvish/edit/eddefs/binding_map.go (about)

     1  package eddefs
     2  
     3  import (
     4  	"errors"
     5  	"sort"
     6  
     7  	"github.com/u-root/u-root/cmds/core/elvish/edit/ui"
     8  	"github.com/u-root/u-root/cmds/core/elvish/eval"
     9  	"github.com/u-root/u-root/cmds/core/elvish/eval/vals"
    10  	"github.com/u-root/u-root/cmds/core/elvish/hashmap"
    11  	"github.com/u-root/u-root/cmds/core/elvish/parse"
    12  )
    13  
    14  var errValueShouldBeFn = errors.New("value should be function")
    15  
    16  // BindingMap is a special Map that converts its key to ui.Key and ensures
    17  // that its values satisfy eval.CallableValue.
    18  type BindingMap struct {
    19  	hashmap.Map
    20  }
    21  
    22  var EmptyBindingMap = BindingMap{vals.EmptyMap}
    23  
    24  // Repr returns the representation of the binding table as if it were an
    25  // ordinary map keyed by strings.
    26  func (bt BindingMap) Repr(indent int) string {
    27  	var builder vals.MapReprBuilder
    28  	builder.Indent = indent
    29  
    30  	var keys ui.Keys
    31  	for it := bt.Map.Iterator(); it.HasElem(); it.Next() {
    32  		k, _ := it.Elem()
    33  		keys = append(keys, k.(ui.Key))
    34  	}
    35  	sort.Sort(keys)
    36  
    37  	for _, k := range keys {
    38  		v, _ := bt.Map.Index(k)
    39  		builder.WritePair(parse.Quote(k.String()), indent+2, vals.Repr(v, indent+2))
    40  	}
    41  
    42  	return builder.String()
    43  }
    44  
    45  // Index converts the index to ui.Key and uses the Index of the inner Map.
    46  func (bt BindingMap) Index(index interface{}) (interface{}, error) {
    47  	return vals.Index(bt.Map, ui.ToKey(index))
    48  }
    49  
    50  func (bt BindingMap) HasKey(k interface{}) bool {
    51  	_, ok := bt.Map.Index(k)
    52  	return ok
    53  }
    54  
    55  func (bt BindingMap) GetKey(k ui.Key) eval.Callable {
    56  	v, ok := bt.Map.Index(k)
    57  	if !ok {
    58  		panic("get called when key not present")
    59  	}
    60  	return v.(eval.Callable)
    61  }
    62  
    63  func (bt BindingMap) GetOrDefault(k ui.Key) eval.Callable {
    64  	switch {
    65  	case bt.HasKey(k):
    66  		return bt.GetKey(k)
    67  	case bt.HasKey(ui.Default):
    68  		return bt.GetKey(ui.Default)
    69  	}
    70  	return nil
    71  }
    72  
    73  // Assoc converts the index to ui.Key, ensures that the value is CallableValue,
    74  // uses the Assoc of the inner Map and converts the result to a BindingTable.
    75  func (bt BindingMap) Assoc(k, v interface{}) (interface{}, error) {
    76  	key := ui.ToKey(k)
    77  	f, ok := v.(eval.Callable)
    78  	if !ok {
    79  		return nil, errValueShouldBeFn
    80  	}
    81  	map2 := bt.Map.Assoc(key, f)
    82  	return BindingMap{map2}, nil
    83  }
    84  
    85  // Dissoc converts the key to ui.Key and calls the Dissoc method of the inner
    86  // map.
    87  func (bt BindingMap) Dissoc(k interface{}) interface{} {
    88  	return BindingMap{bt.Map.Dissoc(ui.ToKey(k))}
    89  }
    90  
    91  // MakeBindingMapCallable implements eval.CustomCallable interface for makeBindingMap.
    92  func MakeBindingMapCallable() eval.CustomCallable {
    93  	return &makeBindingMapCallable{}
    94  }
    95  
    96  func makeBindingMap(raw hashmap.Map) (BindingMap, error) {
    97  	converted := vals.EmptyMap
    98  	for it := raw.Iterator(); it.HasElem(); it.Next() {
    99  		k, v := it.Elem()
   100  		f, ok := v.(eval.Callable)
   101  		if !ok {
   102  			return EmptyBindingMap, errValueShouldBeFn
   103  		}
   104  		converted = converted.Assoc(ui.ToKey(k), f)
   105  	}
   106  
   107  	return BindingMap{converted}, nil
   108  }
   109  
   110  type makeBindingMapCallable struct {
   111  }
   112  
   113  func (*makeBindingMapCallable) Target() interface{} {
   114  	return makeBindingMap
   115  }
   116  
   117  func (*makeBindingMapCallable) Call(f *eval.Frame, args []interface{}, opts eval.RawOptions, inputs eval.Inputs) ([]interface{}, error) {
   118  	out, err := makeBindingMap(args[0].(hashmap.Map))
   119  	return []interface{}{out}, err
   120  }