mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-30 21:30:16 +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)
 | |
| 	}
 | |
| }
 |