mirror of
https://github.com/rocky-linux/peridot.git
synced 2024-11-30 16:46:27 +00:00
318 lines
7.9 KiB
Go
318 lines
7.9 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/v4/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. They are sorted alphabetically.
|
||
|
Key *value.FieldList
|
||
|
|
||
|
// 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 {
|
||
|
return e.Compare(rhs) < 0
|
||
|
}
|
||
|
|
||
|
// Compare provides an order for path elements.
|
||
|
func (e PathElement) Compare(rhs PathElement) int {
|
||
|
if e.FieldName != nil {
|
||
|
if rhs.FieldName == nil {
|
||
|
return -1
|
||
|
}
|
||
|
return strings.Compare(*e.FieldName, *rhs.FieldName)
|
||
|
} else if rhs.FieldName != nil {
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
if e.Key != nil {
|
||
|
if rhs.Key == nil {
|
||
|
return -1
|
||
|
}
|
||
|
return e.Key.Compare(*rhs.Key)
|
||
|
} else if rhs.Key != nil {
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
if e.Value != nil {
|
||
|
if rhs.Value == nil {
|
||
|
return -1
|
||
|
}
|
||
|
return value.Compare(*e.Value, *rhs.Value)
|
||
|
} else if rhs.Value != nil {
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
if e.Index != nil {
|
||
|
if rhs.Index == nil {
|
||
|
return -1
|
||
|
}
|
||
|
if *e.Index < *rhs.Index {
|
||
|
return -1
|
||
|
} else if *e.Index == *rhs.Index {
|
||
|
return 0
|
||
|
}
|
||
|
return 1
|
||
|
} else if rhs.Index != nil {
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
// Equals returns true if both path elements are equal.
|
||
|
func (e PathElement) Equals(rhs PathElement) bool {
|
||
|
if e.FieldName != nil {
|
||
|
if rhs.FieldName == nil {
|
||
|
return false
|
||
|
}
|
||
|
return *e.FieldName == *rhs.FieldName
|
||
|
} else if rhs.FieldName != nil {
|
||
|
return false
|
||
|
}
|
||
|
if e.Key != nil {
|
||
|
if rhs.Key == nil {
|
||
|
return false
|
||
|
}
|
||
|
return e.Key.Equals(*rhs.Key)
|
||
|
} else if rhs.Key != nil {
|
||
|
return false
|
||
|
}
|
||
|
if e.Value != nil {
|
||
|
if rhs.Value == nil {
|
||
|
return false
|
||
|
}
|
||
|
return value.Equals(*e.Value, *rhs.Value)
|
||
|
} else if rhs.Value != nil {
|
||
|
return false
|
||
|
}
|
||
|
if e.Index != nil {
|
||
|
if rhs.Index == nil {
|
||
|
return false
|
||
|
}
|
||
|
return *e.Index == *rhs.Index
|
||
|
} else if rhs.Index != nil {
|
||
|
return false
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// 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))
|
||
|
for i, k := range *e.Key {
|
||
|
strs[i] = fmt.Sprintf("%v=%v", k.Name, value.ToString(k.Value))
|
||
|
}
|
||
|
// Keys are supposed to be sorted.
|
||
|
return "[" + strings.Join(strs, ",") + "]"
|
||
|
case e.Value != nil:
|
||
|
return fmt.Sprintf("[=%v]", value.ToString(*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.FieldList {
|
||
|
if len(nameValues)%2 != 0 {
|
||
|
panic("must have a value for every name")
|
||
|
}
|
||
|
out := value.FieldList{}
|
||
|
for i := 0; i < len(nameValues)-1; i += 2 {
|
||
|
out = append(out, value.Field{Name: nameValues[i].(string), Value: value.NewValueInterface(nameValues[i+1])})
|
||
|
}
|
||
|
out.Sort()
|
||
|
return &out
|
||
|
}
|
||
|
|
||
|
// PathElementSet is a set of path elements.
|
||
|
// TODO: serialize as a list.
|
||
|
type PathElementSet struct {
|
||
|
members sortedPathElements
|
||
|
}
|
||
|
|
||
|
func MakePathElementSet(size int) PathElementSet {
|
||
|
return PathElementSet{
|
||
|
members: make(sortedPathElements, 0, size),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
}
|