...

Source file src/sigs.k8s.io/structured-merge-diff/v4/fieldpath/fromvalue.go

Documentation: sigs.k8s.io/structured-merge-diff/v4/fieldpath

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package fieldpath
    18  
    19  import (
    20  	"sigs.k8s.io/structured-merge-diff/v4/value"
    21  )
    22  
    23  // SetFromValue creates a set containing every leaf field mentioned in v.
    24  func SetFromValue(v value.Value) *Set {
    25  	s := NewSet()
    26  
    27  	w := objectWalker{
    28  		path:      Path{},
    29  		value:     v,
    30  		allocator: value.NewFreelistAllocator(),
    31  		do:        func(p Path) { s.Insert(p) },
    32  	}
    33  
    34  	w.walk()
    35  	return s
    36  }
    37  
    38  type objectWalker struct {
    39  	path      Path
    40  	value     value.Value
    41  	allocator value.Allocator
    42  
    43  	do func(Path)
    44  }
    45  
    46  func (w *objectWalker) walk() {
    47  	switch {
    48  	case w.value.IsNull():
    49  	case w.value.IsFloat():
    50  	case w.value.IsInt():
    51  	case w.value.IsString():
    52  	case w.value.IsBool():
    53  		// All leaf fields handled the same way (after the switch
    54  		// statement).
    55  
    56  	// Descend
    57  	case w.value.IsList():
    58  		// If the list were atomic, we'd break here, but we don't have
    59  		// a schema, so we can't tell.
    60  		l := w.value.AsListUsing(w.allocator)
    61  		defer w.allocator.Free(l)
    62  		iter := l.RangeUsing(w.allocator)
    63  		defer w.allocator.Free(iter)
    64  		for iter.Next() {
    65  			i, value := iter.Item()
    66  			w2 := *w
    67  			w2.path = append(w.path, w.GuessBestListPathElement(i, value))
    68  			w2.value = value
    69  			w2.walk()
    70  		}
    71  		return
    72  	case w.value.IsMap():
    73  		// If the map/struct were atomic, we'd break here, but we don't
    74  		// have a schema, so we can't tell.
    75  
    76  		m := w.value.AsMapUsing(w.allocator)
    77  		defer w.allocator.Free(m)
    78  		m.IterateUsing(w.allocator, func(k string, val value.Value) bool {
    79  			w2 := *w
    80  			w2.path = append(w.path, PathElement{FieldName: &k})
    81  			w2.value = val
    82  			w2.walk()
    83  			return true
    84  		})
    85  		return
    86  	}
    87  
    88  	// Leaf fields get added to the set.
    89  	if len(w.path) > 0 {
    90  		w.do(w.path)
    91  	}
    92  }
    93  
    94  // AssociativeListCandidateFieldNames lists the field names which are
    95  // considered keys if found in a list element.
    96  var AssociativeListCandidateFieldNames = []string{
    97  	"key",
    98  	"id",
    99  	"name",
   100  }
   101  
   102  // GuessBestListPathElement guesses whether item is an associative list
   103  // element, which should be referenced by key(s), or if it is not and therefore
   104  // referencing by index is acceptable. Currently this is done by checking
   105  // whether item has any of the fields listed in
   106  // AssociativeListCandidateFieldNames which have scalar values.
   107  func (w *objectWalker) GuessBestListPathElement(index int, item value.Value) PathElement {
   108  	if !item.IsMap() {
   109  		// Non map items could be parts of sets or regular "atomic"
   110  		// lists. We won't try to guess whether something should be a
   111  		// set or not.
   112  		return PathElement{Index: &index}
   113  	}
   114  
   115  	m := item.AsMapUsing(w.allocator)
   116  	defer w.allocator.Free(m)
   117  	var keys value.FieldList
   118  	for _, name := range AssociativeListCandidateFieldNames {
   119  		f, ok := m.Get(name)
   120  		if !ok {
   121  			continue
   122  		}
   123  		// only accept primitive/scalar types as keys.
   124  		if f.IsNull() || f.IsMap() || f.IsList() {
   125  			continue
   126  		}
   127  		keys = append(keys, value.Field{Name: name, Value: f})
   128  	}
   129  	if len(keys) > 0 {
   130  		keys.Sort()
   131  		return PathElement{Key: &keys}
   132  	}
   133  	return PathElement{Index: &index}
   134  }
   135  

View as plain text