in mito.go [60:259]
func Main() int {
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), `Usage of %s:
%[1]s [opts] <src.cel>
`, os.Args[0])
flag.PrintDefaults()
}
use := flag.String("use", "all", "libraries to use")
data := flag.String("data", "", "path to a JSON object holding input (exposed as the label "+root+")")
maxExecutions := flag.Int("max_executions", -1, "maximum number of evaluations, or no maximum if -1")
cfgPath := flag.String("cfg", "", "path to a YAML file holding run control configuration (see pkg.go.dev/github.com/elastic/mito/cmd/mito)")
insecure := flag.Bool("insecure", false, "disable TLS verification in the HTTP client")
logTrace := flag.Bool("log_requests", false, "log request traces to stderr (go1.21+)")
maxTraceBody := flag.Int("max_log_body", 1000, "maximum length of body logged in request traces (go1.21+)")
fold := flag.Bool("fold", false, "apply constant folding optimisation")
dumpState := flag.String("dump", "", "dump eval state ('always' or 'error')")
coverage := flag.String("coverage", "", "file to write an execution coverage report to (prefix if multiple executions are run)")
version := flag.Bool("version", false, "print version and exit")
flag.Parse()
if *version {
return printVersion()
}
if len(flag.Args()) != 1 {
flag.Usage()
return 2
}
libs := []cel.EnvOption{
cel.OptionalTypes(cel.OptionalTypesVersion(lib.OptionalTypesVersion)),
}
if *cfgPath != "" {
f, err := os.Open(*cfgPath)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 2
}
defer f.Close()
dec := yaml.NewDecoder(f)
var cfg Config
err = dec.Decode(&cfg)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 2
}
if len(cfg.Globals) != 0 {
libs = append(libs, lib.Globals(cfg.Globals))
}
if len(cfg.Regexps) != 0 {
regexps := make(map[string]*regexp.Regexp)
for name, expr := range cfg.Regexps {
re, err := regexp.Compile(expr)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 2
}
regexps[name] = re
}
libs = append(libs, lib.Regexp(regexps))
}
if len(cfg.XSDs) != 0 {
xsds := make(map[string]string)
for name, path := range cfg.XSDs {
b, err := os.ReadFile(path)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 2
}
xsds[name] = string(b)
}
xml, err := lib.XML(nil, xsds)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 2
}
libMap["xml"] = xml
}
var client *http.Client
httpOptions := lib.HTTPOptions{
Headers: cfg.HTTPHeaders,
MaxBodySize: cfg.MaxBodySize,
}
if cfg.Auth != nil {
switch auth := cfg.Auth; {
case auth.Basic != nil && auth.OAuth2 != nil:
fmt.Fprintln(os.Stderr, "configured basic authentication and OAuth2")
return 2
case auth.Basic != nil:
httpOptions.BasicAuth = auth.Basic
case auth.OAuth2 != nil:
client, err = oAuth2Client(*auth.OAuth2)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 2
}
}
}
if client != nil || !httpOptions.IsZero() {
ctx := context.Background()
libMap["http"] = lib.HTTPWithContextOpts(ctx, traceReqs(setClientInsecure(client, *insecure), *logTrace, *maxTraceBody), httpOptions)
}
if *maxExecutions == -1 && cfg.MaxExecutions != nil {
*maxExecutions = *cfg.MaxExecutions
}
}
if libMap["http"] == nil {
libMap["http"] = lib.HTTP(traceReqs(setClientInsecure(nil, *insecure), *logTrace, *maxTraceBody), nil, nil)
}
if libMap["xml"] == nil {
var err error
libMap["xml"], err = lib.XML(nil, nil)
if err != nil {
return 2
}
}
if *use == "all" {
for _, l := range libMap {
libs = append(libs, l)
}
} else {
for _, u := range strings.Split(*use, ",") {
l, ok := libMap[u]
if !ok {
fmt.Fprintf(os.Stderr, "no lib %q\n", u)
return 2
}
libs = append(libs, l)
}
}
b, err := os.ReadFile(flag.Args()[0])
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 2
}
var input interface{}
if *data != "" {
b, err := os.ReadFile(*data)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 2
}
err = json.Unmarshal(b, &input)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return 2
}
input = map[string]interface{}{root: input}
}
var cov lib.Coverage
for n := int(0); *maxExecutions < 0 || n < *maxExecutions; n++ {
res, val, dump, c, err := eval(string(b), root, input, *fold, *dumpState != "", *coverage != "", libs...)
if err := cov.Merge(c); err != nil {
fmt.Fprintf(os.Stderr, "internal error merging coverage: %v\n", err)
return 1
}
if *dumpState == "always" {
fmt.Fprint(os.Stderr, dump)
}
if err != nil {
if *dumpState == "error" {
fmt.Fprint(os.Stderr, dump)
}
fmt.Fprintln(os.Stderr, err)
return 2
}
fmt.Println(res)
// Check if we want more. This can happen when we have a map
// and the map has a true boolean field, want_more.
state, ok := val.(map[string]any)
if !ok {
break
}
if more, _ := state["want_more"].(bool); !more {
break
}
input = map[string]any{"state": val}
}
if *coverage != "" {
f, err := os.Create(*coverage)
if err != nil {
fmt.Fprintf(os.Stderr, "internal error opening coverage file: %v\n", err)
return 1
}
defer func() {
f.Sync()
f.Close()
}()
_, err = f.WriteString(cov.String() + "\n")
if err != nil {
fmt.Fprintf(os.Stderr, "internal error writing coverage file: %v\n", err)
return 1
}
}
return 0
}