@@ -17,162 +17,14 @@ limitations under the License.
1717package cel
1818
1919import (
20- "fmt"
21- "strings"
22-
2320 "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
21+ "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
22+ "k8s.io/apiserver/pkg/cel/common"
2423)
2524
26- // mapList provides a "lookup by key" operation for lists (arrays) with x-kubernetes-list-type=map.
27- type mapList interface {
28- // get returns the first element having given key, for all
29- // x-kubernetes-list-map-keys, to the provided object. If the provided object isn't itself a valid mapList element,
30- // get returns nil.
31- get (interface {}) interface {}
32- }
33-
34- type keyStrategy interface {
35- // CompositeKeyFor returns a composite key for the provided object, if possible, and a
36- // boolean that indicates whether or not a key could be generated for the provided object.
37- CompositeKeyFor (map [string ]interface {}) (interface {}, bool )
38- }
39-
40- // singleKeyStrategy is a cheaper strategy for associative lists that have exactly one key.
41- type singleKeyStrategy struct {
42- key string
43- }
44-
45- // CompositeKeyFor directly returns the value of the single key to
46- // use as a composite key.
47- func (ks * singleKeyStrategy ) CompositeKeyFor (obj map [string ]interface {}) (interface {}, bool ) {
48- v , ok := obj [ks .key ]
49- if ! ok {
50- return nil , false
51- }
52-
53- switch v .(type ) {
54- case bool , float64 , int64 , string :
55- return v , true
56- default :
57- return nil , false // non-scalar
58- }
59- }
60-
61- // multiKeyStrategy computes a composite key of all key values.
62- type multiKeyStrategy struct {
63- sts * schema.Structural
64- }
65-
66- // CompositeKeyFor returns a composite key computed from the values of all
67- // keys.
68- func (ks * multiKeyStrategy ) CompositeKeyFor (obj map [string ]interface {}) (interface {}, bool ) {
69- const keyDelimiter = "\x00 " // 0 byte should never appear in the composite key except as delimiter
70-
71- var delimited strings.Builder
72- for _ , key := range ks .sts .XListMapKeys {
73- v , ok := obj [key ]
74- if ! ok {
75- return nil , false
76- }
77-
78- switch v .(type ) {
79- case bool :
80- fmt .Fprintf (& delimited , keyDelimiter + "%t" , v )
81- case float64 :
82- fmt .Fprintf (& delimited , keyDelimiter + "%f" , v )
83- case int64 :
84- fmt .Fprintf (& delimited , keyDelimiter + "%d" , v )
85- case string :
86- fmt .Fprintf (& delimited , keyDelimiter + "%q" , v )
87- default :
88- return nil , false // values must be scalars
89- }
90- }
91- return delimited .String (), true
92- }
93-
94- // emptyMapList is a mapList containing no elements.
95- type emptyMapList struct {}
96-
97- func (emptyMapList ) get (interface {}) interface {} {
98- return nil
99- }
100-
101- type mapListImpl struct {
102- sts * schema.Structural
103- ks keyStrategy
104- // keyedItems contains all lazily keyed map items
105- keyedItems map [interface {}]interface {}
106- // unkeyedItems contains all map items that have not yet been keyed
107- unkeyedItems []interface {}
108- }
109-
110- func (a * mapListImpl ) get (obj interface {}) interface {} {
111- mobj , ok := obj .(map [string ]interface {})
112- if ! ok {
113- return nil
114- }
115-
116- key , ok := a .ks .CompositeKeyFor (mobj )
117- if ! ok {
118- return nil
119- }
120- if match , ok := a .keyedItems [key ]; ok {
121- return match
122- }
123- // keep keying items until we either find a match or run out of unkeyed items
124- for len (a .unkeyedItems ) > 0 {
125- // dequeue an unkeyed item
126- item := a .unkeyedItems [0 ]
127- a .unkeyedItems = a .unkeyedItems [1 :]
128-
129- // key the item
130- mitem , ok := item .(map [string ]interface {})
131- if ! ok {
132- continue
133- }
134- itemKey , ok := a .ks .CompositeKeyFor (mitem )
135- if ! ok {
136- continue
137- }
138- if _ , exists := a .keyedItems [itemKey ]; ! exists {
139- a .keyedItems [itemKey ] = mitem
140- }
141-
142- // if it matches, short-circuit
143- if itemKey == key {
144- return mitem
145- }
146- }
147-
148- return nil
149- }
150-
151- func makeKeyStrategy (sts * schema.Structural ) keyStrategy {
152- if len (sts .XListMapKeys ) == 1 {
153- key := sts .XListMapKeys [0 ]
154- return & singleKeyStrategy {
155- key : key ,
156- }
157- }
158-
159- return & multiKeyStrategy {
160- sts : sts ,
161- }
162- }
163-
16425// makeMapList returns a queryable interface over the provided x-kubernetes-list-type=map
16526// keyedItems. If the provided schema is _not_ an array with x-kubernetes-list-type=map, returns an
16627// empty mapList.
167- func makeMapList (sts * schema.Structural , items []interface {}) (rv mapList ) {
168- if sts .Type != "array" || sts .XListType == nil || * sts .XListType != "map" || len (sts .XListMapKeys ) == 0 || len (items ) == 0 {
169- return emptyMapList {}
170- }
171- ks := makeKeyStrategy (sts )
172- return & mapListImpl {
173- sts : sts ,
174- ks : ks ,
175- keyedItems : map [interface {}]interface {}{},
176- unkeyedItems : items ,
177- }
28+ func makeMapList (sts * schema.Structural , items []interface {}) (rv common.MapList ) {
29+ return common .MakeMapList (& model.Structural {Structural : sts }, items )
17830}
0 commit comments