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  }