blob: f31cf3b9e6938948d0b1bd5f783a28c1ea12754f [file] [log] [blame]
// Copyright 2019 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pbutil
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"sort"
"go.chromium.org/luci/common/errors"
pb "go.chromium.org/luci/resultdb/proto/v1"
)
// ValidateVariant returns an error if vr is invalid.
func ValidateVariant(vr *pb.Variant) error {
for k, v := range vr.GetDef() {
p := pb.StringPair{Key: k, Value: v}
if err := ValidateStringPair(&p); err != nil {
return errors.Annotate(err, "%q:%q", k, v).Err()
}
}
return nil
}
// Variant creates a pb.Variant from a list of strings alternating
// key/value. Does not validate pairs.
// See also VariantFromStrings.
//
// Panics if an odd number of tokens is passed.
func Variant(pairs ...string) *pb.Variant {
if len(pairs)%2 != 0 {
panic(fmt.Sprintf("odd number of tokens in %q", pairs))
}
vr := &pb.Variant{Def: make(map[string]string, len(pairs)/2)}
for i := 0; i < len(pairs); i += 2 {
vr.Def[pairs[i]] = pairs[i+1]
}
return vr
}
var nonNilEmptyStringSlice = []string{}
// VariantToStrings returns a key:val string slice representation of the Variant.
// Never returns nil.
func VariantToStrings(vr *pb.Variant) []string {
if len(vr.GetDef()) == 0 {
return nonNilEmptyStringSlice
}
keys := SortedVariantKeys(vr)
pairs := make([]string, len(keys))
defMap := vr.GetDef()
for i, k := range keys {
pairs[i] = fmt.Sprintf("%s:%s", k, defMap[k])
}
return pairs
}
// VariantFromStrings returns a Variant proto given the key:val string slice of its contents.
//
// If a key appears multiple times, the last pair wins.
func VariantFromStrings(pairs []string) (*pb.Variant, error) {
if len(pairs) == 0 {
return nil, nil
}
def := make(map[string]string, len(pairs))
for _, p := range pairs {
pair, err := StringPairFromString(p)
if err != nil {
return nil, errors.Annotate(err, "pair %q", p).Err()
}
def[pair.Key] = pair.Value
}
return &pb.Variant{Def: def}, nil
}
// SortedVariantKeys returns the keys in the variant as a sorted slice.
func SortedVariantKeys(vr *pb.Variant) []string {
keys := make([]string, 0, len(vr.GetDef()))
for k := range vr.GetDef() {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}
// VariantHash returns a short hash of the variant.
func VariantHash(vr *pb.Variant) string {
h := sha256.New()
for _, k := range SortedVariantKeys(vr) {
io.WriteString(h, k)
io.WriteString(h, ":")
io.WriteString(h, vr.Def[k])
io.WriteString(h, "\n")
}
return hex.EncodeToString(h.Sum(nil)[:8])
}
// VariantToStringPairs returns a slice of StringPair derived from *pb.Variant.
func VariantToStringPairs(vr *pb.Variant) []*pb.StringPair {
defMap := vr.GetDef()
if len(defMap) == 0 {
return nil
}
keys := SortedVariantKeys(vr)
sp := make([]*pb.StringPair, len(keys))
for i, k := range keys {
sp[i] = StringPair(k, defMap[k])
}
return sp
}
// CombineVariant combines base variant and additional variant. The additional
// variant will overwrite the base variant if there is a duplicate key.
func CombineVariant(baseVariant *pb.Variant, additionalVariant *pb.Variant) *pb.Variant {
if baseVariant == nil && additionalVariant == nil {
return nil
}
variant := &pb.Variant{
Def: make(map[string]string, len(baseVariant.GetDef())+len(additionalVariant.GetDef())),
}
for key, value := range baseVariant.GetDef() {
variant.Def[key] = value
}
for key, value := range additionalVariant.GetDef() {
variant.Def[key] = value
}
return variant
}