...

Source file src/sigs.k8s.io/structured-merge-diff/v4/typed/remove.go

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

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6      http://www.apache.org/licenses/LICENSE-2.0
     7  Unless required by applicable law or agreed to in writing, software
     8  distributed under the License is distributed on an "AS IS" BASIS,
     9  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  See the License for the specific language governing permissions and
    11  limitations under the License.
    12  */
    13  
    14  package typed
    15  
    16  import (
    17  	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
    18  	"sigs.k8s.io/structured-merge-diff/v4/schema"
    19  	"sigs.k8s.io/structured-merge-diff/v4/value"
    20  )
    21  
    22  type removingWalker struct {
    23  	value         value.Value
    24  	out           interface{}
    25  	schema        *schema.Schema
    26  	toRemove      *fieldpath.Set
    27  	allocator     value.Allocator
    28  	shouldExtract bool
    29  }
    30  
    31  // removeItemsWithSchema will walk the given value and look for items from the toRemove set.
    32  // Depending on whether shouldExtract is set true or false, it will return a modified version
    33  // of the input value with either:
    34  // 1. only the items in the toRemove set (when shouldExtract is true) or
    35  // 2. the items from the toRemove set removed from the value (when shouldExtract is false).
    36  func removeItemsWithSchema(val value.Value, toRemove *fieldpath.Set, schema *schema.Schema, typeRef schema.TypeRef, shouldExtract bool) value.Value {
    37  	w := &removingWalker{
    38  		value:         val,
    39  		schema:        schema,
    40  		toRemove:      toRemove,
    41  		allocator:     value.NewFreelistAllocator(),
    42  		shouldExtract: shouldExtract,
    43  	}
    44  	resolveSchema(schema, typeRef, val, w)
    45  	return value.NewValueInterface(w.out)
    46  }
    47  
    48  func (w *removingWalker) doScalar(t *schema.Scalar) ValidationErrors {
    49  	w.out = w.value.Unstructured()
    50  	return nil
    51  }
    52  
    53  func (w *removingWalker) doList(t *schema.List) (errs ValidationErrors) {
    54  	if !w.value.IsList() {
    55  		return nil
    56  	}
    57  	l := w.value.AsListUsing(w.allocator)
    58  	defer w.allocator.Free(l)
    59  	// If list is null or empty just return
    60  	if l == nil || l.Length() == 0 {
    61  		return nil
    62  	}
    63  
    64  	// atomic lists should return everything in the case of extract
    65  	// and nothing in the case of remove (!w.shouldExtract)
    66  	if t.ElementRelationship == schema.Atomic {
    67  		if w.shouldExtract {
    68  			w.out = w.value.Unstructured()
    69  		}
    70  		return nil
    71  	}
    72  
    73  	var newItems []interface{}
    74  	iter := l.RangeUsing(w.allocator)
    75  	defer w.allocator.Free(iter)
    76  	for iter.Next() {
    77  		_, item := iter.Item()
    78  		// Ignore error because we have already validated this list
    79  		pe, _ := listItemToPathElement(w.allocator, w.schema, t, item)
    80  		path, _ := fieldpath.MakePath(pe)
    81  		// save items on the path when we shouldExtract
    82  		// but ignore them when we are removing (i.e. !w.shouldExtract)
    83  		if w.toRemove.Has(path) {
    84  			if w.shouldExtract {
    85  				newItems = append(newItems, removeItemsWithSchema(item, w.toRemove, w.schema, t.ElementType, w.shouldExtract).Unstructured())
    86  			} else {
    87  				continue
    88  			}
    89  		}
    90  		if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
    91  			item = removeItemsWithSchema(item, subset, w.schema, t.ElementType, w.shouldExtract)
    92  		} else {
    93  			// don't save items not on the path when we shouldExtract.
    94  			if w.shouldExtract {
    95  				continue
    96  			}
    97  		}
    98  		newItems = append(newItems, item.Unstructured())
    99  	}
   100  	if len(newItems) > 0 {
   101  		w.out = newItems
   102  	}
   103  	return nil
   104  }
   105  
   106  func (w *removingWalker) doMap(t *schema.Map) ValidationErrors {
   107  	if !w.value.IsMap() {
   108  		return nil
   109  	}
   110  	m := w.value.AsMapUsing(w.allocator)
   111  	if m != nil {
   112  		defer w.allocator.Free(m)
   113  	}
   114  	// If map is null or empty just return
   115  	if m == nil || m.Empty() {
   116  		return nil
   117  	}
   118  
   119  	// atomic maps should return everything in the case of extract
   120  	// and nothing in the case of remove (!w.shouldExtract)
   121  	if t.ElementRelationship == schema.Atomic {
   122  		if w.shouldExtract {
   123  			w.out = w.value.Unstructured()
   124  		}
   125  		return nil
   126  	}
   127  
   128  	fieldTypes := map[string]schema.TypeRef{}
   129  	for _, structField := range t.Fields {
   130  		fieldTypes[structField.Name] = structField.Type
   131  	}
   132  
   133  	newMap := map[string]interface{}{}
   134  	m.Iterate(func(k string, val value.Value) bool {
   135  		pe := fieldpath.PathElement{FieldName: &k}
   136  		path, _ := fieldpath.MakePath(pe)
   137  		fieldType := t.ElementType
   138  		if ft, ok := fieldTypes[k]; ok {
   139  			fieldType = ft
   140  		}
   141  		// save values on the path when we shouldExtract
   142  		// but ignore them when we are removing (i.e. !w.shouldExtract)
   143  		if w.toRemove.Has(path) {
   144  			if w.shouldExtract {
   145  				newMap[k] = removeItemsWithSchema(val, w.toRemove, w.schema, fieldType, w.shouldExtract).Unstructured()
   146  
   147  			}
   148  			return true
   149  		}
   150  		if subset := w.toRemove.WithPrefix(pe); !subset.Empty() {
   151  			val = removeItemsWithSchema(val, subset, w.schema, fieldType, w.shouldExtract)
   152  		} else {
   153  			// don't save values not on the path when we shouldExtract.
   154  			if w.shouldExtract {
   155  				return true
   156  			}
   157  		}
   158  		newMap[k] = val.Unstructured()
   159  		return true
   160  	})
   161  	if len(newMap) > 0 {
   162  		w.out = newMap
   163  	}
   164  	return nil
   165  }
   166  

View as plain text