go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/lucicfg/graph/keyset.go (about) 1 // Copyright 2018 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 graph 16 17 import ( 18 "fmt" 19 "strings" 20 ) 21 22 // KeySet is a set of all keys ever defined in a graph. 23 // 24 // Each key is a singleton object: asking for the same key twice returns exact 25 // same *Key object. This simplifies comparison of keys and using keys as, well, 26 // keys in a map. 27 type KeySet struct { 28 keys map[string]*Key // compact representation of the key path -> *Key 29 } 30 31 // Key returns a *Key given a list of (kind, id) pairs. 32 // 33 // There can be at most one kind that starts with '@...' (aka "namespace kind"), 34 // and it must come first (if present). 35 func (k *KeySet) Key(pairs ...string) (*Key, error) { 36 switch { 37 case len(pairs) == 0: 38 return nil, fmt.Errorf("empty key path") 39 case len(pairs)%2 != 0: 40 return nil, fmt.Errorf("key path %q has odd number of components", pairs) 41 } 42 43 for _, s := range pairs { 44 if strings.IndexByte(s, 0) != -1 { 45 return nil, fmt.Errorf("bad key path element %q, has zero byte inside", s) 46 } 47 } 48 49 for i := 2; i < len(pairs); i += 2 { 50 if strings.HasPrefix(pairs[i], "@") { 51 return nil, fmt.Errorf("kind %q can appear only at the start of the key path", pairs[i]) 52 } 53 } 54 55 keyID := strings.Join(pairs, "\x00") 56 if key := k.keys[keyID]; key != nil { 57 return key, nil 58 } 59 60 if k.keys == nil { 61 k.keys = make(map[string]*Key, 1) 62 } 63 64 key := &Key{set: k, pairs: pairs, idx: len(k.keys), cmp: keyID} 65 k.keys[keyID] = key 66 return key, nil 67 }