Files
releases/cmd/parse.go
T
Anders Eknert da69c32819 Add nilness linter from govet (#7335)
Seeing @thevilledev adding this in conftest, and thinking we
had it here already. We didn't! Fixing the two issues it reported
where we checked for conditions that couldn't possibly be met.

Signed-off-by: Anders Eknert <anders@styra.com>
2025-02-01 10:37:53 +01:00

149 lines
3.7 KiB
Go

// Copyright 2018 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package cmd
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/open-policy-agent/opa/cmd/internal/env"
pr "github.com/open-policy-agent/opa/internal/presentation"
"github.com/open-policy-agent/opa/v1/ast"
astJSON "github.com/open-policy-agent/opa/v1/ast/json"
"github.com/open-policy-agent/opa/v1/loader"
"github.com/open-policy-agent/opa/v1/util"
)
const (
parseFormatPretty = "pretty"
parseFormatJSON = "json"
)
type parseParams struct {
format *util.EnumFlag
jsonInclude string
v0Compatible bool
v1Compatible bool
}
func (p *parseParams) regoVersion() ast.RegoVersion {
// the '--v0--compatible' flag takes precedence over the '--v1-compatible' flag
if p.v0Compatible {
return ast.RegoV0
}
if p.v1Compatible {
return ast.RegoV1
}
return ast.DefaultRegoVersion
}
var configuredParseParams = parseParams{
format: util.NewEnumFlag(parseFormatPretty, []string{parseFormatPretty, parseFormatJSON}),
jsonInclude: "",
}
var parseCommand = &cobra.Command{
Use: "parse <path>",
Short: "Parse Rego source file",
Long: `Parse Rego source file and print AST.`,
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("no source file specified")
}
return env.CmdFlags.CheckEnvironmentVariables(cmd)
},
Run: func(_ *cobra.Command, args []string) {
os.Exit(parse(args, &configuredParseParams, os.Stdout, os.Stderr))
},
}
func parse(args []string, params *parseParams, stdout io.Writer, stderr io.Writer) int {
if len(args) == 0 {
return 0
}
exposeLocation := false
exposeComments := true
for _, opt := range strings.Split(params.jsonInclude, ",") {
value := true
if strings.HasPrefix(opt, "-") {
value = false
}
if strings.HasSuffix(opt, "locations") {
exposeLocation = value
}
if strings.HasSuffix(opt, "comments") {
exposeComments = value
}
}
parserOpts := ast.ParserOptions{
ProcessAnnotation: true,
RegoVersion: params.regoVersion(),
}
if exposeLocation {
astJSON.SetOptions(astJSON.Options{
MarshalOptions: astJSON.MarshalOptions{
IncludeLocationText: true,
IncludeLocation: astJSON.NodeToggle{
Term: true,
Package: true,
Comment: true,
Import: true,
Rule: true,
Head: true,
Expr: true,
SomeDecl: true,
Every: true,
With: true,
Annotations: true,
AnnotationsRef: true,
},
},
})
defer astJSON.SetOptions(astJSON.Defaults())
}
result, err := loader.RegoWithOpts(args[0], parserOpts)
if err != nil {
_ = pr.JSON(stderr, pr.Output{Errors: pr.NewOutputErrors(err)})
return 1
}
if !exposeComments {
result.Parsed.Comments = nil
}
switch params.format.String() {
case parseFormatJSON:
bs, err := json.MarshalIndent(result.Parsed, "", " ")
if err != nil {
_ = pr.JSON(stderr, pr.Output{Errors: pr.NewOutputErrors(err)})
return 1
}
_, _ = fmt.Fprint(stdout, string(bs)+"\n")
default:
ast.Pretty(stdout, result.Parsed)
}
return 0
}
func init() {
parseCommand.Flags().VarP(configuredParseParams.format, "format", "f", "set output format")
parseCommand.Flags().StringVarP(&configuredParseParams.jsonInclude, "json-include", "", "", "include or exclude optional elements. By default comments are included. Current options: locations, comments. E.g. --json-include locations,-comments will include locations and exclude comments.")
addV1CompatibleFlag(parseCommand.Flags(), &configuredParseParams.v1Compatible, false)
RootCommand.AddCommand(parseCommand)
}