diff --git a/pkg/framework/predicates/interface.go b/pkg/framework/path/predicates/interface.go similarity index 100% rename from pkg/framework/predicates/interface.go rename to pkg/framework/path/predicates/interface.go diff --git a/pkg/framework/predicates/interface_test.go b/pkg/framework/path/predicates/interface_test.go similarity index 98% rename from pkg/framework/predicates/interface_test.go rename to pkg/framework/path/predicates/interface_test.go index cba21b28e..15f5439dd 100644 --- a/pkg/framework/predicates/interface_test.go +++ b/pkg/framework/path/predicates/interface_test.go @@ -19,7 +19,7 @@ package predicates_test import ( "testing" - . "k8s.io/kubectl/pkg/framework/predicates" + . "k8s.io/kubectl/pkg/framework/path/predicates" ) type InterfaceTrue struct{} diff --git a/pkg/framework/predicates/map.go b/pkg/framework/path/predicates/map.go similarity index 100% rename from pkg/framework/predicates/map.go rename to pkg/framework/path/predicates/map.go diff --git a/pkg/framework/predicates/map_test.go b/pkg/framework/path/predicates/map_test.go similarity index 98% rename from pkg/framework/predicates/map_test.go rename to pkg/framework/path/predicates/map_test.go index 34baa5a6a..571b89b6f 100644 --- a/pkg/framework/predicates/map_test.go +++ b/pkg/framework/path/predicates/map_test.go @@ -19,7 +19,7 @@ package predicates_test import ( "testing" - . "k8s.io/kubectl/pkg/framework/predicates" + . "k8s.io/kubectl/pkg/framework/path/predicates" ) type MapTrue struct{} diff --git a/pkg/framework/predicates/number.go b/pkg/framework/path/predicates/number.go similarity index 100% rename from pkg/framework/predicates/number.go rename to pkg/framework/path/predicates/number.go diff --git a/pkg/framework/predicates/number_test.go b/pkg/framework/path/predicates/number_test.go similarity index 98% rename from pkg/framework/predicates/number_test.go rename to pkg/framework/path/predicates/number_test.go index b4e78347a..8bdfdd329 100644 --- a/pkg/framework/predicates/number_test.go +++ b/pkg/framework/path/predicates/number_test.go @@ -20,7 +20,7 @@ import ( "fmt" "testing" - . "k8s.io/kubectl/pkg/framework/predicates" + . "k8s.io/kubectl/pkg/framework/path/predicates" ) // This example shows you how you can create a IntP, and how it's use to diff --git a/pkg/framework/predicates/slice.go b/pkg/framework/path/predicates/slice.go similarity index 100% rename from pkg/framework/predicates/slice.go rename to pkg/framework/path/predicates/slice.go diff --git a/pkg/framework/predicates/slice_test.go b/pkg/framework/path/predicates/slice_test.go similarity index 98% rename from pkg/framework/predicates/slice_test.go rename to pkg/framework/path/predicates/slice_test.go index 96accd178..f4e576d19 100644 --- a/pkg/framework/predicates/slice_test.go +++ b/pkg/framework/path/predicates/slice_test.go @@ -19,7 +19,7 @@ package predicates_test import ( "testing" - . "k8s.io/kubectl/pkg/framework/predicates" + . "k8s.io/kubectl/pkg/framework/path/predicates" ) type SliceTrue struct{} diff --git a/pkg/framework/predicates/string.go b/pkg/framework/path/predicates/string.go similarity index 100% rename from pkg/framework/predicates/string.go rename to pkg/framework/path/predicates/string.go diff --git a/pkg/framework/predicates/string_test.go b/pkg/framework/path/predicates/string_test.go similarity index 98% rename from pkg/framework/predicates/string_test.go rename to pkg/framework/path/predicates/string_test.go index 34f6a094b..b7870a907 100644 --- a/pkg/framework/predicates/string_test.go +++ b/pkg/framework/path/predicates/string_test.go @@ -20,7 +20,7 @@ import ( "regexp" "testing" - . "k8s.io/kubectl/pkg/framework/predicates" + . "k8s.io/kubectl/pkg/framework/path/predicates" ) func TestStringEqual(t *testing.T) { diff --git a/pkg/framework/unstructpath/doc.go b/pkg/framework/path/selectors/doc.go similarity index 89% rename from pkg/framework/unstructpath/doc.go rename to pkg/framework/path/selectors/doc.go index b13e8d52f..8b3162793 100644 --- a/pkg/framework/unstructpath/doc.go +++ b/pkg/framework/path/selectors/doc.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// This package helps you find specific fields in your unstruct +// This package helps you find specific fields in your interface{} // object. It is similar to what you can do with jsonpath, but reads the // path from strongly typed object, not strings. -package unstructpath +package selectors diff --git a/pkg/framework/unstructpath/example_test.go b/pkg/framework/path/selectors/example_test.go similarity index 75% rename from pkg/framework/unstructpath/example_test.go rename to pkg/framework/path/selectors/example_test.go index f310cec9a..f21fac58a 100644 --- a/pkg/framework/unstructpath/example_test.go +++ b/pkg/framework/path/selectors/example_test.go @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unstructpath +package selectors import ( "github.com/ghodss/yaml" - p "k8s.io/kubectl/pkg/framework/predicates" + p "k8s.io/kubectl/pkg/framework/path/predicates" ) // This example is inspired from http://goessner.net/articles/JsonPath/#e3. @@ -61,31 +61,31 @@ func Example() { } // The authors of all books in the store. Returns a list of strings. - Children().Map().Field("book").Children().Map().Field("author").String().SelectFrom(u) + Children().Field("book").Children().Field("author").AsString().SelectFrom(u) // All authors. Returns a list of strings. - All().Map().Field("author").String().SelectFrom(u) + All().Field("author").AsString().SelectFrom(u) - // All things in store, which are some books and a red bicycle. Returns a list of interface{}. - Map().Field("store").Children().SelectFrom(u) + // All things in store, which are some books and a red bicycle. Returns a list of maps. + Field("store").Children().AsMap().SelectFrom(u) // The price of everything in the store. Returns a list of "float64". - Map().Field("store").All().Map().Field("price").Number().SelectFrom(u) + Field("store").All().Field("price").AsNumber().SelectFrom(u) // The third book. Returns a list of 1 interface{}. - All().Map().Field("book").Slice().At(2).SelectFrom(u) + All().Field("book").At(2).SelectFrom(u) // The last book in order. Return a list of 1 interface{}. - All().Map().Field("book").Slice().Last().SelectFrom(u) + All().Field("book").Last().SelectFrom(u) // The first two books. Returns a list of 2 interface{}. - All().Map().Field("book").Slice().AtP(p.NumberLessThan(2)).SelectFrom(u) + All().Field("book").AtP(p.NumberLessThan(2)).SelectFrom(u) // Filter all books with isbn number. Returns a list of interface{}. - All().Map().Field("book").Filter(Map().Field("isbn")).SelectFrom(u) + All().Field("book").Filter(Field("isbn")).SelectFrom(u) // Filter all books cheaper than 10. Returns a list of interface{}. - All().Map().Field("book").Children().Filter(Map().Field("price").Number().Filter(p.NumberLessThan(10))).SelectFrom(u) + All().Field("book").Children().Filter(Field("price").AsNumber().Filter(p.NumberLessThan(10))).SelectFrom(u) // All elements in structure. Returns a list of interface{}. All().SelectFrom(u) diff --git a/pkg/framework/path/selectors/interface.go b/pkg/framework/path/selectors/interface.go new file mode 100644 index 000000000..8867bed16 --- /dev/null +++ b/pkg/framework/path/selectors/interface.go @@ -0,0 +1,197 @@ +/* +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 selectors + +import ( + p "k8s.io/kubectl/pkg/framework/path/predicates" +) + +// Interface is a "interface selector". It filters interfaces based on the +// "filtered" predicates. +type Interface interface { + // Interface can be used as a Interface predicate. If the selector can't + // select any interface from the interface, then the predicate is + // false. + p.Interface + + // SelectFrom finds interfaces from interfaces using this selector. The + // list can be bigger or smaller than the initial lists, + // depending on the select criterias. + SelectFrom(...interface{}) []interface{} + + // AsMap returns a selector that selects Maps from the given + // values. + AsMap() Map + // AsSlice returns a selector that selects Slices from the given + // values. + AsSlice() Slice + // Number returns a selector taht selects Numbers from the given values. + AsNumber() Number + // String returns a selector that selects strings from the given values. + AsString() String + + // Field returns the interface pointed by this specific field in the + // map. If the field doesn't exist, the value will be filtered + // out. + Field(string) Interface + // FieldP returns all the interfaces pointed by field that match the + // string predicate. This selector can return more values than + // it gets (for one map, it can returns multiple sub-values, one + // for each field that matches the predicate). + FieldP(...p.String) Interface + + // At returns a selector that select the child at the given + // index, if the list has such an index. Otherwise, nothing is + // returned. + At(index int) Interface + // AtP returns a selector that selects all the item whose index + // matches the number predicate. More predicates can be given, + // they are "and"-ed by this method. + AtP(ips ...p.Number) Interface + // Last returns a selector that selects the last value of the + // list. If the list is empty, then nothing will be selected. + Last() Interface + + // Children returns a selector that selects the direct children + // of the given values. + Children() Interface + // All returns a selector that selects all direct and indrect + // children of the given values. + All() Interface + + // Filter will create a new String that filters only the values + // who match the predicate. + Filter(...p.Interface) Interface +} + +// Field returns the interface pointed by this specific field in the +// map. If the field doesn't exist, the value will be filtered +// out. +func Field(field string) Interface { + return FieldP(p.StringEqual(field)) +} + +// FieldP returns all the interfaces pointed by field that match the +// string predicate. This selector can return more values than +// it gets (for one map, it can returns multiple sub-values, one +// for each field that matches the predicate). +func FieldP(predicates ...p.String) Interface { + return &interfaceS{vf: interfaceFieldPFilter{sp: p.StringAnd(predicates...)}} +} + +// At returns a selector that select the child at the given +// index, if the list has such an index. Otherwise, nothing is +// returned. +func At(index int) Interface { + return AtP(p.NumberEqual(float64(index))) +} + +// AtP returns a selector that selects all the item whose index +// matches the number predicate. More predicates can be given, +// they are "and"-ed by this method. +func AtP(ips ...p.Number) Interface { + return &interfaceS{vf: interfaceAtPFilter{ip: p.NumberAnd(ips...)}} +} + +// Last returns a selector that selects the last value of the +// list. If the list is empty, then nothing will be selected. +func Last() Interface { + return &interfaceS{vf: interfaceLastFilter{}} +} + +// Children selects all the children of the values. +func Children() Interface { + return &interfaceS{vf: interfaceChildrenFilter{}} +} + +// All selects all the direct and indirect childrens of the values. +func All() Interface { + return &interfaceS{vf: interfaceAllFilter{}} +} + +// Filter will only return the values that match the predicate. +func Filter(predicates ...p.Interface) Interface { + return &interfaceS{vf: &interfaceFilterP{vp: p.InterfaceAnd(predicates...)}} +} + +// Interface is a "Interface Selector". It selects a list of values, maps, +// slices, strings, integer from a list of values. +type interfaceS struct { + vs Interface + vf interfaceFilter +} + +// Match returns true if the selector can find items in the given +// value. Otherwise, it returns false. +func (s *interfaceS) Match(value interface{}) bool { + return len(s.SelectFrom(value)) != 0 +} + +func (s *interfaceS) SelectFrom(interfaces ...interface{}) []interface{} { + if s.vs != nil { + interfaces = s.vs.SelectFrom(interfaces...) + } + return s.vf.SelectFrom(interfaces...) +} + +func (s *interfaceS) AsMap() Map { + return &mapS{vs: s} +} + +func (s *interfaceS) AsSlice() Slice { + return &sliceS{vs: s} +} + +func (s *interfaceS) AsNumber() Number { + return &numberS{vs: s} +} + +func (s *interfaceS) AsString() String { + return &stringS{vs: s} +} + +func (s *interfaceS) At(index int) Interface { + return s.AtP(p.NumberEqual(float64(index))) +} + +func (s *interfaceS) AtP(predicates ...p.Number) Interface { + return &interfaceS{vs: s, vf: interfaceAtPFilter{ip: p.NumberAnd(predicates...)}} +} + +func (s *interfaceS) Last() Interface { + return &interfaceS{vs: s, vf: interfaceLastFilter{}} +} + +func (s *interfaceS) Field(key string) Interface { + return s.FieldP(p.StringEqual(key)) +} + +func (s *interfaceS) FieldP(predicates ...p.String) Interface { + return &interfaceS{vs: s, vf: interfaceFieldPFilter{sp: p.StringAnd(predicates...)}} +} + +func (s *interfaceS) Children() Interface { + return &interfaceS{vs: s, vf: interfaceChildrenFilter{}} +} + +func (s *interfaceS) All() Interface { + return &interfaceS{vs: s, vf: interfaceAllFilter{}} +} + +func (s *interfaceS) Filter(predicates ...p.Interface) Interface { + return &interfaceS{vs: s, vf: &interfaceFilterP{vp: p.InterfaceAnd(predicates...)}} +} diff --git a/pkg/framework/unstructpath/interfaces_test.go b/pkg/framework/path/selectors/interface_test.go similarity index 78% rename from pkg/framework/unstructpath/interfaces_test.go rename to pkg/framework/path/selectors/interface_test.go index e07d0d552..65240d110 100644 --- a/pkg/framework/unstructpath/interfaces_test.go +++ b/pkg/framework/path/selectors/interface_test.go @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unstructpath_test +package selectors_test import ( "reflect" "testing" - "k8s.io/kubectl/pkg/framework/unstructpath" + "k8s.io/kubectl/pkg/framework/path/selectors" ) func TestAll(t *testing.T) { @@ -30,7 +30,7 @@ func TestAll(t *testing.T) { "key4": map[string]interface{}{"key5": 5.}, } - numbers := unstructpath.All().Number().SelectFrom(u) + numbers := selectors.All().AsNumber().SelectFrom(u) expected := []float64{1., 2., 3., 4., 5.} if !reflect.DeepEqual(expected, numbers) { t.Fatalf("Expected to find all numbers (%v), got: %v", expected, numbers) @@ -44,7 +44,7 @@ func TestChildren(t *testing.T) { "key4": 5., } - numbers := unstructpath.Children().Number().SelectFrom(u) + numbers := selectors.Children().AsNumber().SelectFrom(u) expected := []float64{1., 5.} if !reflect.DeepEqual(expected, numbers) { t.Fatalf("Expected to find all numbers (%v), got: %v", expected, numbers) @@ -60,19 +60,19 @@ func TestFilter(t *testing.T) { "string", } expected := []interface{}{us[1]} - actual := unstructpath.Filter(unstructpath.Slice().At(3)).SelectFrom(us...) + actual := selectors.Filter(selectors.At(3)).SelectFrom(us...) if !reflect.DeepEqual(expected, actual) { t.Fatalf("Expected to filter (%v), got: %v", expected, actual) } } -func TestInterfaceSPredicate(t *testing.T) { - if !unstructpath.Slice().Match([]interface{}{}) { +func TestInterfacePredicate(t *testing.T) { + if !selectors.AsSlice().Match([]interface{}{}) { t.Fatal("SelectFroming a slice from a slice should match.") } } -func TestInterfaceSMap(t *testing.T) { +func TestInterfaceMap(t *testing.T) { root := map[string]interface{}{ "key1": "value", "key2": 1, @@ -92,13 +92,13 @@ func TestInterfaceSMap(t *testing.T) { root, root["key4"].(map[string]interface{}), } - actual := unstructpath.All().Map().SelectFrom(root) + actual := selectors.All().AsMap().SelectFrom(root) if !reflect.DeepEqual(expected, actual) { - t.Fatalf("Map should find maps %v, got %v", expected, actual) + t.Fatalf("AsMap should find maps %v, got %v", expected, actual) } } -func TestInterfaceSSlice(t *testing.T) { +func TestInterfaceSlice(t *testing.T) { root := map[string]interface{}{ "key1": "value", "key2": 1, @@ -118,13 +118,13 @@ func TestInterfaceSSlice(t *testing.T) { root["key3"].([]interface{}), root["key4"].(map[string]interface{})["subkey"].([]interface{}), } - actual := unstructpath.All().Slice().SelectFrom(root) + actual := selectors.All().AsSlice().SelectFrom(root) if !reflect.DeepEqual(expected, actual) { t.Fatalf("Slice should find slices %#v, got %#v", expected, actual) } } -func TestInterfaceSChildren(t *testing.T) { +func TestInterfaceChildren(t *testing.T) { root := map[string]interface{}{ "key1": "value", "key2": 1, @@ -144,24 +144,24 @@ func TestInterfaceSChildren(t *testing.T) { root["key3"].([]interface{})[0], root["key3"].([]interface{})[1], } - actual := unstructpath.Map().Field("key3").Children().SelectFrom(root) + actual := selectors.Field("key3").Children().SelectFrom(root) if !reflect.DeepEqual(expected, actual) { t.Fatalf("Expected %v, got %v", expected, actual) } } -func TestInterfaceSNumber(t *testing.T) { +func TestInterfaceNumber(t *testing.T) { u := []interface{}{1., 2., "three", 4., 5., []interface{}{}} - numbers := unstructpath.Children().Number().SelectFrom(u) + numbers := selectors.Children().AsNumber().SelectFrom(u) expected := []float64{1., 2., 4., 5.} if !reflect.DeepEqual(expected, numbers) { - t.Fatalf("Children().Number() should select %v, got %v", expected, numbers) + t.Fatalf("Children().AsNumber() should select %v, got %v", expected, numbers) } } -func TestInterfaceSString(t *testing.T) { +func TestInterfaceString(t *testing.T) { root := map[string]interface{}{ "key1": "value", "key2": 1, @@ -182,13 +182,13 @@ func TestInterfaceSString(t *testing.T) { "other value", "string", } - actual := unstructpath.All().String().SelectFrom(root) + actual := selectors.All().AsString().SelectFrom(root) if !reflect.DeepEqual(expected, actual) { t.Fatalf("Expected %v, got %v", expected, actual) } } -func TestInterfaceSAll(t *testing.T) { +func TestInterfaceAll(t *testing.T) { root := map[string]interface{}{ "key1": "value", "key2": 1, @@ -211,7 +211,7 @@ func TestInterfaceSAll(t *testing.T) { root["key4"].(map[string]interface{})["subkey"].([]interface{})[1], } - actual := unstructpath.Map().Field("key4").All().SelectFrom(root) + actual := selectors.Field("key4").All().SelectFrom(root) if !reflect.DeepEqual(expected, actual) { t.Fatalf("Expected %v, got %v", expected, actual) } diff --git a/pkg/framework/path/selectors/interfacef.go b/pkg/framework/path/selectors/interfacef.go new file mode 100644 index 000000000..738cd589b --- /dev/null +++ b/pkg/framework/path/selectors/interfacef.go @@ -0,0 +1,142 @@ +/* +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 selectors + +import ( + "sort" + + p "k8s.io/kubectl/pkg/framework/path/predicates" +) + +// A interfaceFilter allows us to chain Interface to Interface. None of this is +// public. It's implementing the "SelectFrom" part of a Interface. +type interfaceFilter interface { + SelectFrom(...interface{}) []interface{} +} + +// interfaceFilterP filters using a predicate. +type interfaceFilterP struct { + vp p.Interface +} + +func (f *interfaceFilterP) SelectFrom(interfaces ...interface{}) []interface{} { + vs := []interface{}{} + for _, value := range interfaces { + if f.vp.Match(value) { + vs = append(vs, value) + } + } + return vs +} + +type interfaceChildrenFilter struct{} + +func (interfaceChildrenFilter) SelectFrom(interfaces ...interface{}) []interface{} { + children := []interface{}{} + // We could process all slices and then all maps, but we want to + // keep things in the same order. + for _, value := range interfaces { + // Only one of the two should do something useful. + // AtP() with nothing selects all the children of a list + children = append(children, AtP().SelectFrom(value)...) + // FieldP() with nothing selects all the children of a map + children = append(children, FieldP().SelectFrom(value)...) + } + return children +} + +type interfaceAtPFilter struct { + ip p.Number +} + +func (f interfaceAtPFilter) SelectFrom(values ...interface{}) []interface{} { + interfaces := []interface{}{} + + for _, value := range values { + slice, ok := value.([]interface{}) + if !ok { + continue + } + for i := range slice { + if !f.ip.Match(float64(i)) { + continue + } + interfaces = append(interfaces, slice[i]) + } + } + return interfaces +} + +type interfaceLastFilter struct{} + +func (f interfaceLastFilter) SelectFrom(values ...interface{}) []interface{} { + interfaces := []interface{}{} + for _, value := range values { + slice, ok := value.([]interface{}) + if !ok { + continue + } + if len(slice) == 0 { + continue + } + interfaces = append(interfaces, slice[len(slice)-1]) + } + return interfaces +} + +type interfaceFieldPFilter struct { + sp p.String +} + +func (f interfaceFieldPFilter) SelectFrom(values ...interface{}) []interface{} { + interfaces := []interface{}{} + + for _, value := range values { + m, ok := value.(map[string]interface{}) + if !ok { + continue + } + + for _, field := range sortedKeys(m) { + if !f.sp.Match(field) { + continue + } + interfaces = append(interfaces, m[field]) + } + } + return interfaces +} + +func sortedKeys(m map[string]interface{}) []string { + keys := []string{} + for key := range m { + keys = append(keys, key) + } + sort.Strings(keys) + return keys +} + +type interfaceAllFilter struct{} + +func (interfaceAllFilter) SelectFrom(interfaces ...interface{}) []interface{} { + vs := []interface{}{} + for _, value := range interfaces { + vs = append(vs, value) + vs = append(vs, Children().All().SelectFrom(value)...) + } + return vs +} diff --git a/pkg/framework/unstructpath/maps.go b/pkg/framework/path/selectors/map.go similarity index 53% rename from pkg/framework/unstructpath/maps.go rename to pkg/framework/path/selectors/map.go index bce67e0aa..07541bc37 100644 --- a/pkg/framework/unstructpath/maps.go +++ b/pkg/framework/path/selectors/map.go @@ -14,17 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unstructpath +package selectors import ( - p "k8s.io/kubectl/pkg/framework/predicates" + p "k8s.io/kubectl/pkg/framework/path/predicates" ) -// MapS is a "map selector". It selects interfaces as maps (if +// Map is a "map selector". It selects interfaces as maps (if // possible) and filters those maps based on the "filtered" // predicates. -type MapS interface { - // MapS can be used as a Interface predicate. If the selector can't +type Map interface { + // Map can be used as a Interface predicate. If the selector can't // select any map from the interface, then the predicate is // false. p.Interface @@ -34,36 +34,19 @@ type MapS interface { // depending on the select criterias. SelectFrom(...interface{}) []map[string]interface{} - // Field returns the interface pointed by this specific field in the - // map. If the field doesn't exist, the value will be filtered - // out. - Field(string) InterfaceS - // FieldP returns all the interfaces pointed by field that match the - // string predicate. This selector can return more values than - // it gets (for one map, it can returns multiple sub-values, one - // for each field that matches the predicate). - FieldP(...p.String) InterfaceS - - // All returns a selector that selects all direct and indrect - // children of the given values. - Children() InterfaceS - // All returns a selector that selects all direct and indrect - // children of the given values. - All() InterfaceS - - // Filter will create a new MapS that filters only the values + // Filter will create a new Map that filters only the values // who match the predicate. - Filter(...p.Map) MapS + Filter(...p.Map) Map } // Map creates a selector that takes interfaces and filters them into maps // if possible. -func Map() MapS { +func AsMap() Map { return &mapS{} } type mapS struct { - vs InterfaceS + vs Interface mp p.Map } @@ -87,24 +70,7 @@ func (s *mapS) SelectFrom(interfaces ...interface{}) []map[string]interface{} { return maps } -func (s *mapS) Field(str string) InterfaceS { - return s.FieldP(p.StringEqual(str)) -} - -func (s *mapS) FieldP(predicates ...p.String) InterfaceS { - return filterMap(s, mapFieldPFilter{sp: p.StringAnd(predicates...)}) -} - -func (s *mapS) Children() InterfaceS { - // No predicate means select all. - return s.FieldP() -} - -func (s *mapS) All() InterfaceS { - return filterMap(s, mapAllFilter{}) -} - -func (s *mapS) Filter(predicates ...p.Map) MapS { +func (s *mapS) Filter(predicates ...p.Map) Map { return &mapS{vs: s.vs, mp: p.MapAnd(append(predicates, s.mp)...)} } diff --git a/pkg/framework/unstructpath/numbers.go b/pkg/framework/path/selectors/number.go similarity index 76% rename from pkg/framework/unstructpath/numbers.go rename to pkg/framework/path/selectors/number.go index 567c3e608..83b85da3b 100644 --- a/pkg/framework/unstructpath/numbers.go +++ b/pkg/framework/path/selectors/number.go @@ -14,17 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unstructpath +package selectors import ( - p "k8s.io/kubectl/pkg/framework/predicates" + p "k8s.io/kubectl/pkg/framework/path/predicates" ) -// NumberS is a "number selector". It selects values as numbers (if +// Number is a "number selector". It selects values as numbers (if // possible) and filters those numbers based on the "filtered" // predicates. -type NumberS interface { - // NumberS can be used as a Interface predicate. If the selector can't +type Number interface { + // Number can be used as a Interface predicate. If the selector can't // select any number from the value, then the predicate is // false. p.Interface @@ -34,18 +34,18 @@ type NumberS interface { // depending on the select criterias. SelectFrom(...interface{}) []float64 - // Filter will create a new NumberS that filters only the values + // Filter will create a new Number that filters only the values // who match the predicate. - Filter(...p.Number) NumberS + Filter(...p.Number) Number } -// Number returns a NumberS that selects numbers from given values. -func Number() NumberS { +// AsNumber returns a Number that selects numbers from given values. +func AsNumber() Number { return &numberS{} } type numberS struct { - vs InterfaceS + vs Interface ip p.Number } @@ -67,7 +67,7 @@ func (s *numberS) SelectFrom(values ...interface{}) []float64 { return numbers } -func (s *numberS) Filter(predicates ...p.Number) NumberS { +func (s *numberS) Filter(predicates ...p.Number) Number { if s.ip != nil { predicates = append(predicates, s.ip) } diff --git a/pkg/framework/unstructpath/numbers_test.go b/pkg/framework/path/selectors/number_test.go similarity index 65% rename from pkg/framework/unstructpath/numbers_test.go rename to pkg/framework/path/selectors/number_test.go index c49b00ee3..443248730 100644 --- a/pkg/framework/unstructpath/numbers_test.go +++ b/pkg/framework/path/selectors/number_test.go @@ -14,18 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unstructpath_test +package selectors_test import ( "reflect" "testing" - p "k8s.io/kubectl/pkg/framework/predicates" - . "k8s.io/kubectl/pkg/framework/unstructpath" + p "k8s.io/kubectl/pkg/framework/path/predicates" + . "k8s.io/kubectl/pkg/framework/path/selectors" ) -func TestNumberSSelectFrom(t *testing.T) { - s := Number().SelectFrom( +func TestNumberSelectFrom(t *testing.T) { + s := AsNumber().SelectFrom( 1., "string", 2., @@ -36,8 +36,8 @@ func TestNumberSSelectFrom(t *testing.T) { } } -func TestNumberSFilter(t *testing.T) { - s := Number(). +func TestNumberFilter(t *testing.T) { + s := AsNumber(). Filter(p.NumberGreaterThan(2), p.NumberEqualOrLessThan(4)). SelectFrom( 1., @@ -51,20 +51,20 @@ func TestNumberSFilter(t *testing.T) { } } -func TestNumberSPredicate(t *testing.T) { - if !Number().Filter(p.NumberGreaterThan(10)).Match(12.) { +func TestNumberPredicate(t *testing.T) { + if !AsNumber().Filter(p.NumberGreaterThan(10)).Match(12.) { t.Fatal("SelectFromor matching element should match") } - if Number().Filter(p.NumberGreaterThan(10)).Match(4.) { + if AsNumber().Filter(p.NumberGreaterThan(10)).Match(4.) { t.Fatal("SelectFromor not matching element should not match") } } -func TestNumberSFromInterfaceS(t *testing.T) { - if !Children().Number().Filter(p.NumberGreaterThan(10)).Match([]interface{}{1., 2., 5., 12.}) { +func TestNumberFromInterface(t *testing.T) { + if !Children().AsNumber().Filter(p.NumberGreaterThan(10)).Match([]interface{}{1., 2., 5., 12.}) { t.Fatal("SelectFromor should find element that match") } - if Children().Number().Filter(p.NumberGreaterThan(10)).Match([]interface{}{1., 2., 5.}) { + if Children().AsNumber().Filter(p.NumberGreaterThan(10)).Match([]interface{}{1., 2., 5.}) { t.Fatal("SelectFromor shouldn't find element that match") } } diff --git a/pkg/framework/unstructpath/slices.go b/pkg/framework/path/selectors/slice.go similarity index 50% rename from pkg/framework/unstructpath/slices.go rename to pkg/framework/path/selectors/slice.go index dda6d96dd..07e42829b 100644 --- a/pkg/framework/unstructpath/slices.go +++ b/pkg/framework/path/selectors/slice.go @@ -14,17 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unstructpath +package selectors import ( - p "k8s.io/kubectl/pkg/framework/predicates" + p "k8s.io/kubectl/pkg/framework/path/predicates" ) -// SliceS is a "slice selector". It selects values as slices (if +// Slice is a "slice selector". It selects values as slices (if // possible) and filters those slices based on the "filtered" // predicates. -type SliceS interface { - // SliceS can be used as a Interface predicate. If the selector +type Slice interface { + // Slice can be used as a Interface predicate. If the selector // can't select any slice from the value, then the predicate is // false. p.Interface @@ -34,38 +34,19 @@ type SliceS interface { // depending on the select criterias. SelectFrom(...interface{}) [][]interface{} - // At returns a selector that select the child at the given - // index, if the list has such an index. Otherwise, nothing is - // returned. - At(index int) InterfaceS - // AtP returns a selector that selects all the item whose index - // matches the number predicate. More predicates can be given, - // they are "and"-ed by this method. - AtP(ips ...p.Number) InterfaceS - // Last returns a selector that selects the last value of the - // list. If the list is empty, then nothing will be selected. - Last() InterfaceS - - // All returns a selector that selects all direct and indrect - // children of the given values. - Children() InterfaceS - // All returns a selector that selects all direct and indrect - // children of the given values. - All() InterfaceS - - // Filter will create a new SliceS that filters only the values + // Filter will create a new Slice that filters only the values // who match the predicate. - Filter(...p.Slice) SliceS + Filter(...p.Slice) Slice } // Slice creates a selector that takes values and filters them into // slices if possible. -func Slice() SliceS { +func AsSlice() Slice { return &sliceS{} } type sliceS struct { - vs InterfaceS + vs Interface sp p.Slice } @@ -89,28 +70,7 @@ func (s *sliceS) SelectFrom(interfaces ...interface{}) [][]interface{} { return slices } -func (s *sliceS) At(index int) InterfaceS { - return s.AtP(p.NumberEqual(float64(index))) -} - -func (s *sliceS) AtP(predicates ...p.Number) InterfaceS { - return filterSlice(s, sliceAtPFilter{ip: p.NumberAnd(predicates...)}) -} - -func (s *sliceS) Last() InterfaceS { - return filterSlice(s, sliceLastFilter{}) -} - -func (s *sliceS) Children() InterfaceS { - // No predicates means select all direct children. - return s.AtP() -} - -func (s *sliceS) All() InterfaceS { - return filterSlice(s, sliceAllFilter{}) -} - -func (s *sliceS) Filter(sps ...p.Slice) SliceS { +func (s *sliceS) Filter(sps ...p.Slice) Slice { return &sliceS{vs: s.vs, sp: p.SliceAnd(append(sps, s.sp)...)} } diff --git a/pkg/framework/unstructpath/strings.go b/pkg/framework/path/selectors/string.go similarity index 76% rename from pkg/framework/unstructpath/strings.go rename to pkg/framework/path/selectors/string.go index 7a727c04e..6219f988a 100644 --- a/pkg/framework/unstructpath/strings.go +++ b/pkg/framework/path/selectors/string.go @@ -14,17 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unstructpath +package selectors import ( - p "k8s.io/kubectl/pkg/framework/predicates" + p "k8s.io/kubectl/pkg/framework/path/predicates" ) -// StringS is a "string selector". It selects values as strings (if +// String is a "string selector". It selects values as strings (if // possible) and filters those strings based on the "filtered" // predicates. -type StringS interface { - // StringS can be used as a Interface predicate. If the selector can't +type String interface { + // String can be used as a Interface predicate. If the selector can't // select any string from the value, then the predicate is // false. p.Interface @@ -34,18 +34,18 @@ type StringS interface { // depending on the select criterias. SelectFrom(...interface{}) []string - // Filter will create a new StringS that filters only the values + // Filter will create a new String that filters only the values // who match the predicate. - Filter(...p.String) StringS + Filter(...p.String) String } type stringS struct { - vs InterfaceS + vs Interface sp p.String } -// String returns a StringS that selects strings from values. -func String() StringS { +// AsString returns a String that selects strings from values. +func AsString() String { return &stringS{} } @@ -67,7 +67,7 @@ func (s *stringS) SelectFrom(values ...interface{}) []string { return strings } -func (s *stringS) Filter(predicates ...p.String) StringS { +func (s *stringS) Filter(predicates ...p.String) String { if s.sp != nil { predicates = append(predicates, s.sp) } diff --git a/pkg/framework/unstructpath/strings_test.go b/pkg/framework/path/selectors/string_test.go similarity index 64% rename from pkg/framework/unstructpath/strings_test.go rename to pkg/framework/path/selectors/string_test.go index 2572b6c70..c39f7377e 100644 --- a/pkg/framework/unstructpath/strings_test.go +++ b/pkg/framework/path/selectors/string_test.go @@ -14,18 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -package unstructpath_test +package selectors_test import ( "reflect" "testing" - p "k8s.io/kubectl/pkg/framework/predicates" - . "k8s.io/kubectl/pkg/framework/unstructpath" + p "k8s.io/kubectl/pkg/framework/path/predicates" + . "k8s.io/kubectl/pkg/framework/path/selectors" ) -func TestStringSSelectFrom(t *testing.T) { - s := String().SelectFrom( +func TestStringSelectFrom(t *testing.T) { + s := AsString().SelectFrom( "my string", 1, "your string", @@ -36,8 +36,8 @@ func TestStringSSelectFrom(t *testing.T) { } } -func TestStringSFilter(t *testing.T) { - s := String(). +func TestStringFilter(t *testing.T) { + s := AsString(). Filter(p.StringLength(p.NumberEqual(4))). SelectFrom( "one", @@ -51,20 +51,20 @@ func TestStringSFilter(t *testing.T) { } } -func TestStringSPredicate(t *testing.T) { - if !String().Filter(p.StringLength(p.NumberEqual(4))).Match("four") { +func TestStringPredicate(t *testing.T) { + if !AsString().Filter(p.StringLength(p.NumberEqual(4))).Match("four") { t.Fatal("SelectFromor matching element should match") } - if String().Filter(p.StringLength(p.NumberEqual(10))).Match("four") { + if AsString().Filter(p.StringLength(p.NumberEqual(10))).Match("four") { t.Fatal("SelectFromor not matching element should not match") } } -func TestStringSFromInterfaceS(t *testing.T) { - if !Children().String().Filter(p.StringLength(p.NumberEqual(4))).Match([]interface{}{"four", "five"}) { +func TestStringFromInterface(t *testing.T) { + if !Children().AsString().Filter(p.StringLength(p.NumberEqual(4))).Match([]interface{}{"four", "five"}) { t.Fatal("SelectFromor should find element that match") } - if Children().String().Filter(p.StringLength(p.NumberEqual(4))).Match([]interface{}{"one", "two", "three"}) { + if Children().AsString().Filter(p.StringLength(p.NumberEqual(4))).Match([]interface{}{"one", "two", "three"}) { t.Fatal("SelectFromor shouldn't find element that match") } } diff --git a/pkg/framework/unstructpath/interfacef.go b/pkg/framework/unstructpath/interfacef.go deleted file mode 100644 index d6b4cf4a5..000000000 --- a/pkg/framework/unstructpath/interfacef.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -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 unstructpath - -import ( - p "k8s.io/kubectl/pkg/framework/predicates" -) - -// A interfaceFilter allows us to chain InterfaceS to InterfaceS. None of this is -// public. It's implementing the "SelectFrom" part of a InterfaceS. -type interfaceFilter interface { - SelectFrom(...interface{}) []interface{} -} - -// interfaceFilterP filters using a predicate. -type interfaceFilterP struct { - vp p.Interface -} - -func (f *interfaceFilterP) SelectFrom(interfaces ...interface{}) []interface{} { - vs := []interface{}{} - for _, value := range interfaces { - if f.vp.Match(value) { - vs = append(vs, value) - } - } - return vs -} - -type interfaceChildrenFilter struct{} - -func (interfaceChildrenFilter) SelectFrom(interfaces ...interface{}) []interface{} { - children := []interface{}{} - // We could process all slices and then all maps, but we want to - // keep things in the same order. - for _, value := range interfaces { - // Only one of the two should do something useful. - children = append(children, Slice().Children().SelectFrom(value)...) - children = append(children, Map().Children().SelectFrom(value)...) - } - return children -} - -// interfaceSliceFilter is a Interface-to-Slice combined with a Slice-to-Interface -// to form a Interface-to-Interface. -type interfaceSliceFilter struct { - ss SliceS - sf sliceFilter -} - -func (s *interfaceSliceFilter) SelectFrom(interfaces ...interface{}) []interface{} { - return s.sf.SelectFrom(s.ss.SelectFrom(interfaces...)...) -} - -// interfaceMapFilter is a Interface-to-Map combined with a Map-to-Interface to form -// a Interface-to-Interface. -type interfaceMapFilter struct { - ms MapS - mf mapFilter -} - -func (s *interfaceMapFilter) SelectFrom(interfaces ...interface{}) []interface{} { - return s.mf.SelectFrom(s.ms.SelectFrom(interfaces...)...) -} - -type interfaceAllFilter struct{} - -func (interfaceAllFilter) SelectFrom(interfaces ...interface{}) []interface{} { - vs := []interface{}{} - for _, value := range interfaces { - vs = append(vs, value) - // Only one of the follow two statements should return something ... - vs = append(vs, Slice().All().SelectFrom(value)...) - vs = append(vs, Map().All().SelectFrom(value)...) - } - return vs -} diff --git a/pkg/framework/unstructpath/interfaces.go b/pkg/framework/unstructpath/interfaces.go deleted file mode 100644 index 8a9655f4f..000000000 --- a/pkg/framework/unstructpath/interfaces.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -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 unstructpath - -import ( - p "k8s.io/kubectl/pkg/framework/predicates" -) - -// InterfaceS is a "interface selector". It filters interfaces based on the -// "filtered" predicates. -type InterfaceS interface { - // InterfaceS can be used as a Interface predicate. If the selector can't - // select any interface from the interface, then the predicate is - // false. - p.Interface - - // SelectFrom finds interfaces from interfaces using this selector. The - // list can be bigger or smaller than the initial lists, - // depending on the select criterias. - SelectFrom(...interface{}) []interface{} - - // Map returns a selector that selects Maps from the given - // values. - Map() MapS - // Slice returns a selector that selects Slices from the given - // values. - Slice() SliceS - // Number returns a selector taht selects Numbers from the given values. - Number() NumberS - // String returns a selector that selects strings from the given values. - String() StringS - - // Children returns a selector that selects the direct children - // of the given values. - Children() InterfaceS - // All returns a selector that selects all direct and indrect - // children of the given values. - All() InterfaceS - - // Filter will create a new StringS that filters only the values - // who match the predicate. - Filter(...p.Interface) InterfaceS -} - -// Children selects all the children of the values. -func Children() InterfaceS { - return &interfaceS{vf: interfaceChildrenFilter{}} -} - -// All selects all the direct and indirect childrens of the values. -func All() InterfaceS { - return &interfaceS{vf: interfaceAllFilter{}} -} - -// Filter will only return the values that match the predicate. -func Filter(predicates ...p.Interface) InterfaceS { - return &interfaceS{vf: &interfaceFilterP{vp: p.InterfaceAnd(predicates...)}} -} - -// InterfaceS is a "Interface SelectFromor". It selects a list of values, maps, -// slices, strings, integer from a list of values. -type interfaceS struct { - vs InterfaceS - vf interfaceFilter -} - -// Match returns true if the selector can find items in the given -// value. Otherwise, it returns false. -func (s *interfaceS) Match(value interface{}) bool { - return len(s.SelectFrom(value)) != 0 -} - -func (s *interfaceS) SelectFrom(interfaces ...interface{}) []interface{} { - if s.vs != nil { - interfaces = s.vs.SelectFrom(interfaces...) - } - return s.vf.SelectFrom(interfaces...) -} - -func (s *interfaceS) Map() MapS { - return &mapS{vs: s} -} - -func (s *interfaceS) Slice() SliceS { - return &sliceS{vs: s} -} - -func (s *interfaceS) Number() NumberS { - return &numberS{vs: s} -} - -func (s *interfaceS) String() StringS { - return &stringS{vs: s} -} - -func (s *interfaceS) Children() InterfaceS { - return &interfaceS{vs: s, vf: interfaceChildrenFilter{}} -} - -func (s *interfaceS) All() InterfaceS { - return &interfaceS{vs: s, vf: interfaceAllFilter{}} -} - -func (s *interfaceS) Filter(predicates ...p.Interface) InterfaceS { - return &interfaceS{vs: s, vf: &interfaceFilterP{vp: p.InterfaceAnd(predicates...)}} -} diff --git a/pkg/framework/unstructpath/mapf.go b/pkg/framework/unstructpath/mapf.go deleted file mode 100644 index dd464f1bb..000000000 --- a/pkg/framework/unstructpath/mapf.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -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 unstructpath - -import ( - "sort" - - p "k8s.io/kubectl/pkg/framework/predicates" -) - -// This is a Map-to-Interface filter. -type mapFilter interface { - SelectFrom(...map[string]interface{}) []interface{} -} - -func filterMap(ms MapS, mf mapFilter) InterfaceS { - return &interfaceS{ - vf: &interfaceMapFilter{ - ms: ms, - mf: mf, - }, - } -} - -type mapFieldPFilter struct { - sp p.String -} - -func (f mapFieldPFilter) SelectFrom(maps ...map[string]interface{}) []interface{} { - interfaces := []interface{}{} - - for _, m := range maps { - for _, field := range sortedKeys(m) { - if !f.sp.Match(field) { - continue - } - interfaces = append(interfaces, m[field]) - } - } - return interfaces -} - -type mapAllFilter struct{} - -func (mapAllFilter) SelectFrom(maps ...map[string]interface{}) []interface{} { - interfaces := []interface{}{} - for _, m := range maps { - for _, field := range sortedKeys(m) { - interfaces = append(interfaces, All().SelectFrom(m[field])...) - } - } - return interfaces -} - -func sortedKeys(m map[string]interface{}) []string { - keys := []string{} - for key := range m { - keys = append(keys, key) - } - sort.Strings(keys) - return keys -} diff --git a/pkg/framework/unstructpath/slicef.go b/pkg/framework/unstructpath/slicef.go deleted file mode 100644 index ce4baea22..000000000 --- a/pkg/framework/unstructpath/slicef.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -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 unstructpath - -import ( - p "k8s.io/kubectl/pkg/framework/predicates" -) - -func filterSlice(ss SliceS, sf sliceFilter) InterfaceS { - return &interfaceS{ - vf: &interfaceSliceFilter{ - ss: ss, - sf: sf, - }, - } -} - -// This is a Slice-to-Interface filter. -type sliceFilter interface { - SelectFrom(...[]interface{}) []interface{} -} - -type sliceAtPFilter struct { - ip p.Number -} - -func (f sliceAtPFilter) SelectFrom(slices ...[]interface{}) []interface{} { - interfaces := []interface{}{} - - for _, slice := range slices { - for i := range slice { - if !f.ip.Match(float64(i)) { - continue - } - interfaces = append(interfaces, slice[i]) - } - } - return interfaces -} - -type sliceLastFilter struct{} - -func (f sliceLastFilter) SelectFrom(slices ...[]interface{}) []interface{} { - interfaces := []interface{}{} - for _, slice := range slices { - if len(slice) == 0 { - continue - } - interfaces = append(interfaces, slice[len(slice)-1]) - } - return interfaces -} - -type sliceAllFilter struct{} - -func (sliceAllFilter) SelectFrom(slices ...[]interface{}) []interface{} { - interfaces := []interface{}{} - for _, slice := range slices { - for _, v := range slice { - interfaces = append(interfaces, All().SelectFrom(v)...) - } - } - return interfaces -}