cmd/cel/main.go (106 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 main
import (
"flag"
"fmt"
"os"
"strings"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/interpreter"
)
var (
verbose bool
autoVars bool
)
func main() {
flag.BoolVar(&verbose, "v", false, "Print CEL internals if set")
flag.BoolVar(&autoVars, "a", true, "Auto-resolve variable into <variable name> if not found in binding")
flag.Usage = func() {
fmt.Fprint(os.Stderr,
`Usage:
cel [-v] [-a] <expression> [bind.some.name=value1,...]
Flags:
`)
flag.PrintDefaults()
}
flag.Parse()
if flag.NArg() != 2 && flag.NArg() != 1 {
flag.Usage()
os.Exit(1)
}
expression := flag.Arg(0)
binding := make(map[string]interface{})
if flag.NArg() == 2 {
var err error
binding, err = parseKvList(flag.Arg(1))
if err != nil {
fmt.Printf("Unable to parse variable binding: %v\n", err)
os.Exit(1)
}
}
env, err := cel.NewEnv()
if err != nil {
fmt.Printf("Unable to init CEL runtime: %v\n", err)
os.Exit(1)
}
ast, issues := env.Parse(expression)
if issues != nil && issues.Err() != nil {
fmt.Printf("CEL parse error: %s\n", issues.Err())
os.Exit(1)
}
program, err := env.Program(ast)
if err != nil {
fmt.Printf("CEL program construction error: %s\n", err)
os.Exit(1)
}
out, _, err := program.Eval(&verboseActivation{binding})
if err != nil {
fmt.Printf("CEL evaluation error: %v\n", err)
os.Exit(1)
}
fmt.Printf("%+v\n", out)
}
func parseKvList(list string) (map[string]interface{}, error) {
parsed := make(map[string]interface{})
if list == "" {
return parsed, nil
}
vars := strings.Split(list, ",")
for _, v := range vars {
kv := strings.SplitN(v, "=", 2)
if len(kv) != 2 {
return nil, fmt.Errorf("`%s` cannot be split into key/value pair", v)
}
parsed[kv[0]] = kv[1]
}
if verbose {
fmt.Print("Parsed vars binding:\n")
for k, v := range parsed {
fmt.Printf("\t%s => %s\n", k, v)
}
}
return parsed, nil
}
type verboseActivation struct {
bindings map[string]interface{}
}
func (a *verboseActivation) ResolveName(name string) (interface{}, bool) {
value, exist := a.bindings[name]
if !exist && autoVars {
value = fmt.Sprintf("<%s>", name)
exist = true
}
if verbose {
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
}