mirror of
https://github.com/k3s-io/k3s.git
synced 2024-06-07 19:41:36 +00:00
276 lines
7.1 KiB
Go
276 lines
7.1 KiB
Go
/*
|
|
Copyright 2018 The Kubernetes 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 fieldpath
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"sigs.k8s.io/structured-merge-diff/value"
|
|
)
|
|
|
|
// PathElement describes how to select a child field given a containing object.
|
|
type PathElement struct {
|
|
// Exactly one of the following fields should be non-nil.
|
|
|
|
// FieldName selects a single field from a map (reminder: this is also
|
|
// how structs are represented). The containing object must be a map.
|
|
FieldName *string
|
|
|
|
// Key selects the list element which has fields matching those given.
|
|
// The containing object must be an associative list with map typed
|
|
// elements.
|
|
Key *value.Map
|
|
|
|
// Value selects the list element with the given value. The containing
|
|
// object must be an associative list with a primitive typed element
|
|
// (i.e., a set).
|
|
Value *value.Value
|
|
|
|
// Index selects a list element by its index number. The containing
|
|
// object must be an atomic list.
|
|
Index *int
|
|
}
|
|
|
|
// Less provides an order for path elements.
|
|
func (e PathElement) Less(rhs PathElement) bool {
|
|
if e.FieldName != nil {
|
|
if rhs.FieldName == nil {
|
|
return true
|
|
}
|
|
return *e.FieldName < *rhs.FieldName
|
|
} else if rhs.FieldName != nil {
|
|
return false
|
|
}
|
|
|
|
if e.Key != nil {
|
|
if rhs.Key == nil {
|
|
return true
|
|
}
|
|
return e.Key.Less(rhs.Key)
|
|
} else if rhs.Key != nil {
|
|
return false
|
|
}
|
|
|
|
if e.Value != nil {
|
|
if rhs.Value == nil {
|
|
return true
|
|
}
|
|
return e.Value.Less(*rhs.Value)
|
|
} else if rhs.Value != nil {
|
|
return false
|
|
}
|
|
|
|
if e.Index != nil {
|
|
if rhs.Index == nil {
|
|
return true
|
|
}
|
|
return *e.Index < *rhs.Index
|
|
} else if rhs.Index != nil {
|
|
// Yes, I know the next statement is the same. But this way
|
|
// the obvious way of extending the function wil be bug-free.
|
|
return false
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Equals returns true if both path elements are equal.
|
|
func (e PathElement) Equals(rhs PathElement) bool {
|
|
return !e.Less(rhs) && !rhs.Less(e)
|
|
}
|
|
|
|
// String presents the path element as a human-readable string.
|
|
func (e PathElement) String() string {
|
|
switch {
|
|
case e.FieldName != nil:
|
|
return "." + *e.FieldName
|
|
case e.Key != nil:
|
|
strs := make([]string, len(e.Key.Items))
|
|
for i, k := range e.Key.Items {
|
|
strs[i] = fmt.Sprintf("%v=%v", k.Name, k.Value)
|
|
}
|
|
// The order must be canonical, since we use the string value
|
|
// in a set structure.
|
|
sort.Strings(strs)
|
|
return "[" + strings.Join(strs, ",") + "]"
|
|
case e.Value != nil:
|
|
return fmt.Sprintf("[=%v]", e.Value)
|
|
case e.Index != nil:
|
|
return fmt.Sprintf("[%v]", *e.Index)
|
|
default:
|
|
return "{{invalid path element}}"
|
|
}
|
|
}
|
|
|
|
// KeyByFields is a helper function which constructs a key for an associative
|
|
// list type. `nameValues` must have an even number of entries, alternating
|
|
// names (type must be string) with values (type must be value.Value). If these
|
|
// conditions are not met, KeyByFields will panic--it's intended for static
|
|
// construction and shouldn't have user-produced values passed to it.
|
|
func KeyByFields(nameValues ...interface{}) []value.Field {
|
|
if len(nameValues)%2 != 0 {
|
|
panic("must have a value for every name")
|
|
}
|
|
out := []value.Field{}
|
|
for i := 0; i < len(nameValues)-1; i += 2 {
|
|
out = append(out, value.Field{
|
|
Name: nameValues[i].(string),
|
|
Value: nameValues[i+1].(value.Value),
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
// PathElementSet is a set of path elements.
|
|
// TODO: serialize as a list.
|
|
type PathElementSet struct {
|
|
members sortedPathElements
|
|
}
|
|
|
|
type sortedPathElements []PathElement
|
|
|
|
// Implement the sort interface; this would permit bulk creation, which would
|
|
// be faster than doing it one at a time via Insert.
|
|
func (spe sortedPathElements) Len() int { return len(spe) }
|
|
func (spe sortedPathElements) Less(i, j int) bool { return spe[i].Less(spe[j]) }
|
|
func (spe sortedPathElements) Swap(i, j int) { spe[i], spe[j] = spe[j], spe[i] }
|
|
|
|
// Insert adds pe to the set.
|
|
func (s *PathElementSet) Insert(pe PathElement) {
|
|
loc := sort.Search(len(s.members), func(i int) bool {
|
|
return !s.members[i].Less(pe)
|
|
})
|
|
if loc == len(s.members) {
|
|
s.members = append(s.members, pe)
|
|
return
|
|
}
|
|
if s.members[loc].Equals(pe) {
|
|
return
|
|
}
|
|
s.members = append(s.members, PathElement{})
|
|
copy(s.members[loc+1:], s.members[loc:])
|
|
s.members[loc] = pe
|
|
}
|
|
|
|
// Union returns a set containing elements that appear in either s or s2.
|
|
func (s *PathElementSet) Union(s2 *PathElementSet) *PathElementSet {
|
|
out := &PathElementSet{}
|
|
|
|
i, j := 0, 0
|
|
for i < len(s.members) && j < len(s2.members) {
|
|
if s.members[i].Less(s2.members[j]) {
|
|
out.members = append(out.members, s.members[i])
|
|
i++
|
|
} else {
|
|
out.members = append(out.members, s2.members[j])
|
|
if !s2.members[j].Less(s.members[i]) {
|
|
i++
|
|
}
|
|
j++
|
|
}
|
|
}
|
|
|
|
if i < len(s.members) {
|
|
out.members = append(out.members, s.members[i:]...)
|
|
}
|
|
if j < len(s2.members) {
|
|
out.members = append(out.members, s2.members[j:]...)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Intersection returns a set containing elements which appear in both s and s2.
|
|
func (s *PathElementSet) Intersection(s2 *PathElementSet) *PathElementSet {
|
|
out := &PathElementSet{}
|
|
|
|
i, j := 0, 0
|
|
for i < len(s.members) && j < len(s2.members) {
|
|
if s.members[i].Less(s2.members[j]) {
|
|
i++
|
|
} else {
|
|
if !s2.members[j].Less(s.members[i]) {
|
|
out.members = append(out.members, s.members[i])
|
|
i++
|
|
}
|
|
j++
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
// Difference returns a set containing elements which appear in s but not in s2.
|
|
func (s *PathElementSet) Difference(s2 *PathElementSet) *PathElementSet {
|
|
out := &PathElementSet{}
|
|
|
|
i, j := 0, 0
|
|
for i < len(s.members) && j < len(s2.members) {
|
|
if s.members[i].Less(s2.members[j]) {
|
|
out.members = append(out.members, s.members[i])
|
|
i++
|
|
} else {
|
|
if !s2.members[j].Less(s.members[i]) {
|
|
i++
|
|
}
|
|
j++
|
|
}
|
|
}
|
|
if i < len(s.members) {
|
|
out.members = append(out.members, s.members[i:]...)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Size retuns the number of elements in the set.
|
|
func (s *PathElementSet) Size() int { return len(s.members) }
|
|
|
|
// Has returns true if pe is a member of the set.
|
|
func (s *PathElementSet) Has(pe PathElement) bool {
|
|
loc := sort.Search(len(s.members), func(i int) bool {
|
|
return !s.members[i].Less(pe)
|
|
})
|
|
if loc == len(s.members) {
|
|
return false
|
|
}
|
|
if s.members[loc].Equals(pe) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Equals returns true if s and s2 have exactly the same members.
|
|
func (s *PathElementSet) Equals(s2 *PathElementSet) bool {
|
|
if len(s.members) != len(s2.members) {
|
|
return false
|
|
}
|
|
for k := range s.members {
|
|
if !s.members[k].Equals(s2.members[k]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Iterate calls f for each PathElement in the set. The order is deterministic.
|
|
func (s *PathElementSet) Iterate(f func(PathElement)) {
|
|
for _, pe := range s.members {
|
|
f(pe)
|
|
}
|
|
}
|