Skip to content

Commit

Permalink
Ensure variables are propagated and update root command
Browse files Browse the repository at this point in the history
  • Loading branch information
TomWright committed Oct 19, 2024
1 parent 9999c1b commit 8bbae1e
Show file tree
Hide file tree
Showing 14 changed files with 168 additions and 68 deletions.
3 changes: 2 additions & 1 deletion api.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import (

// Query queries the data using the selector and returns the results.
func Query(data any, selector string, opts ...execution.ExecuteOptionFn) ([]*model.Value, int, error) {
options := execution.NewOptions(opts...)
val := model.NewValue(data)
out, err := execution.ExecuteSelector(selector, val, opts...)
out, err := execution.ExecuteSelector(selector, val, options)
if err != nil {
return nil, 0, err
}
Expand Down
1 change: 1 addition & 0 deletions bar.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"x":1}
44 changes: 23 additions & 21 deletions execution/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

// ExecuteSelector parses the selector and executes the resulting AST with the given input.
func ExecuteSelector(selectorStr string, value *model.Value, opts ...ExecuteOptionFn) (*model.Value, error) {
func ExecuteSelector(selectorStr string, value *model.Value, opts *Options) (*model.Value, error) {
if selectorStr == "" {
return value, nil
}
Expand All @@ -19,7 +19,7 @@ func ExecuteSelector(selectorStr string, value *model.Value, opts ...ExecuteOpti
return nil, fmt.Errorf("error parsing selector: %w", err)
}

res, err := ExecuteAST(expr, value, opts...)
res, err := ExecuteAST(expr, value, opts)
if err != nil {
return nil, fmt.Errorf("error executing selector: %w", err)
}
Expand All @@ -30,9 +30,7 @@ func ExecuteSelector(selectorStr string, value *model.Value, opts ...ExecuteOpti
type expressionExecutor func(data *model.Value) (*model.Value, error)

// ExecuteAST executes the given AST with the given input.
func ExecuteAST(expr ast.Expr, value *model.Value, opts ...ExecuteOptionFn) (*model.Value, error) {
options := NewOptions(opts...)

func ExecuteAST(expr ast.Expr, value *model.Value, options *Options) (*model.Value, error) {
if expr == nil {
return value, nil
}
Expand Down Expand Up @@ -69,21 +67,21 @@ func ExecuteAST(expr ast.Expr, value *model.Value, opts ...ExecuteOptionFn) (*mo
func exprExecutor(opts *Options, expr ast.Expr) (expressionExecutor, error) {
switch e := expr.(type) {
case ast.BinaryExpr:
return binaryExprExecutor(e)
return binaryExprExecutor(opts, e)
case ast.CallExpr:
return callExprExecutor(opts, e)
case ast.ChainedExpr:
return chainedExprExecutor(e)
return chainedExprExecutor(opts, e)
case ast.SpreadExpr:
return spreadExprExecutor()
case ast.RangeExpr:
return rangeExprExecutor(e)
return rangeExprExecutor(opts, e)
case ast.IndexExpr:
return indexExprExecutor(e)
return indexExprExecutor(opts, e)
case ast.PropertyExpr:
return propertyExprExecutor(e)
return propertyExprExecutor(opts, e)
case ast.VariableExpr:
return variableExprExecutor(e)
return variableExprExecutor(opts, e)
case ast.NumberIntExpr:
return numberIntExprExecutor(e)
case ast.NumberFloatExpr:
Expand All @@ -93,17 +91,17 @@ func exprExecutor(opts *Options, expr ast.Expr) (expressionExecutor, error) {
case ast.BoolExpr:
return boolExprExecutor(e)
case ast.ObjectExpr:
return objectExprExecutor(e)
return objectExprExecutor(opts, e)
case ast.MapExpr:
return mapExprExecutor(e)
return mapExprExecutor(opts, e)
case ast.FilterExpr:
return filterExprExecutor(e)
return filterExprExecutor(opts, e)
case ast.ConditionalExpr:
return conditionalExprExecutor(e)
return conditionalExprExecutor(opts, e)
case ast.BranchExpr:
return branchExprExecutor(e)
return branchExprExecutor(opts, e)
case ast.ArrayExpr:
return arrayExprExecutor(e)
return arrayExprExecutor(opts, e)
case ast.RegexExpr:
// Noop
return func(data *model.Value) (*model.Value, error) {
Expand All @@ -114,10 +112,10 @@ func exprExecutor(opts *Options, expr ast.Expr) (expressionExecutor, error) {
}
}

func chainedExprExecutor(e ast.ChainedExpr) (expressionExecutor, error) {
func chainedExprExecutor(options *Options, e ast.ChainedExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
for _, expr := range e.Exprs {
res, err := ExecuteAST(expr, data)
res, err := ExecuteAST(expr, data, options)
if err != nil {
return nil, fmt.Errorf("error executing expression: %w", err)
}
Expand All @@ -127,12 +125,16 @@ func chainedExprExecutor(e ast.ChainedExpr) (expressionExecutor, error) {
}, nil
}

func variableExprExecutor(e ast.VariableExpr) (expressionExecutor, error) {
func variableExprExecutor(opts *Options, e ast.VariableExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
varName := e.Name
if varName == "this" {
return data, nil
}
return nil, fmt.Errorf("variable %s not found", varName)
res, ok := opts.Vars[varName]
if !ok {
return nil, fmt.Errorf("variable %s not found", varName)
}
return res, nil
}, nil
}
14 changes: 7 additions & 7 deletions execution/execute_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import (
"github.com/tomwright/dasel/v3/selector/ast"
)

func arrayExprExecutor(e ast.ArrayExpr) (expressionExecutor, error) {
func arrayExprExecutor(opts *Options, e ast.ArrayExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
res := model.NewSliceValue()

for _, expr := range e.Exprs {
el, err := ExecuteAST(expr, data)
el, err := ExecuteAST(expr, data, opts)
if err != nil {
return nil, err
}
Expand All @@ -25,11 +25,11 @@ func arrayExprExecutor(e ast.ArrayExpr) (expressionExecutor, error) {
}, nil
}

func rangeExprExecutor(e ast.RangeExpr) (expressionExecutor, error) {
func rangeExprExecutor(opts *Options, e ast.RangeExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
var start, end int64 = -1, -1
if e.Start != nil {
startE, err := ExecuteAST(e.Start, data)
startE, err := ExecuteAST(e.Start, data, opts)
if err != nil {
return nil, fmt.Errorf("error evaluating start expression: %w", err)
}
Expand All @@ -41,7 +41,7 @@ func rangeExprExecutor(e ast.RangeExpr) (expressionExecutor, error) {
}

if e.End != nil {
endE, err := ExecuteAST(e.End, data)
endE, err := ExecuteAST(e.End, data, opts)
if err != nil {
return nil, fmt.Errorf("error evaluating end expression: %w", err)
}
Expand All @@ -61,9 +61,9 @@ func rangeExprExecutor(e ast.RangeExpr) (expressionExecutor, error) {
}, nil
}

func indexExprExecutor(e ast.IndexExpr) (expressionExecutor, error) {
func indexExprExecutor(opts *Options, e ast.IndexExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
indexE, err := ExecuteAST(e.Index, data)
indexE, err := ExecuteAST(e.Index, data, opts)
if err != nil {
return nil, fmt.Errorf("error evaluating index expression: %w", err)
}
Expand Down
6 changes: 3 additions & 3 deletions execution/execute_binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import (
"github.com/tomwright/dasel/v3/selector/lexer"
)

func binaryExprExecutor(e ast.BinaryExpr) (expressionExecutor, error) {
func binaryExprExecutor(opts *Options, e ast.BinaryExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
left, err := ExecuteAST(e.Left, data)
left, err := ExecuteAST(e.Left, data, opts)
if err != nil {
return nil, fmt.Errorf("error evaluating left expression: %w", err)
}
right, err := ExecuteAST(e.Right, data)
right, err := ExecuteAST(e.Right, data, opts)
if err != nil {
return nil, fmt.Errorf("error evaluating right expression: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions execution/execute_branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import (
"github.com/tomwright/dasel/v3/selector/ast"
)

func branchExprExecutor(e ast.BranchExpr) (expressionExecutor, error) {
func branchExprExecutor(opts *Options, e ast.BranchExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
res := model.NewSliceValue()

for _, expr := range e.Exprs {
r, err := ExecuteAST(expr, data)
r, err := ExecuteAST(expr, data, opts)
if err != nil {
return nil, fmt.Errorf("failed to execute branch expr: %w", err)
}
Expand Down
8 changes: 4 additions & 4 deletions execution/execute_conditional.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"github.com/tomwright/dasel/v3/selector/ast"
)

func conditionalExprExecutor(e ast.ConditionalExpr) (expressionExecutor, error) {
func conditionalExprExecutor(opts *Options, e ast.ConditionalExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
cond, err := ExecuteAST(e.Cond, data)
cond, err := ExecuteAST(e.Cond, data, opts)
if err != nil {
return nil, fmt.Errorf("error evaluating condition: %w", err)
}
Expand All @@ -20,15 +20,15 @@ func conditionalExprExecutor(e ast.ConditionalExpr) (expressionExecutor, error)
}

if condBool {
res, err := ExecuteAST(e.Then, data)
res, err := ExecuteAST(e.Then, data, opts)
if err != nil {
return nil, fmt.Errorf("error executing then block: %w", err)
}
return res, nil
}

if e.Else != nil {
res, err := ExecuteAST(e.Else, data)
res, err := ExecuteAST(e.Else, data, opts)
if err != nil {
return nil, fmt.Errorf("error executing else block: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions execution/execute_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import (
"github.com/tomwright/dasel/v3/selector/ast"
)

func filterExprExecutor(e ast.FilterExpr) (expressionExecutor, error) {
func filterExprExecutor(opts *Options, e ast.FilterExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
if !data.IsSlice() {
return nil, fmt.Errorf("cannot filter over non-array")
}
res := model.NewSliceValue()

if err := data.RangeSlice(func(i int, item *model.Value) error {
v, err := ExecuteAST(e.Expr, item)
v, err := ExecuteAST(e.Expr, item, opts)
if err != nil {
return err
}
Expand Down
10 changes: 5 additions & 5 deletions execution/execute_func.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (
"github.com/tomwright/dasel/v3/selector/ast"
)

func prepareArgs(data *model.Value, argsE ast.Expressions) (model.Values, error) {
func prepareArgs(opts *Options, data *model.Value, argsE ast.Expressions) (model.Values, error) {
args := make(model.Values, 0)
for i, arg := range argsE {
res, err := ExecuteAST(arg, data)
res, err := ExecuteAST(arg, data, opts)
if err != nil {
return nil, fmt.Errorf("error evaluating argument %d: %w", i, err)
}
Expand All @@ -25,9 +25,9 @@ func prepareArgs(data *model.Value, argsE ast.Expressions) (model.Values, error)
return args, nil
}

func callFnExecutor(f FuncFn, argsE ast.Expressions) (expressionExecutor, error) {
func callFnExecutor(opts *Options, f FuncFn, argsE ast.Expressions) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
args, err := prepareArgs(data, argsE)
args, err := prepareArgs(opts, data, argsE)
if err != nil {
return nil, fmt.Errorf("error preparing arguments: %w", err)
}
Expand All @@ -43,7 +43,7 @@ func callFnExecutor(f FuncFn, argsE ast.Expressions) (expressionExecutor, error)

func callExprExecutor(opts *Options, e ast.CallExpr) (expressionExecutor, error) {
if f, ok := opts.Funcs.Get(e.Function); ok {
res, err := callFnExecutor(f, e.Args)
res, err := callFnExecutor(opts, f, e.Args)
if err != nil {
return nil, fmt.Errorf("error executing function %q: %w", e.Function, err)
}
Expand Down
4 changes: 2 additions & 2 deletions execution/execute_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import (
"github.com/tomwright/dasel/v3/selector/ast"
)

func mapExprExecutor(e ast.MapExpr) (expressionExecutor, error) {
func mapExprExecutor(opts *Options, e ast.MapExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
if !data.IsSlice() {
return nil, fmt.Errorf("cannot map over non-array")
}
res := model.NewSliceValue()

if err := data.RangeSlice(func(i int, item *model.Value) error {
item, err := ExecuteAST(e.Expr, item)
item, err := ExecuteAST(e.Expr, item, opts)
if err != nil {
return err
}
Expand Down
14 changes: 7 additions & 7 deletions execution/execute_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/tomwright/dasel/v3/selector/ast"
)

func objectExprExecutor(e ast.ObjectExpr) (expressionExecutor, error) {
func objectExprExecutor(opts *Options, e ast.ObjectExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
obj := model.NewMapValue()
for _, p := range e.Pairs {
Expand All @@ -17,9 +17,9 @@ func objectExprExecutor(e ast.ObjectExpr) (expressionExecutor, error) {
var err error
if p.Value != nil {
// We need to spread the resulting value.
val, err = ExecuteAST(p.Value, data)
val, err = ExecuteAST(p.Value, data, opts)
if err != nil {
return nil, fmt.Errorf("error evaluated spread values")
return nil, fmt.Errorf("error evaluating spread values: %w", err)
}
} else {
val = data
Expand Down Expand Up @@ -52,14 +52,14 @@ func objectExprExecutor(e ast.ObjectExpr) (expressionExecutor, error) {
// return nil, fmt.Errorf("cannot spread object key name")
//}

key, err := ExecuteAST(p.Key, data)
key, err := ExecuteAST(p.Key, data, opts)
if err != nil {
return nil, fmt.Errorf("error evaluating key: %w", err)
}
if !key.IsString() {
return nil, fmt.Errorf("expected key to resolve to string, got %s", key.Type())
}
val, err := ExecuteAST(p.Value, data)
val, err := ExecuteAST(p.Value, data, opts)
if err != nil {
return nil, fmt.Errorf("error evaluating value: %w", err)
}
Expand All @@ -72,9 +72,9 @@ func objectExprExecutor(e ast.ObjectExpr) (expressionExecutor, error) {
}, nil
}

func propertyExprExecutor(e ast.PropertyExpr) (expressionExecutor, error) {
func propertyExprExecutor(opts *Options, e ast.PropertyExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
key, err := ExecuteAST(e.Property, data)
key, err := ExecuteAST(e.Property, data, opts)
if err != nil {
return nil, fmt.Errorf("error evaluating property: %w", err)
}
Expand Down
11 changes: 11 additions & 0 deletions execution/options.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package execution

import "github.com/tomwright/dasel/v3/model"

// ExecuteOptionFn is a function that can be used to set options on the execution of the selector.
type ExecuteOptionFn func(*Options)

// Options contains the options for the execution of the selector.
type Options struct {
Funcs FuncCollection
Vars map[string]*model.Value
}

// NewOptions creates a new Options struct with the given options.
func NewOptions(opts ...ExecuteOptionFn) *Options {
o := &Options{
Funcs: DefaultFuncCollection,
Vars: map[string]*model.Value{},
}
for _, opt := range opts {
if opt == nil {
Expand All @@ -28,3 +32,10 @@ func WithFuncs(fc FuncCollection) ExecuteOptionFn {
o.Funcs = fc
}
}

// WithVariable sets a variable for use in the selector.
func WithVariable(key string, val *model.Value) ExecuteOptionFn {
return func(o *Options) {
o.Vars[key] = val
}
}
Loading

0 comments on commit 8bbae1e

Please sign in to comment.