cmd/hub/cel/eval.go (80 lines of code) (raw):
// Copyright (c) 2022 EPAM Systems, Inc.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package cel
import (
"fmt"
"log"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/interpreter"
"github.com/epam/hubctl/cmd/hub/config"
"github.com/epam/hubctl/cmd/hub/parameters"
"github.com/epam/hubctl/cmd/hub/util"
)
func Eval(expression, vars string, autoVars, yamlValue bool) {
bindings, err := util.ParseKvList(vars)
if err != nil {
log.Fatalf("Unable to parse variable bindings: %v\n", err)
}
activation := &verboseActivation{bindings, autoVars}
env, err := cel.NewEnv()
if err != nil {
log.Fatalf("Unable to init CEL runtime: %v\n", err)
}
var out interface{}
if yamlValue {
out = yamlExpression(expression, env, activation)
} else {
out = plainExpression(expression, env, activation)
}
fmt.Printf("%+v\n", out)
}
func plainExpression(expression string, env *cel.Env, activation interpreter.Activation) interface{} {
ast, issues := env.Parse(expression)
if issues != nil && issues.Err() != nil {
log.Fatalf("CEL parse error: %s\n", issues.Err())
}
program, err := env.Program(ast)
if err != nil {
log.Fatalf("CEL program construction error: %s\n", err)
}
out, _, err := program.Eval(activation)
if err != nil {
log.Fatalf("CEL evaluation error: %v\n", err)
}
return out
}
func yamlExpression(yamlExpression string, env *cel.Env, activation interpreter.Activation) string {
expanded := parameters.CurlyReplacement.ReplaceAllStringFunc(yamlExpression,
func(match string) string {
expression, isCel := parameters.StripCurly(match)
if !isCel {
util.Warn("`%s` is not a CEL substitution", match)
}
result := plainExpression(expression, env, activation)
return fmt.Sprintf("%+v", result)
})
return expanded
}
type verboseActivation struct {
bindings map[string]string
autoVars bool
}
func (a *verboseActivation) ResolveName(name string) (interface{}, bool) {
value, exist := a.bindings[name]
if !exist && a.autoVars {
value = fmt.Sprintf("<%s>", name)
exist = true
}
if config.Debug {
print := "(undefined)"
if exist {
print = fmt.Sprintf("`%s`", value)
}
fmt.Printf("CEL resolving: %s => %s\n", name, print)
}
if !exist {
return nil, false
}
return value, true
}
func (*verboseActivation) Parent() interpreter.Activation {
return nil
}