mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-08-23 22:26:49 +00:00
Bumps [github.com/google/cel-go](https://github.com/google/cel-go) from 0.27.0 to 0.28.0. - [Release notes](https://github.com/google/cel-go/releases) - [Commits](https://github.com/google/cel-go/compare/v0.27.0...v0.28.0) --- updated-dependencies: - dependency-name: github.com/google/cel-go dependency-version: 0.28.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
164 lines
4.3 KiB
Go
164 lines
4.3 KiB
Go
package cel
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/google/cel-go/common"
|
|
"github.com/google/cel-go/common/types"
|
|
)
|
|
|
|
// fieldPath represents a selection path to a field from a variable in a CEL environment.
|
|
type fieldPath struct {
|
|
celType *Type
|
|
// path represents the selection path to the field.
|
|
path string
|
|
description string
|
|
isLeaf bool
|
|
}
|
|
|
|
// Documentation implements the Documentor interface.
|
|
func (f *fieldPath) Documentation() *common.Doc {
|
|
return common.NewFieldDoc(f.path, f.celType.String(), f.description)
|
|
}
|
|
|
|
type documentationProvider interface {
|
|
// FindStructFieldDescription returns documentation for a field if available.
|
|
// Returns false if the field could not be found.
|
|
FindStructFieldDescription(typeName, fieldName string) (string, bool)
|
|
}
|
|
|
|
type backtrack struct {
|
|
// provider used to resolve types.
|
|
provider types.Provider
|
|
// paths of fields that have been visited along the path.
|
|
path []string
|
|
// types of fields that have been visited along the path. used to avoid cycles.
|
|
types []*Type
|
|
}
|
|
|
|
func (b *backtrack) push(pathStep string, celType *Type) {
|
|
b.path = append(b.path, pathStep)
|
|
b.types = append(b.types, celType)
|
|
}
|
|
|
|
func (b *backtrack) pop() {
|
|
b.path = b.path[:len(b.path)-1]
|
|
b.types = b.types[:len(b.types)-1]
|
|
}
|
|
|
|
func formatPath(path []string) string {
|
|
var buffer strings.Builder
|
|
for i, p := range path {
|
|
if i == 0 {
|
|
buffer.WriteString(p)
|
|
continue
|
|
}
|
|
if strings.HasPrefix(p, "[") {
|
|
buffer.WriteString(p)
|
|
continue
|
|
}
|
|
buffer.WriteString(".")
|
|
buffer.WriteString(p)
|
|
}
|
|
return buffer.String()
|
|
}
|
|
|
|
func (b *backtrack) expandFieldPaths(celType *Type, paths []*fieldPath) []*fieldPath {
|
|
if slices.ContainsFunc(b.types[:len(b.types)-1], func(t *Type) bool { return t.String() == celType.String() }) {
|
|
// Cycle detected, so stop expanding.
|
|
paths[len(paths)-1].isLeaf = false
|
|
return paths
|
|
}
|
|
switch celType.Kind() {
|
|
case types.StructKind:
|
|
fields, ok := b.provider.FindStructFieldNames(celType.String())
|
|
if !ok {
|
|
// Caller added this type to the path, so it must be a leaf.
|
|
paths[len(paths)-1].isLeaf = true
|
|
return paths
|
|
}
|
|
for _, field := range fields {
|
|
fieldType, ok := b.provider.FindStructFieldType(celType.String(), field)
|
|
if !ok {
|
|
// Field not found, either hidden or an error.
|
|
continue
|
|
}
|
|
b.push(field, celType)
|
|
description := ""
|
|
if docProvider, ok := b.provider.(documentationProvider); ok {
|
|
description, _ = docProvider.FindStructFieldDescription(celType.String(), field)
|
|
}
|
|
path := &fieldPath{
|
|
celType: fieldType.Type,
|
|
path: formatPath(b.path),
|
|
description: description,
|
|
isLeaf: false,
|
|
}
|
|
paths = append(paths, path)
|
|
paths = b.expandFieldPaths(fieldType.Type, paths)
|
|
b.pop()
|
|
}
|
|
return paths
|
|
case types.MapKind:
|
|
if len(celType.Parameters()) != 2 {
|
|
// dynamic map, so treat as a leaf.
|
|
paths[len(paths)-1].isLeaf = true
|
|
return paths
|
|
}
|
|
mapKeyType := celType.Parameters()[0]
|
|
mapValueType := celType.Parameters()[1]
|
|
// Add a placeholder for the map key kind (the zero value).
|
|
keyIdentifier := ""
|
|
switch mapKeyType.Kind() {
|
|
case types.StringKind:
|
|
keyIdentifier = "[\"\"]"
|
|
case types.IntKind:
|
|
keyIdentifier = "[0]"
|
|
case types.UintKind:
|
|
keyIdentifier = "[0u]"
|
|
case types.BoolKind:
|
|
keyIdentifier = "[false]"
|
|
default:
|
|
// Caller added this type to the path, so it must be a leaf.
|
|
paths[len(paths)-1].isLeaf = true
|
|
return paths
|
|
}
|
|
b.push(keyIdentifier, mapValueType)
|
|
defer b.pop()
|
|
return b.expandFieldPaths(mapValueType, paths)
|
|
case types.ListKind:
|
|
if len(celType.Parameters()) != 1 {
|
|
// dynamic list, so treat as a leaf.
|
|
paths[len(paths)-1].isLeaf = true
|
|
return paths
|
|
}
|
|
listElemType := celType.Parameters()[0]
|
|
b.push("[0]", listElemType)
|
|
defer b.pop()
|
|
return b.expandFieldPaths(listElemType, paths)
|
|
default:
|
|
paths[len(paths)-1].isLeaf = true
|
|
}
|
|
|
|
return paths
|
|
}
|
|
|
|
// fieldPathsForType expands the reachable fields from the given root identifier.
|
|
func fieldPathsForType(provider types.Provider, identifier string, celType *Type) []*fieldPath {
|
|
b := &backtrack{
|
|
provider: provider,
|
|
path: []string{identifier},
|
|
types: []*Type{celType},
|
|
}
|
|
paths := []*fieldPath{
|
|
{
|
|
celType: celType,
|
|
path: identifier,
|
|
isLeaf: false,
|
|
},
|
|
}
|
|
|
|
return b.expandFieldPaths(celType, paths)
|
|
}
|