src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

label.go (5431B)


      1 // Copyright 2019 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package label
      6 
      7 import (
      8 	"fmt"
      9 	"io"
     10 	"slices"
     11 	"unsafe"
     12 )
     13 
     14 // Key is used as the identity of a Label.
     15 // Keys are intended to be compared by pointer only, the name should be unique
     16 // for communicating with external systems, but it is not required or enforced.
     17 type Key interface {
     18 	// Name returns the key name.
     19 	Name() string
     20 	// Description returns a string that can be used to describe the value.
     21 	Description() string
     22 	// Append appends the formatted value of the label to the supplied buffer.
     23 	Append(buf []byte, l Label) []byte
     24 }
     25 
     26 // Label holds a key and value pair.
     27 // It is normally used when passing around lists of labels.
     28 type Label struct {
     29 	key     Key
     30 	packed  uint64
     31 	untyped any
     32 }
     33 
     34 // Map is the interface to a collection of Labels indexed by key.
     35 type Map interface {
     36 	// Find returns the label that matches the supplied key.
     37 	Find(key Key) Label
     38 }
     39 
     40 // List is the interface to something that provides an iterable
     41 // list of labels.
     42 // Iteration should start from 0 and continue until Valid returns false.
     43 type List interface {
     44 	// Valid returns true if the index is within range for the list.
     45 	// It does not imply the label at that index will itself be valid.
     46 	Valid(index int) bool
     47 	// Label returns the label at the given index.
     48 	Label(index int) Label
     49 }
     50 
     51 // list implements LabelList for a list of Labels.
     52 type list struct {
     53 	labels []Label
     54 }
     55 
     56 // filter wraps a LabelList filtering out specific labels.
     57 type filter struct {
     58 	keys       []Key
     59 	underlying List
     60 }
     61 
     62 // listMap implements LabelMap for a simple list of labels.
     63 type listMap struct {
     64 	labels []Label
     65 }
     66 
     67 // mapChain implements LabelMap for a list of underlying LabelMap.
     68 type mapChain struct {
     69 	maps []Map
     70 }
     71 
     72 // OfValue creates a new label from the key and value.
     73 // This method is for implementing new key types, label creation should
     74 // normally be done with the Of method of the key.
     75 func OfValue(k Key, value any) Label { return Label{key: k, untyped: value} }
     76 
     77 // UnpackValue assumes the label was built using LabelOfValue and returns the value
     78 // that was passed to that constructor.
     79 // This method is for implementing new key types, for type safety normal
     80 // access should be done with the From method of the key.
     81 func (t Label) UnpackValue() any { return t.untyped }
     82 
     83 // Of64 creates a new label from a key and a uint64. This is often
     84 // used for non uint64 values that can be packed into a uint64.
     85 // This method is for implementing new key types, label creation should
     86 // normally be done with the Of method of the key.
     87 func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} }
     88 
     89 // Unpack64 assumes the label was built using LabelOf64 and returns the value that
     90 // was passed to that constructor.
     91 // This method is for implementing new key types, for type safety normal
     92 // access should be done with the From method of the key.
     93 func (t Label) Unpack64() uint64 { return t.packed }
     94 
     95 type stringptr unsafe.Pointer
     96 
     97 // OfString creates a new label from a key and a string.
     98 // This method is for implementing new key types, label creation should
     99 // normally be done with the Of method of the key.
    100 func OfString(k Key, v string) Label {
    101 	return Label{
    102 		key:     k,
    103 		packed:  uint64(len(v)),
    104 		untyped: stringptr(unsafe.StringData(v)),
    105 	}
    106 }
    107 
    108 // UnpackString assumes the label was built using LabelOfString and returns the
    109 // value that was passed to that constructor.
    110 // This method is for implementing new key types, for type safety normal
    111 // access should be done with the From method of the key.
    112 func (t Label) UnpackString() string {
    113 	return unsafe.String((*byte)(t.untyped.(stringptr)), int(t.packed))
    114 }
    115 
    116 // Valid returns true if the Label is a valid one (it has a key).
    117 func (t Label) Valid() bool { return t.key != nil }
    118 
    119 // Key returns the key of this Label.
    120 func (t Label) Key() Key { return t.key }
    121 
    122 // Format is used for debug printing of labels.
    123 func (t Label) Format(f fmt.State, r rune) {
    124 	if !t.Valid() {
    125 		io.WriteString(f, `nil`)
    126 		return
    127 	}
    128 	io.WriteString(f, t.Key().Name())
    129 	io.WriteString(f, "=")
    130 	f.Write(t.Key().Append(nil, t)) // ignore error
    131 }
    132 
    133 func (l *list) Valid(index int) bool {
    134 	return index >= 0 && index < len(l.labels)
    135 }
    136 
    137 func (l *list) Label(index int) Label {
    138 	return l.labels[index]
    139 }
    140 
    141 func (f *filter) Valid(index int) bool {
    142 	return f.underlying.Valid(index)
    143 }
    144 
    145 func (f *filter) Label(index int) Label {
    146 	l := f.underlying.Label(index)
    147 	if slices.Contains(f.keys, l.Key()) {
    148 		return Label{}
    149 	}
    150 	return l
    151 }
    152 
    153 func (lm listMap) Find(key Key) Label {
    154 	for _, l := range lm.labels {
    155 		if l.Key() == key {
    156 			return l
    157 		}
    158 	}
    159 	return Label{}
    160 }
    161 
    162 func (c mapChain) Find(key Key) Label {
    163 	for _, src := range c.maps {
    164 		l := src.Find(key)
    165 		if l.Valid() {
    166 			return l
    167 		}
    168 	}
    169 	return Label{}
    170 }
    171 
    172 var emptyList = &list{}
    173 
    174 func NewList(labels ...Label) List {
    175 	if len(labels) == 0 {
    176 		return emptyList
    177 	}
    178 	return &list{labels: labels}
    179 }
    180 
    181 func Filter(l List, keys ...Key) List {
    182 	if len(keys) == 0 {
    183 		return l
    184 	}
    185 	return &filter{keys: keys, underlying: l}
    186 }
    187 
    188 func NewMap(labels ...Label) Map {
    189 	return listMap{labels: labels}
    190 }
    191 
    192 func MergeMaps(srcs ...Map) Map {
    193 	var nonNil []Map
    194 	for _, src := range srcs {
    195 		if src != nil {
    196 			nonNil = append(nonNil, src)
    197 		}
    198 	}
    199 	if len(nonNil) == 1 {
    200 		return nonNil[0]
    201 	}
    202 	return mapChain{maps: nonNil}
    203 }