1 /* 2 * 3 * Copyright 2019 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 // Package attributes defines a generic key/value store used in various gRPC 20 // components. 21 // 22 // # Experimental 23 // 24 // Notice: This package is EXPERIMENTAL and may be changed or removed in a 25 // later release. 26 package attributes 27 28 import ( 29 "fmt" 30 "strings" 31 ) 32 33 // Attributes is an immutable struct for storing and retrieving generic 34 // key/value pairs. Keys must be hashable, and users should define their own 35 // types for keys. Values should not be modified after they are added to an 36 // Attributes or if they were received from one. If values implement 'Equal(o 37 // any) bool', it will be called by (*Attributes).Equal to determine whether 38 // two values with the same key should be considered equal. 39 type Attributes struct { 40 m map[any]any 41 } 42 43 // New returns a new Attributes containing the key/value pair. 44 func New(key, value any) *Attributes { 45 return &Attributes{m: map[any]any{key: value}} 46 } 47 48 // WithValue returns a new Attributes containing the previous keys and values 49 // and the new key/value pair. If the same key appears multiple times, the 50 // last value overwrites all previous values for that key. To remove an 51 // existing key, use a nil value. value should not be modified later. 52 func (a *Attributes) WithValue(key, value any) *Attributes { 53 if a == nil { 54 return New(key, value) 55 } 56 n := &Attributes{m: make(map[any]any, len(a.m)+1)} 57 for k, v := range a.m { 58 n.m[k] = v 59 } 60 n.m[key] = value 61 return n 62 } 63 64 // Value returns the value associated with these attributes for key, or nil if 65 // no value is associated with key. The returned value should not be modified. 66 func (a *Attributes) Value(key any) any { 67 if a == nil { 68 return nil 69 } 70 return a.m[key] 71 } 72 73 // Equal returns whether a and o are equivalent. If 'Equal(o any) bool' is 74 // implemented for a value in the attributes, it is called to determine if the 75 // value matches the one stored in the other attributes. If Equal is not 76 // implemented, standard equality is used to determine if the two values are 77 // equal. Note that some types (e.g. maps) aren't comparable by default, so 78 // they must be wrapped in a struct, or in an alias type, with Equal defined. 79 func (a *Attributes) Equal(o *Attributes) bool { 80 if a == nil && o == nil { 81 return true 82 } 83 if a == nil || o == nil { 84 return false 85 } 86 if len(a.m) != len(o.m) { 87 return false 88 } 89 for k, v := range a.m { 90 ov, ok := o.m[k] 91 if !ok { 92 // o missing element of a 93 return false 94 } 95 if eq, ok := v.(interface{ Equal(o any) bool }); ok { 96 if !eq.Equal(ov) { 97 return false 98 } 99 } else if v != ov { 100 // Fallback to a standard equality check if Value is unimplemented. 101 return false 102 } 103 } 104 return true 105 } 106 107 // String prints the attribute map. If any key or values throughout the map 108 // implement fmt.Stringer, it calls that method and appends. 109 func (a *Attributes) String() string { 110 var sb strings.Builder 111 sb.WriteString("{") 112 first := true 113 for k, v := range a.m { 114 if !first { 115 sb.WriteString(", ") 116 } 117 sb.WriteString(fmt.Sprintf("%q: %q ", str(k), str(v))) 118 first = false 119 } 120 sb.WriteString("}") 121 return sb.String() 122 } 123 124 func str(x any) (s string) { 125 if v, ok := x.(fmt.Stringer); ok { 126 return fmt.Sprint(v) 127 } else if v, ok := x.(string); ok { 128 return v 129 } 130 return fmt.Sprintf("<%p>", x) 131 } 132 133 // MarshalJSON helps implement the json.Marshaler interface, thereby rendering 134 // the Attributes correctly when printing (via pretty.JSON) structs containing 135 // Attributes as fields. 136 // 137 // Is it impossible to unmarshal attributes from a JSON representation and this 138 // method is meant only for debugging purposes. 139 func (a *Attributes) MarshalJSON() ([]byte, error) { 140 return []byte(a.String()), nil 141 } 142