mirror of https://github.com/kubernetes/kops.git
178 lines
3.8 KiB
Go
178 lines
3.8 KiB
Go
/*
|
|
Copyright 2020 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 reflectutils
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"text/scanner"
|
|
|
|
"k8s.io/klog/v2"
|
|
)
|
|
|
|
type FieldPath struct {
|
|
elements []FieldPathElement
|
|
}
|
|
|
|
type FieldPathElementType int
|
|
|
|
const (
|
|
FieldPathElementTypeField = iota
|
|
FieldPathElementTypeMapKey
|
|
FieldPathElementTypeArrayIndex
|
|
FieldPathElementTypeWildcardIndex
|
|
)
|
|
|
|
type FieldPathElement struct {
|
|
Type FieldPathElementType
|
|
token string
|
|
number int
|
|
}
|
|
|
|
func (f *FieldPath) Extend(el FieldPathElement) *FieldPath {
|
|
n := len(f.elements)
|
|
newElements := make([]FieldPathElement, n+1)
|
|
copy(newElements, f.elements)
|
|
newElements[n] = el
|
|
return &FieldPath{elements: newElements}
|
|
}
|
|
|
|
func (f *FieldPath) String() string {
|
|
var sb strings.Builder
|
|
for i, el := range f.elements {
|
|
switch el.Type {
|
|
case FieldPathElementTypeField:
|
|
if i != 0 {
|
|
sb.WriteString(".")
|
|
}
|
|
sb.WriteString(el.token)
|
|
case FieldPathElementTypeMapKey:
|
|
sb.WriteString("[")
|
|
sb.WriteString(el.token)
|
|
sb.WriteString("]")
|
|
case FieldPathElementTypeWildcardIndex:
|
|
sb.WriteString("[*]")
|
|
case FieldPathElementTypeArrayIndex:
|
|
sb.WriteString("[")
|
|
sb.WriteString(strconv.Itoa(el.number))
|
|
sb.WriteString("]")
|
|
default:
|
|
klog.Fatalf("unknown token type %+v", el)
|
|
}
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
func ParseFieldPath(s string) (*FieldPath, error) {
|
|
var elements []FieldPathElement
|
|
|
|
var scan scanner.Scanner
|
|
|
|
scan.Init(strings.NewReader(s))
|
|
scan.Mode ^= scanner.SkipComments // don't skip comments
|
|
|
|
for tok := scan.Scan(); tok != scanner.EOF; tok = scan.Scan() {
|
|
switch tok {
|
|
case scanner.Ident:
|
|
field := scan.TokenText()
|
|
elements = append(elements, FieldPathElement{
|
|
Type: FieldPathElementTypeField,
|
|
token: field,
|
|
})
|
|
|
|
case '.', '/':
|
|
// Skip
|
|
|
|
case '[':
|
|
{
|
|
tok := scan.Scan()
|
|
switch tok {
|
|
case '*':
|
|
elements = append(elements, FieldPathElement{
|
|
Type: FieldPathElementTypeWildcardIndex,
|
|
})
|
|
|
|
case scanner.Int:
|
|
v := scan.TokenText()
|
|
n, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot parse token %q as array-index", v)
|
|
}
|
|
elements = append(elements, FieldPathElement{
|
|
Type: FieldPathElementTypeArrayIndex,
|
|
number: n,
|
|
})
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unexpected token %v (%s)", tok, scan.TokenText())
|
|
}
|
|
|
|
tok = scan.Scan()
|
|
switch tok {
|
|
case ']':
|
|
// ok
|
|
default:
|
|
return nil, fmt.Errorf("unexpected token %v (%s)", tok, scan.TokenText())
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unexpected token %v (%s)", tok, scan.TokenText())
|
|
}
|
|
}
|
|
|
|
return &FieldPath{elements: elements}, nil
|
|
}
|
|
|
|
func (p *FieldPath) IsEmpty() bool {
|
|
return len(p.elements) == 0
|
|
}
|
|
|
|
func (p *FieldPath) Matches(r *FieldPath) bool {
|
|
if len(p.elements) != len(r.elements) {
|
|
return false
|
|
}
|
|
return p.HasPrefixMatch(r)
|
|
}
|
|
|
|
func (p *FieldPath) HasPrefixMatch(r *FieldPath) bool {
|
|
if len(p.elements) < len(r.elements) {
|
|
return false
|
|
}
|
|
for i := range r.elements {
|
|
if p.elements[i] == r.elements[i] {
|
|
continue
|
|
}
|
|
isMatch := false
|
|
|
|
matcher := p.elements[i]
|
|
target := r.elements[i]
|
|
switch matcher.Type {
|
|
case FieldPathElementTypeWildcardIndex:
|
|
if target.Type == FieldPathElementTypeArrayIndex {
|
|
isMatch = true
|
|
}
|
|
}
|
|
if !isMatch {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|