123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- /*
- 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)
- }
- }
|