go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/proto/protowalk/attributes.go (about) 1 // Copyright 2022 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package protowalk 16 17 import "google.golang.org/protobuf/reflect/protoreflect" 18 19 // recurseAttrs is a cache data value which indicates that Fields() should 20 // recurse into the field (because some deeper message has indicated that it 21 // needs to process something). 22 // 23 // This also encodes the TYPE of recursion which should occur, to avoid 24 // unnecessary protoreflect calls. 25 // 26 // This byte holds multiple values; The least significant 2 bits are used 27 // to indicate {None, One, Repeated, Map}. In the case of Map, an additional 28 // 3 bits are used as a value to indicate what proto kind the map key is 29 // (bool, int, uint, string). The map key kinds are explicitly enumerated here, 30 // because we want to do sorted map iteration for deterministic error messages. 31 // 32 // Note; If someone wants to process the keys of a map, they should annotate the 33 // map field itself. 34 // 35 // All values other than recurseNone imply that the field is a Message type. 36 type recurseAttr byte 37 38 const ( 39 recurseNone recurseAttr = 0b000_00 // No recursion 40 recurseOne recurseAttr = 0b000_01 // Singular message field 41 recurseRepeated recurseAttr = 0b000_10 // Repeated message field 42 recurseMapKind recurseAttr = 0b000_11 // map<*, Message> 43 recurseMapBool recurseAttr = 0b001_00 | recurseMapKind // map<bool, Message> 44 recurseMapInt recurseAttr = 0b010_00 | recurseMapKind // map<int{32,64}, Message> 45 recurseMapUint recurseAttr = 0b011_00 | recurseMapKind // map<uint{32,64}, Message> 46 recurseMapString recurseAttr = 0b100_00 | recurseMapKind // map<string, Message> 47 ) 48 49 // isMap returns true if recurseAttr is any kind of map recursion. 50 func (a recurseAttr) isMap() bool { return a&recurseMapKind == recurseMapKind } 51 52 // Converts this recurseAttr for a Map into a protoreflect Kind for the map's 53 // key. 54 func (a recurseAttr) mapKeyKind() protoreflect.Kind { 55 switch a { 56 case recurseMapBool: 57 return protoreflect.BoolKind 58 case recurseMapInt: 59 return protoreflect.Int32Kind // also counts as int64 60 case recurseMapUint: 61 return protoreflect.Uint32Kind // also counts as uint64 62 case recurseMapString: 63 return protoreflect.StringKind 64 } 65 return 0 // invalid 66 } 67 68 // set will replace this recurseAttr with `other`, if `other` has a non-zero 69 // value. 70 // 71 // Returns `true` if `other` was non-zero (i.e. replacement or equivalence). 72 // 73 // Panics if `a` is already set and `other` is a different non-zero value. 74 // This should NEVER happen unless this package has a bug because a given 75 // field cannot have multiple recursion types (i.e. it's either single valued, 76 // repeated, or a map... not some combination of the above). 77 func (a *recurseAttr) set(other recurseAttr) (wasSet bool) { 78 wasSet = other != recurseNone 79 if wasSet { 80 switch myval := *a; { 81 case myval == recurseNone: 82 *a = other 83 case myval != other: 84 panic("BUG: recurseAttr.set called with incompatible recursion types.") 85 } 86 } 87 return 88 } 89 90 // ProcessAttr is a cache data value which indicates that this field needs to be 91 // processed, whether for set fields, unset fields, or both. 92 type ProcessAttr byte 93 94 const ( 95 // ProcessNever indicates that this processor doesn't want to process this 96 // field. 97 ProcessNever ProcessAttr = 0b00 98 99 // ProcessIfSet indicates that this processor only applies to this field when 100 // the field has a value (i.e. protoreflect.Message.Has(field)). 101 ProcessIfSet ProcessAttr = 0b01 102 103 // ProcessIfUnset indicates that this processor only applies to this field when 104 // the field does not have a value (i.e. !protoreflect.Message.Has(field)). 105 ProcessIfUnset ProcessAttr = 0b10 106 107 // ProcessAlways indicates that this processor always applies to this field 108 // regardless of value status. 109 ProcessAlways = ProcessIfSet | ProcessIfUnset 110 ) 111 112 // applies returns true if this ProcessAttr should process a field which `isSet`. 113 func (a ProcessAttr) applies(isSet bool) bool { 114 return ((isSet && (a&ProcessIfSet == ProcessIfSet)) || 115 (!isSet && (a&ProcessIfUnset == ProcessIfUnset))) 116 } 117 118 // Valid returns true if `a` is a known ProcessAttr value. 119 func (a ProcessAttr) Valid() bool { 120 switch a { 121 case ProcessNever, ProcessIfSet, ProcessIfUnset, ProcessAlways: 122 return true 123 } 124 return false 125 }