github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/elvish/eval/vals/struct.go (about) 1 package vals 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 9 "github.com/u-root/u-root/cmds/elvish/parse" 10 "github.com/xiaq/persistent/hash" 11 ) 12 13 var ( 14 ErrIndexMustBeString = errors.New("index must be string") 15 ) 16 17 // Struct is like a Map with fixed keys. 18 type Struct struct { 19 descriptor *StructDescriptor 20 fields []interface{} 21 } 22 23 var _ Indexer = (*Struct)(nil) 24 25 // NewStruct creates a new *Struct value. 26 func NewStruct(descriptor *StructDescriptor, fields []interface{}) *Struct { 27 return &Struct{descriptor, fields} 28 } 29 30 func (*Struct) Kind() string { 31 return "map" 32 } 33 34 // Equal returns true if the rhs is MapLike and all pairs are equal. 35 func (s *Struct) Equal(rhs interface{}) bool { 36 if s == rhs { 37 return true 38 } 39 s2, ok := rhs.(*Struct) 40 if !ok { 41 return false 42 } 43 if s.descriptor != s2.descriptor { 44 return false 45 } 46 for i, field := range s.fields { 47 if !Equal(field, s2.fields[i]) { 48 return false 49 } 50 } 51 return true 52 } 53 54 func (s *Struct) Hash() uint32 { 55 h := hash.DJBInit 56 for _, field := range s.fields { 57 h = hash.DJBCombine(h, Hash(field)) 58 } 59 return h 60 } 61 62 func (s *Struct) Repr(indent int) string { 63 var builder MapReprBuilder 64 builder.Indent = indent 65 for i, name := range s.descriptor.fieldNames { 66 builder.WritePair(parse.Quote(name), indent+2, Repr(s.fields[i], indent+2)) 67 } 68 return builder.String() 69 } 70 71 func (s *Struct) Len() int { 72 return len(s.descriptor.fieldNames) 73 } 74 75 func (s *Struct) Index(k interface{}) (interface{}, bool) { 76 i, ok := s.index(k) 77 if !ok { 78 return nil, false 79 } 80 return s.fields[i], true 81 } 82 83 func (s *Struct) Assoc(k, v interface{}) (interface{}, error) { 84 i, ok := s.index(k) 85 if !ok { 86 return nil, NoSuchKey(k) 87 } 88 fields := make([]interface{}, len(s.fields)) 89 copy(fields, s.fields) 90 fields[i] = v 91 return &Struct{s.descriptor, fields}, nil 92 } 93 94 func (s *Struct) IterateKey(f func(interface{}) bool) { 95 for _, field := range s.descriptor.fieldNames { 96 if !f(string(field)) { 97 break 98 } 99 } 100 } 101 102 func (s *Struct) IteratePair(f func(interface{}, interface{}) bool) { 103 for i, field := range s.descriptor.fieldNames { 104 if !f(string(field), s.fields[i]) { 105 break 106 } 107 } 108 } 109 110 func (s *Struct) HasKey(k interface{}) bool { 111 _, ok := s.index(k) 112 return ok 113 } 114 115 func (s *Struct) index(k interface{}) (int, bool) { 116 kstring, ok := k.(string) 117 if !ok { 118 return 0, false 119 } 120 index, ok := s.descriptor.fieldIndex[kstring] 121 return index, ok 122 } 123 124 // MarshalJSON encodes the Struct to a JSON Object. 125 func (s *Struct) MarshalJSON() ([]byte, error) { 126 var buf bytes.Buffer 127 buf.WriteByte('{') 128 for i, fieldName := range s.descriptor.fieldNames { 129 if i > 0 { 130 buf.WriteByte(',') 131 } 132 buf.Write(s.descriptor.jsonFieldNames[i]) 133 buf.WriteByte(':') 134 fieldJSON, err := json.Marshal(s.fields[i]) 135 if err != nil { 136 return nil, fmt.Errorf("cannot encode field %q: %v", fieldName, err) 137 } 138 buf.Write(fieldJSON) 139 } 140 buf.WriteByte('}') 141 return buf.Bytes(), nil 142 } 143 144 // StructDescriptor contains information about the fields in a Struct. 145 type StructDescriptor struct { 146 fieldNames []string 147 jsonFieldNames [][]byte 148 fieldIndex map[string]int 149 } 150 151 // NewStructDescriptor creates a new struct descriptor from a list of field 152 // names. 153 func NewStructDescriptor(fields ...string) *StructDescriptor { 154 fieldNames := append([]string(nil), fields...) 155 jsonFieldNames := make([][]byte, len(fields)) 156 fieldIndex := make(map[string]int) 157 for i, name := range fieldNames { 158 fieldIndex[name] = i 159 jsonFieldName, err := json.Marshal(name) 160 // json.Marshal should never fail on string. 161 if err != nil { 162 panic(err) 163 } 164 jsonFieldNames[i] = jsonFieldName 165 } 166 return &StructDescriptor{fieldNames, jsonFieldNames, fieldIndex} 167 }