go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/lucicfg/graph/key.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 "strconv" 19 "strings" 20 21 "go.starlark.net/starlark" 22 ) 23 24 // Key is a unique identifier of a node in the graph. 25 // 26 // It is constructed from a series of (kind: string, id: string) pairs, and once 27 // constructed it acts as an opaque label, not examinable through Starlark. 28 // 29 // From the Starlark side it looks like a ref-like hashable object: keys can be 30 // compared to each other via == and !=, and can be used in dicts and sets. 31 // 32 // Keys live in a KeySet, which represents a universe of all keys and it is also 33 // responsible for their construction. Keys from different key sets (even if 34 // they represent same path) are considered not equal and shouldn't be used 35 // together. 36 type Key struct { 37 set *KeySet // the parent key set that created this key 38 pairs []string // original list of (kind1, id1, kind2, id2, ...) strings 39 idx int // index of this key in the KeySet 40 cmp string // composite string to use to compare keys lexicographically 41 } 42 43 // Container returns a key with all (kind, id) pairs of this key, except the 44 // last one, or nil if this key has only one (kind, id) pair. 45 // 46 // Usually it represents a container that holds an object represented by the 47 // this key. 48 func (k *Key) Container() *Key { 49 if len(k.pairs) == 2 { 50 return nil 51 } 52 cont, err := k.set.Key(k.pairs[:len(k.pairs)-2]...) 53 if err != nil { 54 panic(err) // 'k' has been validated, all its components are thus also valid 55 } 56 return cont 57 } 58 59 // ID returns id of the last (kind, id) pair in the key, which usually holds 60 // a user-friendly name of an object this key represents. 61 func (k *Key) ID() string { 62 return k.pairs[len(k.pairs)-1] 63 } 64 65 // Kind returns kind of the last (kind, id) pair in the key, which usually 66 // defines what sort of an object this key represents. 67 func (k *Key) Kind() string { 68 return k.pairs[len(k.pairs)-2] 69 } 70 71 // Root returns a key with the first (kind, id) pair of this key. 72 func (k *Key) Root() *Key { 73 r, err := k.set.Key(k.pairs[:2]...) 74 if err != nil { 75 panic(err) // 'k' has been validated, all its components are thus also valid 76 } 77 return r 78 } 79 80 // Less returns true if this key is lexicographically before another key. 81 func (k *Key) Less(an *Key) bool { 82 return k.cmp < an.cmp 83 } 84 85 // String is part of starlark.Value interface. 86 // 87 // Returns [kind1("id1"), kind2("id2"), ...]. Must not be parsed, only for 88 // logging. 89 func (k *Key) String() string { 90 if len(k.pairs)%2 != 0 { 91 panic("odd") // never happens, validated by KeySet.Key(...) constructor 92 } 93 sb := strings.Builder{} 94 sb.WriteRune('[') 95 for i := 0; i < len(k.pairs)/2; i++ { 96 kind, id := k.pairs[i*2], k.pairs[i*2+1] 97 if i != 0 { 98 sb.WriteString(", ") 99 } 100 sb.WriteString(kind) 101 sb.WriteRune('(') 102 sb.WriteString(strconv.Quote(id)) 103 sb.WriteRune(')') 104 } 105 sb.WriteRune(']') 106 return sb.String() 107 } 108 109 // Type is a part of starlark.Value interface. 110 func (k *Key) Type() string { return "graph.key" } 111 112 // Freeze is a part of starlark.Value interface. 113 func (k *Key) Freeze() {} 114 115 // Truth is a part of starlark.Value interface. 116 func (k *Key) Truth() starlark.Bool { return starlark.True } 117 118 // Hash is a part of starlark.Value interface. 119 func (k *Key) Hash() (uint32, error) { return uint32(k.idx), nil } 120 121 // AttrNames is a part of starlark.HasAttrs interface. 122 func (k *Key) AttrNames() []string { 123 return []string{ 124 "container", // graph.key(...) of the owning container or None 125 "id", // key ID string 126 "kind", // key Kind string 127 "root", // graph.key(...) of the outermost owning container (or self) 128 } 129 } 130 131 // Attr is a part of starlark.HasAttrs interface. 132 func (k *Key) Attr(name string) (starlark.Value, error) { 133 switch name { 134 case "container": 135 if c := k.Container(); c != nil { 136 return c, nil 137 } 138 return starlark.None, nil 139 case "id": 140 return starlark.String(k.ID()), nil 141 case "kind": 142 return starlark.String(k.Kind()), nil 143 case "root": 144 return k.Root(), nil 145 default: 146 return nil, nil // per Attr(...) contract 147 } 148 }