github.com/facebookincubator/go-belt@v0.0.0-20230703220935-39cd348f1a38/pkg/field/fields_chain.go (about) 1 // Copyright 2022 Meta Platforms, Inc. and affiliates. 2 // 3 // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 // 5 // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 // 7 // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 // 9 // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 // 11 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 13 // Copyright (c) Facebook, Inc. and its affiliates. 14 // 15 // This source code is licensed under the MIT license found in the 16 // LICENSE file in the root directory of this source tree. 17 18 package field 19 20 import ( 21 "fmt" 22 "strings" 23 ) 24 25 // FieldsChain is a chain of fields. 26 // 27 // FieldsChain is focused on high-performance Clone method. 28 type FieldsChain struct { 29 parent *FieldsChain 30 31 // In current implementation oneFieldKey and multipleFields are never 32 // used together, but still we preserve oneFieldKey and oneFieldValue due 33 // to performance reasons, it allows to avoid allocating and accessing 34 // a map for a single field. 35 oneField Field 36 multipleFields AbstractFields 37 } 38 39 var _ AbstractFields = (*FieldsChain)(nil) 40 41 // NewChainFromOne returns a FieldsChain which contains only one Field. 42 func NewChainFromOne(key Key, value Value, props ...Property) *FieldsChain { 43 return &FieldsChain{ 44 oneField: Field{ 45 Key: key, 46 Value: value, 47 Properties: props, 48 }, 49 } 50 } 51 52 // ForEachField implements AbstractFields 53 func (fields *FieldsChain) ForEachField(callback func(f *Field) bool) bool { 54 if fields == nil { 55 return true 56 } 57 58 if fields.multipleFields != nil { 59 if !fields.multipleFields.ForEachField(callback) { 60 return false 61 } 62 } else { 63 if !callback(&fields.oneField) { 64 return false 65 } 66 } 67 68 if !fields.parent.ForEachField(callback) { 69 return false 70 } 71 72 return true 73 } 74 75 // Len returns the total amount of fields reported by ForEachField. 76 // T: O(n) 77 // 78 // Implements AbstractFields 79 func (fields *FieldsChain) Len() int { 80 length := 0 81 fields.ForEachField(func(field *Field) bool { 82 length++ 83 return true 84 }) 85 return length 86 } 87 88 // WithField adds the field to the chain (from the forward -- FIFO) and 89 // returns the pointer to the new beginning. 90 func (fields *FieldsChain) WithField(key Key, value Value, props ...Property) *FieldsChain { 91 return &FieldsChain{ 92 parent: fields, 93 oneField: Field{ 94 Key: key, 95 Value: value, 96 Properties: props, 97 }, 98 } 99 } 100 101 // WithFields adds the fields to the chain (from the forward -- FIFO) and 102 // returns the pointer to the new beginning. 103 func (fields *FieldsChain) WithFields(add AbstractFields) *FieldsChain { 104 if add == nil { 105 return fields 106 } 107 return &FieldsChain{ 108 parent: fields, 109 multipleFields: add, 110 } 111 } 112 113 // WithMap adds the fields constructed from the map to the chain (from the forward -- FIFO) and 114 // returns the pointer to the new beginning. 115 func (fields *FieldsChain) WithMap(m map[Key]interface{}, props ...Property) *FieldsChain { 116 if m == nil { 117 return fields 118 } 119 120 if len(m) < 4 { 121 result := fields 122 for k, v := range m { 123 result = result.WithField(k, v, props...) 124 } 125 return result 126 } 127 128 add := make(Fields, 0, len(m)) 129 for k, v := range m { 130 add = append(add, Field{ 131 Key: k, 132 Value: v, 133 Properties: props, 134 }) 135 } 136 return fields.WithFields(add) 137 } 138 139 // GoString implements fmt.GoStringer 140 func (fields *FieldsChain) GoString() string { 141 var strs []string 142 fields.ForEachField(func(f *Field) bool { 143 strs = append(strs, fmt.Sprintf("%#+v", *f)) 144 return true 145 }) 146 return "Fields{" + strings.Join(strs, "; ") + "}" 147 }