go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/resultdb/pbutil/variant.go (about) 1 // Copyright 2019 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 pbutil 16 17 import ( 18 "crypto/sha256" 19 "encoding/hex" 20 "fmt" 21 "io" 22 "sort" 23 24 "go.chromium.org/luci/common/errors" 25 26 pb "go.chromium.org/luci/resultdb/proto/v1" 27 ) 28 29 // ValidateVariant returns an error if vr is invalid. 30 func ValidateVariant(vr *pb.Variant) error { 31 for k, v := range vr.GetDef() { 32 p := pb.StringPair{Key: k, Value: v} 33 if err := ValidateStringPair(&p); err != nil { 34 return errors.Annotate(err, "%q:%q", k, v).Err() 35 } 36 } 37 return nil 38 } 39 40 // Variant creates a pb.Variant from a list of strings alternating 41 // key/value. Does not validate pairs. 42 // See also VariantFromStrings. 43 // 44 // Panics if an odd number of tokens is passed. 45 func Variant(pairs ...string) *pb.Variant { 46 if len(pairs)%2 != 0 { 47 panic(fmt.Sprintf("odd number of tokens in %q", pairs)) 48 } 49 50 vr := &pb.Variant{Def: make(map[string]string, len(pairs)/2)} 51 for i := 0; i < len(pairs); i += 2 { 52 vr.Def[pairs[i]] = pairs[i+1] 53 } 54 return vr 55 } 56 57 var nonNilEmptyStringSlice = []string{} 58 59 // VariantToStrings returns a key:val string slice representation of the Variant. 60 // Never returns nil. 61 func VariantToStrings(vr *pb.Variant) []string { 62 if len(vr.GetDef()) == 0 { 63 return nonNilEmptyStringSlice 64 } 65 66 keys := SortedVariantKeys(vr) 67 pairs := make([]string, len(keys)) 68 defMap := vr.GetDef() 69 for i, k := range keys { 70 pairs[i] = fmt.Sprintf("%s:%s", k, defMap[k]) 71 } 72 return pairs 73 } 74 75 // VariantFromStrings returns a Variant proto given the key:val string slice of its contents. 76 // 77 // If a key appears multiple times, the last pair wins. 78 func VariantFromStrings(pairs []string) (*pb.Variant, error) { 79 if len(pairs) == 0 { 80 return nil, nil 81 } 82 83 def := make(map[string]string, len(pairs)) 84 for _, p := range pairs { 85 pair, err := StringPairFromString(p) 86 if err != nil { 87 return nil, errors.Annotate(err, "pair %q", p).Err() 88 } 89 def[pair.Key] = pair.Value 90 } 91 return &pb.Variant{Def: def}, nil 92 } 93 94 // SortedVariantKeys returns the keys in the variant as a sorted slice. 95 func SortedVariantKeys(vr *pb.Variant) []string { 96 keys := make([]string, 0, len(vr.GetDef())) 97 for k := range vr.GetDef() { 98 keys = append(keys, k) 99 } 100 sort.Strings(keys) 101 return keys 102 } 103 104 // VariantHash returns a short hash of the variant. 105 func VariantHash(vr *pb.Variant) string { 106 h := sha256.New() 107 for _, k := range SortedVariantKeys(vr) { 108 io.WriteString(h, k) 109 io.WriteString(h, ":") 110 io.WriteString(h, vr.Def[k]) 111 io.WriteString(h, "\n") 112 } 113 return hex.EncodeToString(h.Sum(nil)[:8]) 114 } 115 116 // VariantToStringPairs returns a slice of StringPair derived from *pb.Variant. 117 func VariantToStringPairs(vr *pb.Variant) []*pb.StringPair { 118 defMap := vr.GetDef() 119 if len(defMap) == 0 { 120 return nil 121 } 122 123 keys := SortedVariantKeys(vr) 124 sp := make([]*pb.StringPair, len(keys)) 125 for i, k := range keys { 126 sp[i] = StringPair(k, defMap[k]) 127 } 128 return sp 129 } 130 131 // CombineVariant combines base variant and additional variant. The additional 132 // variant will overwrite the base variant if there is a duplicate key. 133 func CombineVariant(baseVariant *pb.Variant, additionalVariant *pb.Variant) *pb.Variant { 134 if baseVariant == nil && additionalVariant == nil { 135 return nil 136 } 137 138 variant := &pb.Variant{ 139 Def: make(map[string]string, len(baseVariant.GetDef())+len(additionalVariant.GetDef())), 140 } 141 for key, value := range baseVariant.GetDef() { 142 variant.Def[key] = value 143 } 144 for key, value := range additionalVariant.GetDef() { 145 variant.Def[key] = value 146 } 147 return variant 148 }