Skip to content

Commit

Permalink
switch from govaluate to Expr (--filter <expr> option) (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
jandelgado authored Aug 20, 2024
1 parent a2dc5d3 commit 5f06ad0
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 38 deletions.
4 changes: 2 additions & 2 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ builds:
ignore:
- goos: wasip1
goarch: amd64
- goos: wasipi
- goos: wasip1
goarch: arm
- goos: wasipi
- goos: wasip1
goarch: arm64
- goos: darwin
goarch: "386"
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog for rabtap

## v1.40 (2024-08-20)

* govaluate not being maintained since 2017, we switch to
[Expr](https://expr-lang.org/) for use as the expression-evaluator of the
`--filter <expr>` option. The syntax of `Expr` is similar, but not the same,
so this can be considered a breaking change
* dependency updates

## v1.39.3 (2024-06-22)

* simplify code (fanin) and reduce dependencies
Expand Down
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -787,15 +787,18 @@ a *predicate*). Rabtap allows the specification of predicates to be applied
when printing queues using the `info` command. The output will only proceed
if the predicate evaluates to `true`.

Rabtap uses the [govalute](https://github.com/Knetic/govaluate) to evaluate the
predicate. This allows or complex expressions.
Rabtap uses [Expr](https://expr-lang.org/) to evaluate predicates. This
allows for complex expressions.

See [official govaluate
documentation](https://github.com/Knetic/govaluate/blob/master/MANUAL.md) for
further information.
See the [official expr-lang
documentation](https://expr-lang.org/docs/language-definition) for further
information.

Note: currently the filter is ignored when used in conjunction with
`--by-connection`.
> Note: prior to version 1.40, rabtap used
> [govaluate](https://github.com/Knetic/govaluate) to evaluate expressions.
> With the switch to [Expr](https://expr-lang.org/), the syntax has changed in
> some aspects (e.g. `=~` vs `matches` in regular expression matches). Consult
> the documentation for details.
#### Evaluation context

Expand All @@ -813,9 +816,9 @@ broker to be used, e.g. `http://guest:guest@localhost:15672/api`).

* `rabtap info --filter "exchange.Name == 'amq.direct'" --omit-empty` - print
only queues bound to exchange `amq.direct` and skip all empty exchanges.
* `rabtap info --filter "queue.Name =~ '.*test.*'" --omit-empty` - print all
* `rabtap info --filter "queue.Name matches '.*test.*'" --omit-empty` - print all
queues with `test` in their name.
* `rabtap info --filter "queue.Name =~ '.*test.*' && exchange.Type == 'topic'" --omit-empty` - like
* `rabtap info --filter "queue.Name matches '.*test.*' && exchange.Type == 'topic'" --omit-empty` - like
before, but consider only exchanges of type `topic`.
* `rabtap info --filter "queue.Consumers > 0" --omit --stats --consumers` - print
all queues with at least one consumer
Expand Down
15 changes: 7 additions & 8 deletions cmd/rabtap/predicate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ package main
import (
"errors"

"github.com/Knetic/govaluate"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/vm"
)

// Predicate evaluates an expression to a boolean value
Expand All @@ -25,23 +26,21 @@ func (s truePredicate) Eval(params map[string]interface{}) (bool, error) {
// PredicateExpression implements an predicate expression evaluator using
// the govaluate package
type PredicateExpression struct {
expression *govaluate.EvaluableExpression
prog *vm.Program
}

// NewPredicateExpression creates a new predicate expression
func NewPredicateExpression(exprstr string) (Predicate, error) {
expression, err := govaluate.NewEvaluableExpression(exprstr)
prog, err := expr.Compile(exprstr)
if err != nil {
return nil, err
}
return &PredicateExpression{
expression: expression,
}, nil
return &PredicateExpression{prog: prog}, nil
}

// Eval evaluates the expression with a given set of parameters
func (s PredicateExpression) Eval(params map[string]interface{}) (bool, error) {
result, err := s.expression.Evaluate(params)
func (s PredicateExpression) Eval(env map[string]interface{}) (bool, error) {
result, err := expr.Run(s.prog, env)
if err != nil {
return false, err
}
Expand Down
32 changes: 17 additions & 15 deletions cmd/rabtap/predicate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,60 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestTruePredicate(t *testing.T) {
res, err := TruePredicate.Eval(nil)
assert.Nil(t, err)
require.NoError(t, err)
assert.True(t, res)
}

func TestPredicateTrue(t *testing.T) {
f, err := NewPredicateExpression("1 == 1")
assert.Nil(t, err)
require.NoError(t, err)
params := map[string]interface{}{}
res, err := f.Eval(params)
assert.Nil(t, err)
require.NoError(t, err)
assert.True(t, res)
}

func TestPredicateFalse(t *testing.T) {
f, err := NewPredicateExpression("1 == 0")
assert.Nil(t, err)
require.NoError(t, err)
params := map[string]interface{}{}
res, err := f.Eval(params)
assert.Nil(t, err)
require.NoError(t, err)
assert.False(t, res)
}

func TestPredicateWithParams(t *testing.T) {
f, err := NewPredicateExpression("a == 1337 && b.X == 42")
assert.Nil(t, err)
func TestPredicateWithEnv(t *testing.T) {
f, err := NewPredicateExpression(`a == 1337 && b.X == 42 && c == "JD"`)
require.NoError(t, err)
params := make(map[string]interface{}, 1)
params["a"] = 1337
params["b"] = struct{ X int }{X: 42}
params["c"] = "JD"
res, err := f.Eval(params)
assert.Nil(t, err)
require.NoError(t, err)
assert.True(t, res)
}

func TestPredicateReturnsErrorOnInvalidSyntax(t *testing.T) {
_, err := NewPredicateExpression("invalid syntax")
assert.NotNil(t, err)
_, err := NewPredicateExpression(")invalid syntax(")
assert.ErrorContains(t, err, "unexpected token")
}

func TestPredicateReturnsErrorOnEvalError(t *testing.T) {
f, err := NewPredicateExpression("(1/a) == 1")
assert.Nil(t, err)
require.NoError(t, err)
_, err = f.Eval(nil)
assert.NotNil(t, err)
assert.ErrorContains(t, err, "invalid operation")
}
func TestPredicateReturnsErrorOnNonBoolReturnValue(t *testing.T) {
f, err := NewPredicateExpression("1+1")
assert.Nil(t, err)
require.NoError(t, err)
params := map[string]interface{}{}
_, err = f.Eval(params)
assert.NotNil(t, err)
assert.ErrorContains(t, err, "expression does not evaluate to bool")
}
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/jandelgado/rabtap
go 1.18

require (
github.com/Knetic/govaluate v0.0.0-20171022003610-9aa49832a739 //v0.0.0-20171022003610-9aa49832a739
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
github.com/fatih/color v1.17.0
github.com/google/uuid v1.6.0
Expand All @@ -15,7 +14,10 @@ require (
golang.org/x/sync v0.8.0
)

require github.com/stealthrocket/net v0.2.1
require (
github.com/expr-lang/expr v1.16.9
github.com/stealthrocket/net v0.2.1
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
github.com/Knetic/govaluate v0.0.0-20171022003610-9aa49832a739 h1:g5ROzGMXE/5BOW8kbvOZm+zH6Q+yVUaQBl3vwgLeUqY=
github.com/Knetic/govaluate v0.0.0-20171022003610-9aa49832a739/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
Expand Down

0 comments on commit 5f06ad0

Please sign in to comment.