123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- /*
- 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/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,
- do: func(p Path) { s.Insert(p) },
- }
- w.walk()
- return s
- }
- type objectWalker struct {
- path Path
- value value.Value
- do func(Path)
- }
- func (w *objectWalker) walk() {
- switch {
- case w.value.Null:
- case w.value.FloatValue != nil:
- case w.value.IntValue != nil:
- case w.value.StringValue != nil:
- case w.value.BooleanValue != nil:
- // All leaf fields handled the same way (after the switch
- // statement).
- // Descend
- case w.value.ListValue != nil:
- // If the list were atomic, we'd break here, but we don't have
- // a schema, so we can't tell.
- for i, child := range w.value.ListValue.Items {
- w2 := *w
- w2.path = append(w.path, GuessBestListPathElement(i, child))
- w2.value = child
- w2.walk()
- }
- return
- case w.value.MapValue != nil:
- // If the map/struct were atomic, we'd break here, but we don't
- // have a schema, so we can't tell.
- for i := range w.value.MapValue.Items {
- child := w.value.MapValue.Items[i]
- w2 := *w
- w2.path = append(w.path, PathElement{FieldName: &child.Name})
- w2.value = child.Value
- w2.walk()
- }
- 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 GuessBestListPathElement(index int, item value.Value) PathElement {
- if item.MapValue == nil {
- // 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}
- }
- var keys []value.Field
- for _, name := range AssociativeListCandidateFieldNames {
- f, ok := item.MapValue.Get(name)
- if !ok {
- continue
- }
- // only accept primitive/scalar types as keys.
- if f.Value.Null || f.Value.MapValue != nil || f.Value.ListValue != nil {
- continue
- }
- keys = append(keys, *f)
- }
- if len(keys) > 0 {
- return PathElement{Key: keys}
- }
- return PathElement{Index: &index}
- }
|