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)
 | 
						|
	}
 | 
						|
}
 |