mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 13:50:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			135 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			3.4 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 (
 | |
| 	"sigs.k8s.io/structured-merge-diff/v4/value"
 | |
| )
 | |
| 
 | |
| // SetFromValue creates a set containing every leaf field mentioned in v.
 | |
| func SetFromValue(v value.Value) *Set {
 | |
| 	s := NewSet()
 | |
| 
 | |
| 	w := objectWalker{
 | |
| 		path:      Path{},
 | |
| 		value:     v,
 | |
| 		allocator: value.NewFreelistAllocator(),
 | |
| 		do:        func(p Path) { s.Insert(p) },
 | |
| 	}
 | |
| 
 | |
| 	w.walk()
 | |
| 	return s
 | |
| }
 | |
| 
 | |
| type objectWalker struct {
 | |
| 	path      Path
 | |
| 	value     value.Value
 | |
| 	allocator value.Allocator
 | |
| 
 | |
| 	do func(Path)
 | |
| }
 | |
| 
 | |
| func (w *objectWalker) walk() {
 | |
| 	switch {
 | |
| 	case w.value.IsNull():
 | |
| 	case w.value.IsFloat():
 | |
| 	case w.value.IsInt():
 | |
| 	case w.value.IsString():
 | |
| 	case w.value.IsBool():
 | |
| 		// All leaf fields handled the same way (after the switch
 | |
| 		// statement).
 | |
| 
 | |
| 	// Descend
 | |
| 	case w.value.IsList():
 | |
| 		// If the list were atomic, we'd break here, but we don't have
 | |
| 		// a schema, so we can't tell.
 | |
| 		l := w.value.AsListUsing(w.allocator)
 | |
| 		defer w.allocator.Free(l)
 | |
| 		iter := l.RangeUsing(w.allocator)
 | |
| 		defer w.allocator.Free(iter)
 | |
| 		for iter.Next() {
 | |
| 			i, value := iter.Item()
 | |
| 			w2 := *w
 | |
| 			w2.path = append(w.path, w.GuessBestListPathElement(i, value))
 | |
| 			w2.value = value
 | |
| 			w2.walk()
 | |
| 		}
 | |
| 		return
 | |
| 	case w.value.IsMap():
 | |
| 		// If the map/struct were atomic, we'd break here, but we don't
 | |
| 		// have a schema, so we can't tell.
 | |
| 
 | |
| 		m := w.value.AsMapUsing(w.allocator)
 | |
| 		defer w.allocator.Free(m)
 | |
| 		m.IterateUsing(w.allocator, func(k string, val value.Value) bool {
 | |
| 			w2 := *w
 | |
| 			w2.path = append(w.path, PathElement{FieldName: &k})
 | |
| 			w2.value = val
 | |
| 			w2.walk()
 | |
| 			return true
 | |
| 		})
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Leaf fields get added to the set.
 | |
| 	if len(w.path) > 0 {
 | |
| 		w.do(w.path)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // AssociativeListCandidateFieldNames lists the field names which are
 | |
| // considered keys if found in a list element.
 | |
| var AssociativeListCandidateFieldNames = []string{
 | |
| 	"key",
 | |
| 	"id",
 | |
| 	"name",
 | |
| }
 | |
| 
 | |
| // GuessBestListPathElement guesses whether item is an associative list
 | |
| // element, which should be referenced by key(s), or if it is not and therefore
 | |
| // referencing by index is acceptable. Currently this is done by checking
 | |
| // whether item has any of the fields listed in
 | |
| // AssociativeListCandidateFieldNames which have scalar values.
 | |
| func (w *objectWalker) GuessBestListPathElement(index int, item value.Value) PathElement {
 | |
| 	if !item.IsMap() {
 | |
| 		// Non map items could be parts of sets or regular "atomic"
 | |
| 		// lists. We won't try to guess whether something should be a
 | |
| 		// set or not.
 | |
| 		return PathElement{Index: &index}
 | |
| 	}
 | |
| 
 | |
| 	m := item.AsMapUsing(w.allocator)
 | |
| 	defer w.allocator.Free(m)
 | |
| 	var keys value.FieldList
 | |
| 	for _, name := range AssociativeListCandidateFieldNames {
 | |
| 		f, ok := m.Get(name)
 | |
| 		if !ok {
 | |
| 			continue
 | |
| 		}
 | |
| 		// only accept primitive/scalar types as keys.
 | |
| 		if f.IsNull() || f.IsMap() || f.IsList() {
 | |
| 			continue
 | |
| 		}
 | |
| 		keys = append(keys, value.Field{Name: name, Value: f})
 | |
| 	}
 | |
| 	if len(keys) > 0 {
 | |
| 		keys.Sort()
 | |
| 		return PathElement{Key: &keys}
 | |
| 	}
 | |
| 	return PathElement{Index: &index}
 | |
| }
 |