k8s.io/apiserver@v0.31.1/pkg/cel/lazy/lazy.go (about)

     1  /*
     2  Copyright 2023 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 lazy
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  
    23  	"github.com/google/cel-go/common/types"
    24  	"github.com/google/cel-go/common/types/ref"
    25  	"github.com/google/cel-go/common/types/traits"
    26  
    27  	"k8s.io/apiserver/pkg/cel"
    28  )
    29  
    30  type GetFieldFunc func(*MapValue) ref.Val
    31  
    32  var _ ref.Val = (*MapValue)(nil)
    33  var _ traits.Mapper = (*MapValue)(nil)
    34  
    35  // MapValue is a map that lazily evaluate its value when a field is first accessed.
    36  // The map value is not designed to be thread-safe.
    37  type MapValue struct {
    38  	typeValue *types.Type
    39  
    40  	// values are previously evaluated values obtained from callbacks
    41  	values map[string]ref.Val
    42  	// callbacks are a map of field name to the function that returns the field Val
    43  	callbacks map[string]GetFieldFunc
    44  	// knownValues are registered names, used for iteration
    45  	knownValues []string
    46  }
    47  
    48  func NewMapValue(objectType ref.Type) *MapValue {
    49  	return &MapValue{
    50  		typeValue: types.NewTypeValue(objectType.TypeName(), traits.IndexerType|traits.FieldTesterType|traits.IterableType),
    51  		values:    map[string]ref.Val{},
    52  		callbacks: map[string]GetFieldFunc{},
    53  	}
    54  }
    55  
    56  // Append adds the given field with its name and callback.
    57  func (m *MapValue) Append(name string, callback GetFieldFunc) {
    58  	m.knownValues = append(m.knownValues, name)
    59  	m.callbacks[name] = callback
    60  }
    61  
    62  // Contains checks if the key is known to the map
    63  func (m *MapValue) Contains(key ref.Val) ref.Val {
    64  	v, found := m.Find(key)
    65  	if v != nil && types.IsUnknownOrError(v) {
    66  		return v
    67  	}
    68  	return types.Bool(found)
    69  }
    70  
    71  // Iterator returns an iterator to traverse the map.
    72  func (m *MapValue) Iterator() traits.Iterator {
    73  	return &iterator{parent: m, index: 0}
    74  }
    75  
    76  // Size returns the number of currently known fields
    77  func (m *MapValue) Size() ref.Val {
    78  	return types.Int(len(m.callbacks))
    79  }
    80  
    81  // ConvertToNative returns an error because it is disallowed
    82  func (m *MapValue) ConvertToNative(typeDesc reflect.Type) (any, error) {
    83  	return nil, fmt.Errorf("disallowed conversion from %q to %q", m.typeValue.TypeName(), typeDesc.Name())
    84  }
    85  
    86  // ConvertToType converts the map to the given type.
    87  // Only its own type and "Type" type are allowed.
    88  func (m *MapValue) ConvertToType(typeVal ref.Type) ref.Val {
    89  	switch typeVal {
    90  	case m.typeValue:
    91  		return m
    92  	case types.TypeType:
    93  		return m.typeValue
    94  	}
    95  	return types.NewErr("disallowed conversion from %q to %q", m.typeValue.TypeName(), typeVal.TypeName())
    96  }
    97  
    98  // Equal returns true if the other object is the same pointer-wise.
    99  func (m *MapValue) Equal(other ref.Val) ref.Val {
   100  	otherMap, ok := other.(*MapValue)
   101  	if !ok {
   102  		return types.MaybeNoSuchOverloadErr(other)
   103  	}
   104  	return types.Bool(m == otherMap)
   105  }
   106  
   107  // Type returns its registered type.
   108  func (m *MapValue) Type() ref.Type {
   109  	return m.typeValue
   110  }
   111  
   112  // Value is not allowed.
   113  func (m *MapValue) Value() any {
   114  	return types.NoSuchOverloadErr()
   115  }
   116  
   117  // resolveField resolves the field. Calls the callback if the value is not yet stored.
   118  func (m *MapValue) resolveField(name string) ref.Val {
   119  	v, seen := m.values[name]
   120  	if seen {
   121  		return v
   122  	}
   123  	f := m.callbacks[name]
   124  	v = f(m)
   125  	m.values[name] = v
   126  	return v
   127  }
   128  
   129  func (m *MapValue) Find(key ref.Val) (ref.Val, bool) {
   130  	n, ok := key.(types.String)
   131  	if !ok {
   132  		return types.MaybeNoSuchOverloadErr(n), true
   133  	}
   134  	name, ok := cel.Unescape(n.Value().(string))
   135  	if !ok {
   136  		return nil, false
   137  	}
   138  	if _, exists := m.callbacks[name]; !exists {
   139  		return nil, false
   140  	}
   141  	return m.resolveField(name), true
   142  }
   143  
   144  func (m *MapValue) Get(key ref.Val) ref.Val {
   145  	v, found := m.Find(key)
   146  	if found {
   147  		return v
   148  	}
   149  	return types.ValOrErr(key, "no such key: %v", key)
   150  }
   151  
   152  type iterator struct {
   153  	parent *MapValue
   154  	index  int
   155  }
   156  
   157  func (i *iterator) ConvertToNative(typeDesc reflect.Type) (any, error) {
   158  	return nil, fmt.Errorf("disallowed conversion to %q", typeDesc.Name())
   159  }
   160  
   161  func (i *iterator) ConvertToType(typeValue ref.Type) ref.Val {
   162  	return types.NewErr("disallowed conversion o %q", typeValue.TypeName())
   163  }
   164  
   165  func (i *iterator) Equal(other ref.Val) ref.Val {
   166  	otherIterator, ok := other.(*iterator)
   167  	if !ok {
   168  		return types.MaybeNoSuchOverloadErr(other)
   169  	}
   170  	return types.Bool(otherIterator == i)
   171  }
   172  
   173  func (i *iterator) Type() ref.Type {
   174  	return types.IteratorType
   175  }
   176  
   177  func (i *iterator) Value() any {
   178  	return nil
   179  }
   180  
   181  func (i *iterator) HasNext() ref.Val {
   182  	return types.Bool(i.index < len(i.parent.knownValues))
   183  }
   184  
   185  func (i *iterator) Next() ref.Val {
   186  	ret := i.parent.Get(types.String(i.parent.knownValues[i.index]))
   187  	i.index++
   188  	return ret
   189  }
   190  
   191  var _ traits.Iterator = (*iterator)(nil)