Merge pull request #354 from apelisse/unstructpath
unstructpath: Create json path type package.
This commit is contained in:
commit
fffaddd605
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This package helps you find specific fields in your unstruct
|
||||||
|
// object. It is similar to what you can do with jsonpath, but reads the
|
||||||
|
// path from strongly typed object, not strings.
|
||||||
|
package unstructpath
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This example is inspired from http://goessner.net/articles/JsonPath/#e3.
|
||||||
|
func Example() {
|
||||||
|
data := `{ "store": {
|
||||||
|
"book": [
|
||||||
|
{ "category": "reference",
|
||||||
|
"author": "Nigel Rees",
|
||||||
|
"title": "Sayings of the Century",
|
||||||
|
"price": 8.95
|
||||||
|
},
|
||||||
|
{ "category": "fiction",
|
||||||
|
"author": "Evelyn Waugh",
|
||||||
|
"title": "Sword of Honour",
|
||||||
|
"price": 12.99
|
||||||
|
},
|
||||||
|
{ "category": "fiction",
|
||||||
|
"author": "Herman Melville",
|
||||||
|
"title": "Moby Dick",
|
||||||
|
"isbn": "0-553-21311-3",
|
||||||
|
"price": 8.99
|
||||||
|
},
|
||||||
|
{ "category": "fiction",
|
||||||
|
"author": "J. R. R. Tolkien",
|
||||||
|
"title": "The Lord of the Rings",
|
||||||
|
"isbn": "0-395-19395-8",
|
||||||
|
"price": 22.99
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bicycle": {
|
||||||
|
"color": "red",
|
||||||
|
"price": 19.95
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
u := map[string]interface{}{}
|
||||||
|
if err := yaml.Unmarshal([]byte(data), &u); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The authors of all books in the store. Returns a list of strings.
|
||||||
|
Children().Map().Field("book").Children().Map().Field("author").String().SelectFrom(u)
|
||||||
|
|
||||||
|
// All authors. Returns a list of strings.
|
||||||
|
All().Map().Field("author").String().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)
|
||||||
|
|
||||||
|
// The price of everything in the store. Returns a list of "float64".
|
||||||
|
Map().Field("store").All().Map().Field("price").Number().SelectFrom(u)
|
||||||
|
|
||||||
|
// The third book. Returns a list of 1 interface{}.
|
||||||
|
All().Map().Field("book").Slice().At(2).SelectFrom(u)
|
||||||
|
|
||||||
|
// The last book in order. Return a list of 1 interface{}.
|
||||||
|
All().Map().Field("book").Slice().Last().SelectFrom(u)
|
||||||
|
|
||||||
|
// The first two books. Returns a list of 2 interface{}.
|
||||||
|
All().Map().Field("book").Slice().AtP(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)
|
||||||
|
|
||||||
|
// Filter all books cheaper than 10. Returns a list of interface{}.
|
||||||
|
All().Map().Field("book").Children().Filter(Map().Field("price").Number().Filter(NumberLessThan(10))).SelectFrom(u)
|
||||||
|
|
||||||
|
// All elements in structure. Returns a list of interface{}.
|
||||||
|
All().SelectFrom(u)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
// This is a Map-to-Value filter.
|
||||||
|
type mapFilter interface {
|
||||||
|
SelectFrom(...map[string]interface{}) []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterMap(ms MapS, mf mapFilter) ValueS {
|
||||||
|
return &valueS{
|
||||||
|
vf: &valueMapFilter{
|
||||||
|
ms: ms,
|
||||||
|
mf: mf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapFieldPFilter struct {
|
||||||
|
sp StringP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f mapFieldPFilter) SelectFrom(maps ...map[string]interface{}) []interface{} {
|
||||||
|
values := []interface{}{}
|
||||||
|
|
||||||
|
for _, m := range maps {
|
||||||
|
for _, field := range sortedKeys(m) {
|
||||||
|
if !f.sp.Match(field) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
values = append(values, m[field])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapAllFilter struct{}
|
||||||
|
|
||||||
|
func (mapAllFilter) SelectFrom(maps ...map[string]interface{}) []interface{} {
|
||||||
|
values := []interface{}{}
|
||||||
|
for _, m := range maps {
|
||||||
|
for _, field := range sortedKeys(m) {
|
||||||
|
values = append(values, All().SelectFrom(m[field])...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortedKeys(m map[string]interface{}) []string {
|
||||||
|
keys := []string{}
|
||||||
|
for key := range m {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
// MapP is a "map predicate". It's a type that decides if a
|
||||||
|
// map matches or not.
|
||||||
|
type MapP interface {
|
||||||
|
Match(map[string]interface{}) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapNot inverses the value of the predicate.
|
||||||
|
func MapNot(predicate MapP) MapP {
|
||||||
|
return mapNot{mp: predicate}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapNot struct {
|
||||||
|
mp MapP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p mapNot) Match(m map[string]interface{}) bool {
|
||||||
|
return !p.mp.Match(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapAnd returns true if all the sub-predicates are true. If there are
|
||||||
|
// no sub-predicates, always returns true.
|
||||||
|
func MapAnd(predicates ...MapP) MapP {
|
||||||
|
return mapAnd{mps: predicates}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapAnd struct {
|
||||||
|
mps []MapP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p mapAnd) Match(m map[string]interface{}) bool {
|
||||||
|
for _, mp := range p.mps {
|
||||||
|
if !mp.Match(m) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapOr returns true if any sub-predicate is true. If there are no
|
||||||
|
// sub-predicates, always returns false.
|
||||||
|
func MapOr(predicates ...MapP) MapP {
|
||||||
|
mps := []MapP{}
|
||||||
|
|
||||||
|
// Implements "De Morgan's law"
|
||||||
|
for _, mp := range predicates {
|
||||||
|
mps = append(mps, MapNot(mp))
|
||||||
|
}
|
||||||
|
return MapNot(MapAnd(mps...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapNumFields matches if the number of fields matches the number
|
||||||
|
// predicate.
|
||||||
|
func MapNumFields(predicate NumberP) MapP {
|
||||||
|
return mapNumFields{ip: predicate}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapNumFields struct {
|
||||||
|
ip NumberP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p mapNumFields) Match(m map[string]interface{}) bool {
|
||||||
|
return p.ip.Match(float64(len(m)))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
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_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "k8s.io/kubectl/pkg/framework/unstructpath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MapTrue struct{}
|
||||||
|
|
||||||
|
func (MapTrue) Match(m map[string]interface{}) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapNot(t *testing.T) {
|
||||||
|
if MapNot(MapTrue{}).Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapNot(MapTrue{}) should never match")
|
||||||
|
}
|
||||||
|
if !MapNot(MapNot(MapTrue{})).Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapNot(MapNot(MapTrue{})) should always match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapAnd(t *testing.T) {
|
||||||
|
if !MapAnd().Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapAnd() should always match")
|
||||||
|
}
|
||||||
|
if MapAnd(MapNot(MapTrue{})).Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapAnd(MapNot(MapTrue{})) should never match")
|
||||||
|
}
|
||||||
|
if !MapAnd(MapTrue{}).Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapAnd(MapTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if !MapAnd(MapTrue{}, MapTrue{}).Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapAnd(MapTrue{}, MapTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if MapAnd(MapTrue{}, MapNot(MapTrue{}), MapTrue{}).Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapAnd(MapTrue{}, MapNot(MapTrue{}), MapTrue{}) should never match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapOr(t *testing.T) {
|
||||||
|
if MapOr().Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapOr() should never match")
|
||||||
|
}
|
||||||
|
if MapOr(MapNot(MapTrue{})).Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapOr(MapNot(MapTrue{})) should never match")
|
||||||
|
}
|
||||||
|
if !MapOr(MapTrue{}).Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapOr(MapTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if !MapOr(MapTrue{}, MapTrue{}).Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapOr(MapTrue{}, MapTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if !MapOr(MapTrue{}, MapNot(MapTrue{}), MapTrue{}).Match(map[string]interface{}{}) {
|
||||||
|
t.Fatal("MapOr(MapTrue{}, MapNot(MapTrue{}), MapTrue{}) should always match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapNumFields(t *testing.T) {
|
||||||
|
m := map[string]interface{}{"First": 1, "Second": 2, "Third": 3}
|
||||||
|
if !MapNumFields(NumberEqual(3)).Match(m) {
|
||||||
|
t.Fatal(`MapNumFields(NumberEqual(3)) should match []interface{}{1, 2, 3}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if MapNumFields(NumberLessThan(2)).Match(m) {
|
||||||
|
t.Fatal(`MapNumFields(NumberLessThan(2)) should not match []interface{}{1, 2, 3}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !MapNumFields(NumberLessThan(5)).Match(m) {
|
||||||
|
t.Fatal(`MapNumFields(NumberLessThan(5)) should match []interface{}{1, 2, 3}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
// MapS is a "map selector". It selects values as maps (if
|
||||||
|
// possible) and filters those maps based on the "filtered"
|
||||||
|
// predicates.
|
||||||
|
type MapS interface {
|
||||||
|
// MapS can be used as a Value predicate. If the selector can't
|
||||||
|
// select any map from the value, then the predicate is
|
||||||
|
// false.
|
||||||
|
ValueP
|
||||||
|
|
||||||
|
// SelectFrom finds maps from values using this selector. The
|
||||||
|
// list can be bigger or smaller than the initial lists,
|
||||||
|
// depending on the select criterias.
|
||||||
|
SelectFrom(...interface{}) []map[string]interface{}
|
||||||
|
|
||||||
|
// Field returns the value pointed by this specific field in the
|
||||||
|
// map. If the field doesn't exist, the value will be filtered
|
||||||
|
// out.
|
||||||
|
Field(string) ValueS
|
||||||
|
// FieldP returns all the values 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(...StringP) ValueS
|
||||||
|
|
||||||
|
// All returns a selector that selects all direct and indrect
|
||||||
|
// children of the given values.
|
||||||
|
Children() ValueS
|
||||||
|
// All returns a selector that selects all direct and indrect
|
||||||
|
// children of the given values.
|
||||||
|
All() ValueS
|
||||||
|
|
||||||
|
// Filter will create a new MapS that filters only the values
|
||||||
|
// who match the predicate.
|
||||||
|
Filter(...MapP) MapS
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map creates a selector that takes values and filters them into maps
|
||||||
|
// if possible.
|
||||||
|
func Map() MapS {
|
||||||
|
return &mapS{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapS struct {
|
||||||
|
vs ValueS
|
||||||
|
mp MapP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *mapS) SelectFrom(values ...interface{}) []map[string]interface{} {
|
||||||
|
if s.vs != nil {
|
||||||
|
values = s.vs.SelectFrom(values...)
|
||||||
|
}
|
||||||
|
|
||||||
|
maps := []map[string]interface{}{}
|
||||||
|
for _, value := range values {
|
||||||
|
m, ok := value.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s.mp != nil && !s.mp.Match(m) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
maps = append(maps, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
return maps
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *mapS) Field(str string) ValueS {
|
||||||
|
return s.FieldP(StringEqual(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *mapS) FieldP(predicates ...StringP) ValueS {
|
||||||
|
return filterMap(s, mapFieldPFilter{sp: StringAnd(predicates...)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *mapS) Children() ValueS {
|
||||||
|
// No predicate means select all.
|
||||||
|
return s.FieldP()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *mapS) All() ValueS {
|
||||||
|
return filterMap(s, mapAllFilter{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *mapS) Filter(predicates ...MapP) MapS {
|
||||||
|
return &mapS{vs: s.vs, mp: MapAnd(append(predicates, s.mp)...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *mapS) Match(value interface{}) bool {
|
||||||
|
return len(s.SelectFrom(value)) != 0
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
// NumberP is a "number predicate". It's a type that decides if a
|
||||||
|
// number matches or not.
|
||||||
|
type NumberP interface {
|
||||||
|
Match(float64) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumberNot inverts the result of the sub-predicate.
|
||||||
|
func NumberNot(predicate NumberP) NumberP {
|
||||||
|
return numberNot{ip: predicate}
|
||||||
|
}
|
||||||
|
|
||||||
|
type numberNot struct {
|
||||||
|
ip NumberP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p numberNot) Match(i float64) bool {
|
||||||
|
return !p.ip.Match(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumberAnd returns true if all the sub-predicates are true. If there are
|
||||||
|
// no sub-predicates, always returns true.
|
||||||
|
func NumberAnd(predicates ...NumberP) NumberP {
|
||||||
|
return numberAnd{ips: predicates}
|
||||||
|
}
|
||||||
|
|
||||||
|
type numberAnd struct {
|
||||||
|
ips []NumberP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p numberAnd) Match(i float64) bool {
|
||||||
|
for _, ip := range p.ips {
|
||||||
|
if !ip.Match(i) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumberOr returns true if any sub-predicate is true. If there are no
|
||||||
|
// sub-predicates, always returns false.
|
||||||
|
func NumberOr(predicates ...NumberP) NumberP {
|
||||||
|
ips := []NumberP{}
|
||||||
|
|
||||||
|
// Implements "De Morgan's law"
|
||||||
|
for _, ip := range predicates {
|
||||||
|
ips = append(ips, NumberNot(ip))
|
||||||
|
}
|
||||||
|
return NumberNot(NumberAnd(ips...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumberEqual returns true if the value is exactly i.
|
||||||
|
func NumberEqual(i float64) NumberP {
|
||||||
|
return numberEqual{i: i}
|
||||||
|
}
|
||||||
|
|
||||||
|
type numberEqual struct {
|
||||||
|
i float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p numberEqual) Match(i float64) bool {
|
||||||
|
return i == p.i
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumberGreaterThan returns true if the value is strictly greater than i.
|
||||||
|
func NumberGreaterThan(i float64) NumberP {
|
||||||
|
return numberGreaterThan{i: i}
|
||||||
|
}
|
||||||
|
|
||||||
|
type numberGreaterThan struct {
|
||||||
|
i float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p numberGreaterThan) Match(i float64) bool {
|
||||||
|
return i > p.i
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumberEqualOrGreaterThan returns true if the value is equal or greater
|
||||||
|
// than i.
|
||||||
|
func NumberEqualOrGreaterThan(i float64) NumberP {
|
||||||
|
return NumberOr(NumberEqual(i), NumberGreaterThan(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumberLessThan returns true if the value is strictly less than i.
|
||||||
|
func NumberLessThan(i float64) NumberP {
|
||||||
|
// It's not equal, and it's not greater than i.
|
||||||
|
return NumberAnd(NumberNot(NumberEqual(i)), NumberNot(NumberGreaterThan(i)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumberEqualOrLessThan returns true if the value is equal or less than i.
|
||||||
|
func NumberEqualOrLessThan(i float64) NumberP {
|
||||||
|
return NumberOr(NumberEqual(i), NumberLessThan(i))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
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_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "k8s.io/kubectl/pkg/framework/unstructpath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This example shows you how you can create a IntP, and how it's use to
|
||||||
|
// compare with the actual value.
|
||||||
|
//
|
||||||
|
// XXX: This could definitely be improved to add a better example.
|
||||||
|
func ExampleNumberP() {
|
||||||
|
fmt.Println(NumberEqual(5).Match(5))
|
||||||
|
// Output: true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberEqual(t *testing.T) {
|
||||||
|
if !NumberEqual(5).Match(5) {
|
||||||
|
t.Fatal("NumberEqual(5) should match 5")
|
||||||
|
}
|
||||||
|
if NumberEqual(5).Match(4) {
|
||||||
|
t.Fatal("NumberEqual(5) should not match 4")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberNot(t *testing.T) {
|
||||||
|
if NumberNot(NumberEqual(5)).Match(5) {
|
||||||
|
t.Fatal("NumberNot(NumberEqual(5)) should not match 5")
|
||||||
|
}
|
||||||
|
if !NumberNot(NumberEqual(5)).Match(4) {
|
||||||
|
t.Fatal("NumberNot(NumberEqual(5)) should match 4")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberAnd(t *testing.T) {
|
||||||
|
if !NumberAnd().Match(5) {
|
||||||
|
t.Fatal("NumberAnd() should match 5")
|
||||||
|
}
|
||||||
|
if !NumberAnd(NumberEqual(5)).Match(5) {
|
||||||
|
t.Fatal("NumberAnd(NumberEqual(5)) should match 5")
|
||||||
|
}
|
||||||
|
if NumberAnd(NumberEqual(5)).Match(4) {
|
||||||
|
t.Fatal("NumberAnd(NumberEqual(5)) should not match 4")
|
||||||
|
}
|
||||||
|
if NumberAnd(NumberEqual(5), NumberEqual(4)).Match(5) {
|
||||||
|
t.Fatal("NumberAnd(NumberEqual(5), NumberEqual(4)) should not match 5")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberOr(t *testing.T) {
|
||||||
|
if NumberOr().Match(5) {
|
||||||
|
t.Fatal("NumberOr() should not match 5")
|
||||||
|
}
|
||||||
|
if !NumberOr(NumberEqual(5)).Match(5) {
|
||||||
|
t.Fatal("NumberOr(NumberEqual(5)) should match 5")
|
||||||
|
}
|
||||||
|
if NumberOr(NumberEqual(5)).Match(4) {
|
||||||
|
t.Fatal("NumberOr(NumberEqual(5)) should not match 4")
|
||||||
|
}
|
||||||
|
if !NumberOr(NumberEqual(5), NumberEqual(4)).Match(5) {
|
||||||
|
t.Fatal("NumberOr(NumberEqual(5), NumberEqual(4)) should match 5")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberLessThan(t *testing.T) {
|
||||||
|
if NumberLessThan(3).Match(5) {
|
||||||
|
t.Fatal("NumberLessThan(3) should not match 5")
|
||||||
|
}
|
||||||
|
if NumberLessThan(3).Match(3) {
|
||||||
|
t.Fatal("NumberLessThan(3) should not match 3")
|
||||||
|
}
|
||||||
|
if !NumberLessThan(3).Match(1) {
|
||||||
|
t.Fatal("NumberLessThan(3) should match 1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberEqualOrLessThan(t *testing.T) {
|
||||||
|
if NumberEqualOrLessThan(3).Match(5) {
|
||||||
|
t.Fatal("NumberEqualOrLessThan(3) should not match 5")
|
||||||
|
}
|
||||||
|
if !NumberEqualOrLessThan(3).Match(3) {
|
||||||
|
t.Fatal("NumberEqualOrLessThan(3) should match 3")
|
||||||
|
}
|
||||||
|
if !NumberEqualOrLessThan(3).Match(1) {
|
||||||
|
t.Fatal("NumberEqualOrLessThan(3) should match 1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberGreaterThan(t *testing.T) {
|
||||||
|
if !NumberGreaterThan(3).Match(5) {
|
||||||
|
t.Fatal("NumberGreaterThan(3) should match 5")
|
||||||
|
}
|
||||||
|
if NumberGreaterThan(3).Match(3) {
|
||||||
|
t.Fatal("NumberGreaterThan(3) should not match 3")
|
||||||
|
}
|
||||||
|
if NumberGreaterThan(3).Match(1) {
|
||||||
|
t.Fatal("NumberGreaterThan(3) should not match 1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberEqualOrGreaterThan(t *testing.T) {
|
||||||
|
if !NumberGreaterThan(3).Match(5) {
|
||||||
|
t.Fatal("NumberGreaterThan(3) should match 5")
|
||||||
|
}
|
||||||
|
if NumberGreaterThan(3).Match(3) {
|
||||||
|
t.Fatal("NumberGreaterThan(3) should not match 3")
|
||||||
|
}
|
||||||
|
if NumberGreaterThan(3).Match(1) {
|
||||||
|
t.Fatal("NumberGreaterThan(3) should not match 1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
// NumberS 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 Value predicate. If the selector can't
|
||||||
|
// select any number from the value, then the predicate is
|
||||||
|
// false.
|
||||||
|
ValueP
|
||||||
|
|
||||||
|
// SelectFrom finds numbers from values using this selector. The
|
||||||
|
// list can be bigger or smaller than the initial lists,
|
||||||
|
// depending on the select criterias.
|
||||||
|
SelectFrom(...interface{}) []float64
|
||||||
|
|
||||||
|
// Filter will create a new NumberS that filters only the values
|
||||||
|
// who match the predicate.
|
||||||
|
Filter(...NumberP) NumberS
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number returns a NumberS that selects numbers from given values.
|
||||||
|
func Number() NumberS {
|
||||||
|
return &numberS{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type numberS struct {
|
||||||
|
vs ValueS
|
||||||
|
ip NumberP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *numberS) SelectFrom(values ...interface{}) []float64 {
|
||||||
|
numbers := []float64{}
|
||||||
|
if s.vs != nil {
|
||||||
|
values = s.vs.SelectFrom(values...)
|
||||||
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
i, ok := value.(float64)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s.ip != nil && !s.ip.Match(i) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
numbers = append(numbers, i)
|
||||||
|
}
|
||||||
|
return numbers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *numberS) Filter(predicates ...NumberP) NumberS {
|
||||||
|
if s.ip != nil {
|
||||||
|
predicates = append(predicates, s.ip)
|
||||||
|
}
|
||||||
|
return &numberS{
|
||||||
|
vs: s.vs,
|
||||||
|
ip: NumberAnd(predicates...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *numberS) Match(values interface{}) bool {
|
||||||
|
return len(s.SelectFrom(values)) != 0
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
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_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "k8s.io/kubectl/pkg/framework/unstructpath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNumberSSelectFrom(t *testing.T) {
|
||||||
|
s := Number().SelectFrom(
|
||||||
|
1.,
|
||||||
|
"string",
|
||||||
|
2.,
|
||||||
|
[]float64{3, 4})
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(s, []float64{1, 2}) {
|
||||||
|
t.Fatal("SelectFrom should select all integers")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberSFilter(t *testing.T) {
|
||||||
|
s := Number().
|
||||||
|
Filter(NumberGreaterThan(2), NumberEqualOrLessThan(4)).
|
||||||
|
SelectFrom(
|
||||||
|
1.,
|
||||||
|
2.,
|
||||||
|
3.,
|
||||||
|
4.,
|
||||||
|
5.)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(s, []float64{3, 4}) {
|
||||||
|
t.Fatal("SelectFrom should filter selected numberegers")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberSPredicate(t *testing.T) {
|
||||||
|
if !Number().Filter(NumberGreaterThan(10)).Match(12.) {
|
||||||
|
t.Fatal("SelectFromor matching element should match")
|
||||||
|
}
|
||||||
|
if Number().Filter(NumberGreaterThan(10)).Match(4.) {
|
||||||
|
t.Fatal("SelectFromor not matching element should not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberSFromValueS(t *testing.T) {
|
||||||
|
if !Children().Number().Filter(NumberGreaterThan(10)).Match([]interface{}{1., 2., 5., 12.}) {
|
||||||
|
t.Fatal("SelectFromor should find element that match")
|
||||||
|
}
|
||||||
|
if Children().Number().Filter(NumberGreaterThan(10)).Match([]interface{}{1., 2., 5.}) {
|
||||||
|
t.Fatal("SelectFromor shouldn't find element that match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
func filterSlice(ss SliceS, sf sliceFilter) ValueS {
|
||||||
|
return &valueS{
|
||||||
|
vf: &valueSliceFilter{
|
||||||
|
ss: ss,
|
||||||
|
sf: sf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a Slice-to-Value filter.
|
||||||
|
type sliceFilter interface {
|
||||||
|
SelectFrom(...[]interface{}) []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceAtPFilter struct {
|
||||||
|
ip NumberP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f sliceAtPFilter) SelectFrom(slices ...[]interface{}) []interface{} {
|
||||||
|
values := []interface{}{}
|
||||||
|
|
||||||
|
for _, slice := range slices {
|
||||||
|
for i := range slice {
|
||||||
|
if !f.ip.Match(float64(i)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
values = append(values, slice[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceLastFilter struct{}
|
||||||
|
|
||||||
|
func (f sliceLastFilter) SelectFrom(slices ...[]interface{}) []interface{} {
|
||||||
|
values := []interface{}{}
|
||||||
|
for _, slice := range slices {
|
||||||
|
if len(slice) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
values = append(values, slice[len(slice)-1])
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceAllFilter struct{}
|
||||||
|
|
||||||
|
func (sliceAllFilter) SelectFrom(slices ...[]interface{}) []interface{} {
|
||||||
|
values := []interface{}{}
|
||||||
|
for _, slice := range slices {
|
||||||
|
for _, v := range slice {
|
||||||
|
values = append(values, All().SelectFrom(v)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
// SliceP is a "slice predicate". It's a type that decides if a
|
||||||
|
// slice matches or not.
|
||||||
|
type SliceP interface {
|
||||||
|
Match([]interface{}) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceNot inverses the value of the predicate.
|
||||||
|
func SliceNot(predicate SliceP) SliceP {
|
||||||
|
return sliceNot{vp: predicate}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceNot struct {
|
||||||
|
vp SliceP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p sliceNot) Match(slice []interface{}) bool {
|
||||||
|
return !p.vp.Match(slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceAnd returns true if all the sub-predicates are true. If there are
|
||||||
|
// no sub-predicates, always returns true.
|
||||||
|
func SliceAnd(predicates ...SliceP) SliceP {
|
||||||
|
return sliceAnd{sps: predicates}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceAnd struct {
|
||||||
|
sps []SliceP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p sliceAnd) Match(slice []interface{}) bool {
|
||||||
|
for _, sp := range p.sps {
|
||||||
|
if !sp.Match(slice) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceOr returns true if any sub-predicate is true. If there are no
|
||||||
|
// sub-predicates, always returns false.
|
||||||
|
func SliceOr(predicates ...SliceP) SliceP {
|
||||||
|
sps := []SliceP{}
|
||||||
|
|
||||||
|
// Implements "De Morgan's law"
|
||||||
|
for _, sp := range predicates {
|
||||||
|
sps = append(sps, SliceNot(sp))
|
||||||
|
}
|
||||||
|
return SliceNot(SliceAnd(sps...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceLength matches if the length of the list matches the given
|
||||||
|
// integer predicate.
|
||||||
|
func SliceLength(ip NumberP) SliceP {
|
||||||
|
return sliceLength{ip: ip}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceLength struct {
|
||||||
|
ip NumberP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p sliceLength) Match(slice []interface{}) bool {
|
||||||
|
return p.ip.Match(float64(len(slice)))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
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_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "k8s.io/kubectl/pkg/framework/unstructpath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SliceTrue struct{}
|
||||||
|
|
||||||
|
func (SliceTrue) Match(slice []interface{}) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceNot(t *testing.T) {
|
||||||
|
if SliceNot(SliceTrue{}).Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceNot(SliceTrue{}) should never match")
|
||||||
|
}
|
||||||
|
if !SliceNot(SliceNot(SliceTrue{})).Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceNot(SliceNot(SliceTrue{})) should always match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceAnd(t *testing.T) {
|
||||||
|
if !SliceAnd().Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceAnd() should always match")
|
||||||
|
}
|
||||||
|
if SliceAnd(SliceNot(SliceTrue{})).Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceAnd(SliceNot(SliceTrue{})) should never match")
|
||||||
|
}
|
||||||
|
if !SliceAnd(SliceTrue{}).Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceAnd(SliceTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if !SliceAnd(SliceTrue{}, SliceTrue{}).Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceAnd(SliceTrue{}, SliceTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if SliceAnd(SliceTrue{}, SliceNot(SliceTrue{}), SliceTrue{}).Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceAnd(SliceTrue{}, SliceNot(SliceTrue{}), SliceTrue{}) should never match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceOr(t *testing.T) {
|
||||||
|
if SliceOr().Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceOr() should never match")
|
||||||
|
}
|
||||||
|
if SliceOr(SliceNot(SliceTrue{})).Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceOr(SliceNot(SliceTrue{})) should never match")
|
||||||
|
}
|
||||||
|
if !SliceOr(SliceTrue{}).Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceOr(SliceTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if !SliceOr(SliceTrue{}, SliceTrue{}).Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceOr(SliceTrue{}, SliceTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if !SliceOr(SliceTrue{}, SliceNot(SliceTrue{}), SliceTrue{}).Match([]interface{}{}) {
|
||||||
|
t.Fatal("SliceOr(SliceTrue{}, SliceNot(SliceTrue{}), SliceTrue{}) should always match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSliceLength(t *testing.T) {
|
||||||
|
slice := []interface{}{1, 2, 3}
|
||||||
|
if !SliceLength(NumberEqual(3)).Match(slice) {
|
||||||
|
t.Fatal(`SliceLength(NumberEqual(3)) should match []interface{}{1, 2, 3}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if SliceLength(NumberLessThan(2)).Match(slice) {
|
||||||
|
t.Fatal(`SliceLength(NumberLessThan(2)) should not match []interface{}{1, 2, 3}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !SliceLength(NumberLessThan(5)).Match(slice) {
|
||||||
|
t.Fatal(`SliceLength(NumberLessThan(5)) should match []interface{}{1, 2, 3}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
// SliceS 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 Value predicate. If the selector
|
||||||
|
// can't select any slice from the value, then the predicate is
|
||||||
|
// false.
|
||||||
|
ValueP
|
||||||
|
|
||||||
|
// SelectFrom finds slices from values using this selector. The
|
||||||
|
// list can be bigger or smaller than the initial lists,
|
||||||
|
// 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) ValueS
|
||||||
|
// 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 ...NumberP) ValueS
|
||||||
|
// Last returns a selector that selects the last value of the
|
||||||
|
// list. If the list is empty, then nothing will be selected.
|
||||||
|
Last() ValueS
|
||||||
|
|
||||||
|
// All returns a selector that selects all direct and indrect
|
||||||
|
// children of the given values.
|
||||||
|
Children() ValueS
|
||||||
|
// All returns a selector that selects all direct and indrect
|
||||||
|
// children of the given values.
|
||||||
|
All() ValueS
|
||||||
|
|
||||||
|
// Filter will create a new SliceS that filters only the values
|
||||||
|
// who match the predicate.
|
||||||
|
Filter(...SliceP) SliceS
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice creates a selector that takes values and filters them into
|
||||||
|
// slices if possible.
|
||||||
|
func Slice() SliceS {
|
||||||
|
return &sliceS{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sliceS struct {
|
||||||
|
vs ValueS
|
||||||
|
sp SliceP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sliceS) SelectFrom(values ...interface{}) [][]interface{} {
|
||||||
|
if s.vs != nil {
|
||||||
|
values = s.vs.SelectFrom(values...)
|
||||||
|
}
|
||||||
|
|
||||||
|
slices := [][]interface{}{}
|
||||||
|
for _, value := range values {
|
||||||
|
slice, ok := value.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s.sp != nil && !s.sp.Match(slice) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
slices = append(slices, slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
return slices
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sliceS) At(index int) ValueS {
|
||||||
|
return s.AtP(NumberEqual(float64(index)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sliceS) AtP(predicates ...NumberP) ValueS {
|
||||||
|
return filterSlice(s, sliceAtPFilter{ip: NumberAnd(predicates...)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sliceS) Last() ValueS {
|
||||||
|
return filterSlice(s, sliceLastFilter{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sliceS) Children() ValueS {
|
||||||
|
// No predicates means select all direct children.
|
||||||
|
return s.AtP()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sliceS) All() ValueS {
|
||||||
|
return filterSlice(s, sliceAllFilter{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sliceS) Filter(sps ...SliceP) SliceS {
|
||||||
|
return &sliceS{vs: s.vs, sp: SliceAnd(append(sps, s.sp)...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sliceS) Match(value interface{}) bool {
|
||||||
|
return len(s.SelectFrom(value)) != 0
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StringP is a "string predicate". It's a type that decides if a
|
||||||
|
// string matches or not.
|
||||||
|
type StringP interface {
|
||||||
|
Match(string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringNot will inverse the result of the predicate.
|
||||||
|
func StringNot(predicate StringP) StringP {
|
||||||
|
return stringNot{sp: predicate}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringNot struct {
|
||||||
|
sp StringP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p stringNot) Match(str string) bool {
|
||||||
|
return !p.sp.Match(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringAnd returns true if all the sub-predicates are true. If there are
|
||||||
|
// no sub-predicates, always returns true.
|
||||||
|
func StringAnd(predicates ...StringP) StringP {
|
||||||
|
return stringAnd{sps: predicates}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringAnd struct {
|
||||||
|
sps []StringP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p stringAnd) Match(str string) bool {
|
||||||
|
for _, sp := range p.sps {
|
||||||
|
if !sp.Match(str) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringOr returns true if any sub-predicate is true. If there are no
|
||||||
|
// sub-predicates, always returns false.
|
||||||
|
func StringOr(predicates ...StringP) StringP {
|
||||||
|
sps := []StringP{}
|
||||||
|
|
||||||
|
// Implements "De Morgan's law"
|
||||||
|
for _, sp := range predicates {
|
||||||
|
sps = append(sps, StringNot(sp))
|
||||||
|
}
|
||||||
|
return StringNot(StringAnd(sps...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringEqual returns a predicate that matches only the exact string.
|
||||||
|
func StringEqual(str string) StringP {
|
||||||
|
return stringEqual{str: str}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringEqual struct {
|
||||||
|
str string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p stringEqual) Match(str string) bool {
|
||||||
|
return p.str == str
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringLength matches if the length of the string matches the given
|
||||||
|
// integer predicate.
|
||||||
|
func StringLength(predicate NumberP) StringP {
|
||||||
|
return stringLength{ip: predicate}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringLength struct {
|
||||||
|
ip NumberP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p stringLength) Match(str string) bool {
|
||||||
|
return p.ip.Match(float64(len(str)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringHasPrefix matches if the string starts with the given prefix.
|
||||||
|
func StringHasPrefix(prefix string) StringP {
|
||||||
|
return stringHasPrefix{prefix: prefix}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringHasPrefix struct {
|
||||||
|
prefix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p stringHasPrefix) Match(str string) bool {
|
||||||
|
return strings.HasPrefix(str, p.prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringHasSuffix matches if the string ends with the given suffix.
|
||||||
|
func StringHasSuffix(suffix string) StringP {
|
||||||
|
return stringHasSuffix{suffix: suffix}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringHasSuffix struct {
|
||||||
|
suffix string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p stringHasSuffix) Match(str string) bool {
|
||||||
|
return strings.HasSuffix(str, p.suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringRegexp matches if the string matches with the given regexp.
|
||||||
|
func StringRegexp(regex *regexp.Regexp) StringP {
|
||||||
|
return stringRegexp{regex: regex}
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringRegexp struct {
|
||||||
|
regex *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p stringRegexp) Match(str string) bool {
|
||||||
|
return p.regex.MatchString(str)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
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_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "k8s.io/kubectl/pkg/framework/unstructpath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStringEqual(t *testing.T) {
|
||||||
|
if !StringEqual("some string").Match("some string") {
|
||||||
|
t.Fatal(`StringEqual("some string") should match "some string"`)
|
||||||
|
}
|
||||||
|
if StringEqual("some string").Match("another string") {
|
||||||
|
t.Fatal(`StringEqual("some string") should not match "another string"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringNot(t *testing.T) {
|
||||||
|
if StringNot(StringEqual("some string")).Match("some string") {
|
||||||
|
t.Fatal(`StringNot(StringEqual("some string")) should not match "some string"`)
|
||||||
|
}
|
||||||
|
if !StringNot(StringEqual("some string")).Match("another string") {
|
||||||
|
t.Fatal(`StringNot(StringEqual("some string")) should match "another string"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringAnd(t *testing.T) {
|
||||||
|
if !StringAnd().Match("some string") {
|
||||||
|
t.Fatal(`StringAnd() should match "some string"`)
|
||||||
|
}
|
||||||
|
if !StringAnd(StringEqual("some string")).Match("some string") {
|
||||||
|
t.Fatal(`StringAnd(StringEqual("some string")) should match "some string"`)
|
||||||
|
}
|
||||||
|
if StringAnd(StringEqual("some string")).Match("another string") {
|
||||||
|
t.Fatal(`StringAnd(StringEqual("some string")) should not match "another string"`)
|
||||||
|
}
|
||||||
|
if StringAnd(StringEqual("some string"), StringEqual("another string")).Match("some string") {
|
||||||
|
t.Fatal(`StringAnd(StringEqual("some string"), StringEqual("another string")) should not match "some string"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringOr(t *testing.T) {
|
||||||
|
if StringOr().Match("some string") {
|
||||||
|
t.Fatal(`StringOr() should not match "some string"`)
|
||||||
|
}
|
||||||
|
if !StringOr(StringEqual("some string")).Match("some string") {
|
||||||
|
t.Fatal(`StringOr(StringEqual("some string")) should match "some string"`)
|
||||||
|
}
|
||||||
|
if StringOr(StringEqual("some string")).Match("another string") {
|
||||||
|
t.Fatal(`StringOr(StringEqual("some string")) should not match "another string"`)
|
||||||
|
}
|
||||||
|
if !StringOr(StringEqual("some string"), StringEqual("another string")).Match("some string") {
|
||||||
|
t.Fatal(`StringOr(StringEqual("some string"), StringEqual("another string")) should match "some string"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringLength(t *testing.T) {
|
||||||
|
if !StringLength(NumberEqual(11)).Match("some string") {
|
||||||
|
t.Fatal(`StringLength(NumberEqual(11)) should match "some string"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if StringLength(NumberLessThan(6)).Match("some string") {
|
||||||
|
t.Fatal(`StringLength(NumberLessThan(6)) should not match "some string"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !StringLength(NumberLessThan(16)).Match("some string") {
|
||||||
|
t.Fatal(`StringLength(NumberLessThan(16)) should match "some string"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringHasPrefix(t *testing.T) {
|
||||||
|
if !StringHasPrefix("some ").Match("some string") {
|
||||||
|
t.Fatal(`StringHasPrefix("some ") should match "some string"`)
|
||||||
|
}
|
||||||
|
if StringHasPrefix("some ").Match("another string") {
|
||||||
|
t.Fatal(`StringHasPrefix("some ") should not match "some string"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringHasSuffix(t *testing.T) {
|
||||||
|
if !StringHasSuffix("string").Match("some string") {
|
||||||
|
t.Fatal(`StringHasSuffix("string") should match "some string"`)
|
||||||
|
}
|
||||||
|
if StringHasSuffix("integer").Match("some string") {
|
||||||
|
t.Fatal(`StringHasSuffix("integer") should not match "some string"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringRegexp(t *testing.T) {
|
||||||
|
if !StringRegexp(regexp.MustCompile(".*")).Match("") {
|
||||||
|
t.Fatal(`StringRegexp(regexp.MustCompile(".*")) should match ""`)
|
||||||
|
}
|
||||||
|
if !StringRegexp(regexp.MustCompile(".*")).Match("Anything") {
|
||||||
|
t.Fatal(`StringRegexp(regexp.MustCompile(".*")) should match "Anything"`)
|
||||||
|
}
|
||||||
|
if !StringRegexp(regexp.MustCompile("word")).Match("word") {
|
||||||
|
t.Fatal(`StringRegexp(regexp.MustCompile("word")) should match "word"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
// StringS 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 Value predicate. If the selector can't
|
||||||
|
// select any string from the value, then the predicate is
|
||||||
|
// false.
|
||||||
|
ValueP
|
||||||
|
|
||||||
|
// SelectFrom finds strings from values using this selector. The
|
||||||
|
// list can be bigger or smaller than the initial lists,
|
||||||
|
// depending on the select criterias.
|
||||||
|
SelectFrom(...interface{}) []string
|
||||||
|
|
||||||
|
// Filter will create a new StringS that filters only the values
|
||||||
|
// who match the predicate.
|
||||||
|
Filter(...StringP) StringS
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringS struct {
|
||||||
|
vs ValueS
|
||||||
|
sp StringP
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a StringS that selects strings from values.
|
||||||
|
func String() StringS {
|
||||||
|
return &stringS{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringS) SelectFrom(values ...interface{}) []string {
|
||||||
|
strings := []string{}
|
||||||
|
if s.vs != nil {
|
||||||
|
values = s.vs.SelectFrom(values...)
|
||||||
|
}
|
||||||
|
for _, value := range values {
|
||||||
|
str, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s.sp != nil && !s.sp.Match(str) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
strings = append(strings, str)
|
||||||
|
}
|
||||||
|
return strings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringS) Filter(predicates ...StringP) StringS {
|
||||||
|
if s.sp != nil {
|
||||||
|
predicates = append(predicates, s.sp)
|
||||||
|
}
|
||||||
|
return &stringS{
|
||||||
|
vs: s.vs,
|
||||||
|
sp: StringAnd(predicates...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringS) Match(values interface{}) bool {
|
||||||
|
return len(s.SelectFrom(values)) != 0
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
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_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "k8s.io/kubectl/pkg/framework/unstructpath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStringSSelectFrom(t *testing.T) {
|
||||||
|
s := String().SelectFrom(
|
||||||
|
"my string",
|
||||||
|
1,
|
||||||
|
"your string",
|
||||||
|
[]int{3, 4})
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(s, []string{"my string", "your string"}) {
|
||||||
|
t.Fatal("SelectFrom should select all integers")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringSFilter(t *testing.T) {
|
||||||
|
s := String().
|
||||||
|
Filter(StringLength(NumberEqual(4))).
|
||||||
|
SelectFrom(
|
||||||
|
"one",
|
||||||
|
"two",
|
||||||
|
"three",
|
||||||
|
"four",
|
||||||
|
"five")
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(s, []string{"four", "five"}) {
|
||||||
|
t.Fatal("SelectFrom should filter selected strings")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringSPredicate(t *testing.T) {
|
||||||
|
if !String().Filter(StringLength(NumberEqual(4))).Match("four") {
|
||||||
|
t.Fatal("SelectFromor matching element should match")
|
||||||
|
}
|
||||||
|
if String().Filter(StringLength(NumberEqual(10))).Match("four") {
|
||||||
|
t.Fatal("SelectFromor not matching element should not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringSFromValueS(t *testing.T) {
|
||||||
|
if !Children().String().Filter(StringLength(NumberEqual(4))).Match([]interface{}{"four", "five"}) {
|
||||||
|
t.Fatal("SelectFromor should find element that match")
|
||||||
|
}
|
||||||
|
if Children().String().Filter(StringLength(NumberEqual(4))).Match([]interface{}{"one", "two", "three"}) {
|
||||||
|
t.Fatal("SelectFromor shouldn't find element that match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
// A valueFilter allows us to chain ValueS to ValueS. None of this is
|
||||||
|
// public. It's implementing the "SelectFrom" part of a ValueS.
|
||||||
|
type valueFilter interface {
|
||||||
|
SelectFrom(...interface{}) []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// valueFilterP filters using a predicate.
|
||||||
|
type valueFilterP struct {
|
||||||
|
vp ValueP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *valueFilterP) SelectFrom(values ...interface{}) []interface{} {
|
||||||
|
vs := []interface{}{}
|
||||||
|
for _, value := range values {
|
||||||
|
if f.vp.Match(value) {
|
||||||
|
vs = append(vs, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueChildrenFilter struct{}
|
||||||
|
|
||||||
|
func (valueChildrenFilter) SelectFrom(values ...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 values {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// valueSliceFilter is a Value-to-Slice combined with a Slice-to-Value
|
||||||
|
// to form a Value-to-Value.
|
||||||
|
type valueSliceFilter struct {
|
||||||
|
ss SliceS
|
||||||
|
sf sliceFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *valueSliceFilter) SelectFrom(values ...interface{}) []interface{} {
|
||||||
|
return s.sf.SelectFrom(s.ss.SelectFrom(values...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// valueMapFilter is a Value-to-Map combined with a Map-to-Value to form
|
||||||
|
// a Value-to-Value.
|
||||||
|
type valueMapFilter struct {
|
||||||
|
ms MapS
|
||||||
|
mf mapFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *valueMapFilter) SelectFrom(values ...interface{}) []interface{} {
|
||||||
|
return s.mf.SelectFrom(s.ms.SelectFrom(values...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueAllFilter struct{}
|
||||||
|
|
||||||
|
func (valueAllFilter) SelectFrom(values ...interface{}) []interface{} {
|
||||||
|
vs := []interface{}{}
|
||||||
|
for _, value := range values {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValueP is a "value predicate". It's a type that decides if a
|
||||||
|
// value matches or not.
|
||||||
|
type ValueP interface {
|
||||||
|
Match(interface{}) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueDeepEqual compares the Value data with DeepEqual.
|
||||||
|
func ValueDeepEqual(v interface{}) ValueP {
|
||||||
|
return valueEqual{v: v}
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueEqual struct {
|
||||||
|
v interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p valueEqual) Match(v interface{}) bool {
|
||||||
|
return reflect.DeepEqual(v, p.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueNot inverses the value of the predicate.
|
||||||
|
func ValueNot(predicate ValueP) ValueP {
|
||||||
|
return valueNot{vp: predicate}
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueNot struct {
|
||||||
|
vp ValueP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p valueNot) Match(v interface{}) bool {
|
||||||
|
return !p.vp.Match(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueAnd returns true if all the sub-predicates are true. If there are
|
||||||
|
// no sub-predicates, always returns true.
|
||||||
|
func ValueAnd(predicates ...ValueP) ValueP {
|
||||||
|
return valueAnd{vps: predicates}
|
||||||
|
}
|
||||||
|
|
||||||
|
type valueAnd struct {
|
||||||
|
vps []ValueP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p valueAnd) Match(value interface{}) bool {
|
||||||
|
for _, vp := range p.vps {
|
||||||
|
if !vp.Match(value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueOr returns true if any sub-predicate is true. If there are no
|
||||||
|
// sub-predicates, always returns false.
|
||||||
|
func ValueOr(predicates ...ValueP) ValueP {
|
||||||
|
vps := []ValueP{}
|
||||||
|
|
||||||
|
// Implements "De Morgan's law"
|
||||||
|
for _, vp := range predicates {
|
||||||
|
vps = append(vps, ValueNot(vp))
|
||||||
|
}
|
||||||
|
return ValueNot(ValueAnd(vps...))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
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_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "k8s.io/kubectl/pkg/framework/unstructpath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ValueTrue struct{}
|
||||||
|
|
||||||
|
func (ValueTrue) Match(value interface{}) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueNot(t *testing.T) {
|
||||||
|
if ValueNot(ValueTrue{}).Match(nil) {
|
||||||
|
t.Fatal("ValueNot(ValueTrue{}) should never match")
|
||||||
|
}
|
||||||
|
if !ValueNot(ValueNot(ValueTrue{})).Match(nil) {
|
||||||
|
t.Fatal("ValueNot(ValueNot(ValueTrue{})) should always match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueAnd(t *testing.T) {
|
||||||
|
if !ValueAnd().Match(nil) {
|
||||||
|
t.Fatal("ValueAnd() should always match")
|
||||||
|
}
|
||||||
|
if ValueAnd(ValueNot(ValueTrue{})).Match(nil) {
|
||||||
|
t.Fatal("ValueAnd(ValueNot(ValueTrue{})) should never match")
|
||||||
|
}
|
||||||
|
if !ValueAnd(ValueTrue{}).Match(nil) {
|
||||||
|
t.Fatal("ValueAnd(ValueTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if !ValueAnd(ValueTrue{}, ValueTrue{}).Match(nil) {
|
||||||
|
t.Fatal("ValueAnd(ValueTrue{}, ValueTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if ValueAnd(ValueTrue{}, ValueNot(ValueTrue{}), ValueTrue{}).Match(nil) {
|
||||||
|
t.Fatal("ValueAnd(ValueTrue{}, ValueNot(ValueTrue{}), ValueTrue{}) should never match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueOr(t *testing.T) {
|
||||||
|
if ValueOr().Match(nil) {
|
||||||
|
t.Fatal("ValueOr() should never match")
|
||||||
|
}
|
||||||
|
if ValueOr(ValueNot(ValueTrue{})).Match(nil) {
|
||||||
|
t.Fatal("ValueOr(ValueNot(ValueTrue{})) should never match")
|
||||||
|
}
|
||||||
|
if !ValueOr(ValueTrue{}).Match(nil) {
|
||||||
|
t.Fatal("ValueOr(ValueTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if !ValueOr(ValueTrue{}, ValueTrue{}).Match(nil) {
|
||||||
|
t.Fatal("ValueOr(ValueTrue{}, ValueTrue{}) should always match")
|
||||||
|
}
|
||||||
|
if !ValueOr(ValueTrue{}, ValueNot(ValueTrue{}), ValueTrue{}).Match(nil) {
|
||||||
|
t.Fatal("ValueOr(ValueTrue{}, ValueNot(ValueTrue{}), ValueTrue{}) should always match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueDeepEqual(t *testing.T) {
|
||||||
|
if !ValueDeepEqual([]int{1, 2, 3}).Match([]int{1, 2, 3}) {
|
||||||
|
t.Fatal("ValueDeepEqual([]int{1, 2, 3}) should match []int{1, 2, 3}")
|
||||||
|
}
|
||||||
|
if ValueDeepEqual([]int{1, 2, 3}).Match([]int{1, 2, 4}) {
|
||||||
|
t.Fatal("ValueDeepEqual([]int{1, 2, 3}) should not match []int{1, 2, 4}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
// ValueS is a "value selector". It filters values based on the
|
||||||
|
// "filtered" predicates.
|
||||||
|
type ValueS interface {
|
||||||
|
// ValueS can be used as a Value predicate. If the selector can't
|
||||||
|
// select any value from the value, then the predicate is
|
||||||
|
// false.
|
||||||
|
ValueP
|
||||||
|
|
||||||
|
// SelectFrom finds values from values 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() ValueS
|
||||||
|
// All returns a selector that selects all direct and indrect
|
||||||
|
// children of the given values.
|
||||||
|
All() ValueS
|
||||||
|
|
||||||
|
// Filter will create a new StringS that filters only the values
|
||||||
|
// who match the predicate.
|
||||||
|
Filter(...ValueP) ValueS
|
||||||
|
}
|
||||||
|
|
||||||
|
// Children selects all the children of the values.
|
||||||
|
func Children() ValueS {
|
||||||
|
return &valueS{vf: valueChildrenFilter{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All selects all the direct and indirect childrens of the values.
|
||||||
|
func All() ValueS {
|
||||||
|
return &valueS{vf: valueAllFilter{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter will only return the values that match the predicate.
|
||||||
|
func Filter(predicates ...ValueP) ValueS {
|
||||||
|
return &valueS{vf: &valueFilterP{vp: ValueAnd(predicates...)}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueS is a "Value SelectFromor". It selects a list of values, maps,
|
||||||
|
// slices, strings, integer from a list of values.
|
||||||
|
type valueS struct {
|
||||||
|
vs ValueS
|
||||||
|
vf valueFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if the selector can find items in the given
|
||||||
|
// value. Otherwise, it returns false.
|
||||||
|
func (s *valueS) Match(value interface{}) bool {
|
||||||
|
return len(s.SelectFrom(value)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *valueS) SelectFrom(values ...interface{}) []interface{} {
|
||||||
|
if s.vs != nil {
|
||||||
|
values = s.vs.SelectFrom(values...)
|
||||||
|
}
|
||||||
|
return s.vf.SelectFrom(values...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *valueS) Map() MapS {
|
||||||
|
return &mapS{vs: s}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *valueS) Slice() SliceS {
|
||||||
|
return &sliceS{vs: s}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *valueS) Number() NumberS {
|
||||||
|
return &numberS{vs: s}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *valueS) String() StringS {
|
||||||
|
return &stringS{vs: s}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *valueS) Children() ValueS {
|
||||||
|
return &valueS{vs: s, vf: valueChildrenFilter{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *valueS) All() ValueS {
|
||||||
|
return &valueS{vs: s, vf: valueAllFilter{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *valueS) Filter(predicates ...ValueP) ValueS {
|
||||||
|
return &valueS{vs: s, vf: &valueFilterP{vp: ValueAnd(predicates...)}}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,218 @@
|
||||||
|
/*
|
||||||
|
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_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubectl/pkg/framework/unstructpath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAll(t *testing.T) {
|
||||||
|
u := map[string]interface{}{
|
||||||
|
"key1": 1.,
|
||||||
|
"key2": []interface{}{2., 3., map[string]interface{}{"key3": 4.}},
|
||||||
|
"key4": map[string]interface{}{"key5": 5.},
|
||||||
|
}
|
||||||
|
|
||||||
|
numbers := unstructpath.All().Number().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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChildren(t *testing.T) {
|
||||||
|
u := map[string]interface{}{
|
||||||
|
"key1": 1.,
|
||||||
|
"key2": []interface{}{2., 3., map[string]interface{}{"key3": 4.}},
|
||||||
|
"key4": 5.,
|
||||||
|
}
|
||||||
|
|
||||||
|
numbers := unstructpath.Children().Number().SelectFrom(u)
|
||||||
|
expected := []float64{1., 5.}
|
||||||
|
if !reflect.DeepEqual(expected, numbers) {
|
||||||
|
t.Fatalf("Expected to find all numbers (%v), got: %v", expected, numbers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFilter(t *testing.T) {
|
||||||
|
us := []interface{}{
|
||||||
|
[]interface{}{1., 2., 3.},
|
||||||
|
[]interface{}{3., 4., 5., 6.},
|
||||||
|
map[string]interface{}{},
|
||||||
|
5.,
|
||||||
|
"string",
|
||||||
|
}
|
||||||
|
expected := []interface{}{us[1]}
|
||||||
|
actual := unstructpath.Filter(unstructpath.Slice().At(3)).SelectFrom(us...)
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("Expected to filter (%v), got: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueSPredicate(t *testing.T) {
|
||||||
|
if !unstructpath.Slice().Match([]interface{}{}) {
|
||||||
|
t.Fatal("SelectFroming a slice from a slice should match.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueSMap(t *testing.T) {
|
||||||
|
root := map[string]interface{}{
|
||||||
|
"key1": "value",
|
||||||
|
"key2": 1,
|
||||||
|
"key3": []interface{}{
|
||||||
|
"other value",
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
"key4": map[string]interface{}{
|
||||||
|
"subkey": []interface{}{
|
||||||
|
3,
|
||||||
|
"string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []map[string]interface{}{
|
||||||
|
root,
|
||||||
|
root["key4"].(map[string]interface{}),
|
||||||
|
}
|
||||||
|
actual := unstructpath.All().Map().SelectFrom(root)
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("Map should find maps %v, got %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueSSlice(t *testing.T) {
|
||||||
|
root := map[string]interface{}{
|
||||||
|
"key1": "value",
|
||||||
|
"key2": 1,
|
||||||
|
"key3": []interface{}{
|
||||||
|
"other value",
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
"key4": map[string]interface{}{
|
||||||
|
"subkey": []interface{}{
|
||||||
|
3,
|
||||||
|
"string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := [][]interface{}{
|
||||||
|
root["key3"].([]interface{}),
|
||||||
|
root["key4"].(map[string]interface{})["subkey"].([]interface{}),
|
||||||
|
}
|
||||||
|
actual := unstructpath.All().Slice().SelectFrom(root)
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("Slice should find slices %#v, got %#v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueSChildren(t *testing.T) {
|
||||||
|
root := map[string]interface{}{
|
||||||
|
"key1": "value",
|
||||||
|
"key2": 1,
|
||||||
|
"key3": []interface{}{
|
||||||
|
"other value",
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
"key4": map[string]interface{}{
|
||||||
|
"subkey": []interface{}{
|
||||||
|
3,
|
||||||
|
"string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []interface{}{
|
||||||
|
root["key3"].([]interface{})[0],
|
||||||
|
root["key3"].([]interface{})[1],
|
||||||
|
}
|
||||||
|
actual := unstructpath.Map().Field("key3").Children().SelectFrom(root)
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueSNumber(t *testing.T) {
|
||||||
|
u := []interface{}{1., 2., "three", 4., 5., []interface{}{}}
|
||||||
|
|
||||||
|
numbers := unstructpath.Children().Number().SelectFrom(u)
|
||||||
|
expected := []float64{1., 2., 4., 5.}
|
||||||
|
if !reflect.DeepEqual(expected, numbers) {
|
||||||
|
t.Fatalf("Children().Number() should select %v, got %v", expected, numbers)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueSString(t *testing.T) {
|
||||||
|
root := map[string]interface{}{
|
||||||
|
"key1": "value",
|
||||||
|
"key2": 1,
|
||||||
|
"key3": []interface{}{
|
||||||
|
"other value",
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
"key4": map[string]interface{}{
|
||||||
|
"subkey": []interface{}{
|
||||||
|
3,
|
||||||
|
"string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []string{
|
||||||
|
"value",
|
||||||
|
"other value",
|
||||||
|
"string",
|
||||||
|
}
|
||||||
|
actual := unstructpath.All().String().SelectFrom(root)
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueSAll(t *testing.T) {
|
||||||
|
root := map[string]interface{}{
|
||||||
|
"key1": "value",
|
||||||
|
"key2": 1,
|
||||||
|
"key3": []interface{}{
|
||||||
|
"other value",
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
"key4": map[string]interface{}{
|
||||||
|
"subkey": []interface{}{
|
||||||
|
3,
|
||||||
|
"string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []interface{}{
|
||||||
|
root["key4"],
|
||||||
|
root["key4"].(map[string]interface{})["subkey"],
|
||||||
|
root["key4"].(map[string]interface{})["subkey"].([]interface{})[0],
|
||||||
|
root["key4"].(map[string]interface{})["subkey"].([]interface{})[1],
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := unstructpath.Map().Field("key4").All().SelectFrom(root)
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue