github.com/psiphon-labs/goarista@v0.0.0-20160825065156-d002785f4c67/key/key.go (about)

     1  // Copyright (C) 2015  Arista Networks, Inc.
     2  // Use of this source code is governed by the Apache License 2.0
     3  // that can be found in the COPYING file.
     4  
     5  package key
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  
    11  	"github.com/aristanetworks/goarista/value"
    12  )
    13  
    14  // Key represents the Key in the updates and deletes of the Notification
    15  // objects.  The only reason this exists is that Go won't let us define
    16  // our own hash function for non-hashable types, and unfortunately we
    17  // need to be able to index maps by map[string]interface{} objects.
    18  type Key interface {
    19  	Key() interface{}
    20  	String() string
    21  	Equal(other interface{}) bool
    22  
    23  	// Helper methods to manipulate maps keyed by `Key'.
    24  
    25  	// GetFromMap returns the value for the entry of this Key.
    26  	GetFromMap(map[Key]interface{}) (interface{}, bool)
    27  	// DeleteFromMap deletes the entry in the map for this Key.
    28  	// This is a no-op if the key does not exist in the map.
    29  	DeleteFromMap(map[Key]interface{})
    30  	// SetToMap updates or inserts an entry in the map for this Key.
    31  	SetToMap(m map[Key]interface{}, value interface{})
    32  }
    33  
    34  type keyImpl struct {
    35  	key interface{}
    36  }
    37  
    38  // New wraps the given value in a Key.
    39  // This function panics if the value passed in isn't allowed in a Key or
    40  // doesn't implement value.Value.
    41  func New(intf interface{}) Key {
    42  	switch t := intf.(type) {
    43  	case map[string]interface{}:
    44  		return composite{sentinel, t}
    45  	case int8, int16, int32, int64,
    46  		uint8, uint16, uint32, uint64,
    47  		float32, float64, string, bool,
    48  		value.Value:
    49  		return keyImpl{key: intf}
    50  	default:
    51  		panic(fmt.Sprintf("Invalid type for key: %T", intf))
    52  	}
    53  }
    54  
    55  func (k keyImpl) Key() interface{} {
    56  	return k.key
    57  }
    58  
    59  func (k keyImpl) String() string {
    60  	return stringify(k.key)
    61  }
    62  
    63  func (k keyImpl) GetFromMap(m map[Key]interface{}) (interface{}, bool) {
    64  	v, ok := m[k]
    65  	return v, ok
    66  }
    67  
    68  func (k keyImpl) DeleteFromMap(m map[Key]interface{}) {
    69  	delete(m, k)
    70  }
    71  
    72  func (k keyImpl) SetToMap(m map[Key]interface{}, value interface{}) {
    73  	m[k] = value
    74  }
    75  
    76  func (k keyImpl) GoString() string {
    77  	return fmt.Sprintf("key.New(%#v)", k.Key())
    78  }
    79  
    80  func (k keyImpl) MarshalJSON() ([]byte, error) {
    81  	return json.Marshal(k.Key())
    82  }
    83  
    84  func (k keyImpl) Equal(other interface{}) bool {
    85  	o, ok := other.(Key)
    86  	if !ok {
    87  		return false
    88  	}
    89  	return keyEqual(k.key, o.Key())
    90  }
    91  
    92  // Comparable types have an equality-testing method.
    93  type Comparable interface {
    94  	// Equal returns true if this object is equal to the other one.
    95  	Equal(other interface{}) bool
    96  }
    97  
    98  func keyEqual(a, b interface{}) bool {
    99  	switch a := a.(type) {
   100  	case map[string]interface{}:
   101  		b, ok := b.(map[string]interface{})
   102  		if !ok || len(a) != len(b) {
   103  			return false
   104  		}
   105  		for k, av := range a {
   106  			if bv, ok := b[k]; !ok || !keyEqual(av, bv) {
   107  				return false
   108  			}
   109  		}
   110  		return true
   111  	case map[Key]interface{}:
   112  		b, ok := b.(map[Key]interface{})
   113  		if !ok || len(a) != len(b) {
   114  			return false
   115  		}
   116  		for k, av := range a {
   117  			if bv, ok := k.GetFromMap(b); !ok || !keyEqual(av, bv) {
   118  				return false
   119  			}
   120  		}
   121  		return true
   122  	case Comparable:
   123  		return a.Equal(b)
   124  	}
   125  
   126  	return a == b
   127  }