khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 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 | // |
Joey Armstrong | ba3d9d1 | 2024-01-15 14:22:11 -0500 | [diff] [blame] | 22 | // # Experimental |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 23 | // |
| 24 | // Notice: This package is EXPERIMENTAL and may be changed or removed in a |
| 25 | // later release. |
| 26 | package attributes |
| 27 | |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 28 | // Attributes is an immutable struct for storing and retrieving generic |
| 29 | // key/value pairs. Keys must be hashable, and users should define their own |
khenaidoo | 5cb0d40 | 2021-12-08 14:09:16 -0500 | [diff] [blame] | 30 | // types for keys. Values should not be modified after they are added to an |
| 31 | // Attributes or if they were received from one. If values implement 'Equal(o |
| 32 | // interface{}) bool', it will be called by (*Attributes).Equal to determine |
| 33 | // whether two values with the same key should be considered equal. |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 34 | type Attributes struct { |
| 35 | m map[interface{}]interface{} |
| 36 | } |
| 37 | |
khenaidoo | 5cb0d40 | 2021-12-08 14:09:16 -0500 | [diff] [blame] | 38 | // New returns a new Attributes containing the key/value pair. |
| 39 | func New(key, value interface{}) *Attributes { |
| 40 | return &Attributes{m: map[interface{}]interface{}{key: value}} |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 41 | } |
| 42 | |
khenaidoo | 5cb0d40 | 2021-12-08 14:09:16 -0500 | [diff] [blame] | 43 | // WithValue returns a new Attributes containing the previous keys and values |
| 44 | // and the new key/value pair. If the same key appears multiple times, the |
| 45 | // last value overwrites all previous values for that key. To remove an |
| 46 | // existing key, use a nil value. value should not be modified later. |
| 47 | func (a *Attributes) WithValue(key, value interface{}) *Attributes { |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 48 | if a == nil { |
khenaidoo | 5cb0d40 | 2021-12-08 14:09:16 -0500 | [diff] [blame] | 49 | return New(key, value) |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 50 | } |
khenaidoo | 5cb0d40 | 2021-12-08 14:09:16 -0500 | [diff] [blame] | 51 | n := &Attributes{m: make(map[interface{}]interface{}, len(a.m)+1)} |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 52 | for k, v := range a.m { |
| 53 | n.m[k] = v |
| 54 | } |
khenaidoo | 5cb0d40 | 2021-12-08 14:09:16 -0500 | [diff] [blame] | 55 | n.m[key] = value |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 56 | return n |
| 57 | } |
| 58 | |
| 59 | // Value returns the value associated with these attributes for key, or nil if |
khenaidoo | 5cb0d40 | 2021-12-08 14:09:16 -0500 | [diff] [blame] | 60 | // no value is associated with key. The returned value should not be modified. |
khenaidoo | 5fc5cea | 2021-08-11 17:39:16 -0400 | [diff] [blame] | 61 | func (a *Attributes) Value(key interface{}) interface{} { |
| 62 | if a == nil { |
| 63 | return nil |
| 64 | } |
| 65 | return a.m[key] |
| 66 | } |
khenaidoo | 5cb0d40 | 2021-12-08 14:09:16 -0500 | [diff] [blame] | 67 | |
| 68 | // Equal returns whether a and o are equivalent. If 'Equal(o interface{}) |
| 69 | // bool' is implemented for a value in the attributes, it is called to |
| 70 | // determine if the value matches the one stored in the other attributes. If |
| 71 | // Equal is not implemented, standard equality is used to determine if the two |
khenaidoo | 257f319 | 2021-12-15 16:46:37 -0500 | [diff] [blame] | 72 | // values are equal. Note that some types (e.g. maps) aren't comparable by |
| 73 | // default, so they must be wrapped in a struct, or in an alias type, with Equal |
| 74 | // defined. |
khenaidoo | 5cb0d40 | 2021-12-08 14:09:16 -0500 | [diff] [blame] | 75 | func (a *Attributes) Equal(o *Attributes) bool { |
| 76 | if a == nil && o == nil { |
| 77 | return true |
| 78 | } |
| 79 | if a == nil || o == nil { |
| 80 | return false |
| 81 | } |
| 82 | if len(a.m) != len(o.m) { |
| 83 | return false |
| 84 | } |
| 85 | for k, v := range a.m { |
| 86 | ov, ok := o.m[k] |
| 87 | if !ok { |
| 88 | // o missing element of a |
| 89 | return false |
| 90 | } |
| 91 | if eq, ok := v.(interface{ Equal(o interface{}) bool }); ok { |
| 92 | if !eq.Equal(ov) { |
| 93 | return false |
| 94 | } |
| 95 | } else if v != ov { |
| 96 | // Fallback to a standard equality check if Value is unimplemented. |
| 97 | return false |
| 98 | } |
| 99 | } |
| 100 | return true |
| 101 | } |