95 lines
2.0 KiB
Go
95 lines
2.0 KiB
Go
package domain
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type Pattern struct {
|
|
Namespace string
|
|
Labels map[string]map[string]string
|
|
Exact bool
|
|
}
|
|
|
|
// Canonical returns a canonical string representation of the Pattern struct
|
|
// TODO: Ensure labels and namespaces are set to lowercase
|
|
func (p *Pattern) Canonical() string {
|
|
var b strings.Builder
|
|
b.Grow(len(p.Namespace) + 10*len(p.Labels) + 20) // preallocate a rough size estimate
|
|
|
|
b.WriteString(p.Namespace)
|
|
b.WriteString("::")
|
|
|
|
labelNames := make([]string, 0, len(p.Labels))
|
|
for name := range p.Labels {
|
|
labelNames = append(labelNames, name)
|
|
}
|
|
sort.Strings(labelNames) // sort the labels for determinism
|
|
|
|
for i, name := range labelNames {
|
|
if i > 0 {
|
|
b.WriteByte('|')
|
|
}
|
|
b.WriteString(name)
|
|
|
|
params := p.Labels[name]
|
|
if len(params) > 0 {
|
|
keys := make([]string, 0, len(params))
|
|
for k := range params {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys) // sort params for determinism
|
|
|
|
b.WriteByte('[')
|
|
for j, k := range keys {
|
|
if j > 0 {
|
|
b.WriteByte(';')
|
|
}
|
|
b.WriteString(k)
|
|
b.WriteByte('=')
|
|
b.WriteString(params[k])
|
|
}
|
|
b.WriteByte(']')
|
|
}
|
|
}
|
|
|
|
b.WriteString("::")
|
|
if p.Exact {
|
|
b.WriteString("t")
|
|
} else {
|
|
b.WriteString("f")
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
// Satisfies checks if a domain.Identifier satisfies the pattern.
|
|
func (p *Pattern) Satisfies(id Identifier) bool {
|
|
ns, idLabels, err := id.Parse()
|
|
if err != nil || ns != p.Namespace {
|
|
return false
|
|
}
|
|
|
|
// Every pattern label must be present in the identifier.
|
|
for lname, wantParams := range p.Labels {
|
|
haveParams, ok := idLabels[lname]
|
|
if !ok {
|
|
return false
|
|
}
|
|
// If pattern specifies params, they must be a subset of identifier's params.
|
|
for k, v := range wantParams {
|
|
hv, ok := haveParams[k]
|
|
if !ok || hv != v {
|
|
return false
|
|
}
|
|
}
|
|
// If pattern has no params for this label, it matches any/none params in the identifier.
|
|
}
|
|
|
|
// Exact applies to label names only: no extras allowed.
|
|
if p.Exact && len(idLabels) != len(p.Labels) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|