Skip to content

Commit

Permalink
Add ToExpressionSet helper function. (goadesign#2601)
Browse files Browse the repository at this point in the history
The function takes an arbitrary slice of expressions and returns an ExpressionSet.
This is especially convenient for plugins that do not have to loop through all
the slice to cast the expression objects to the Expression interface. Yes it does
make use of reflect and hides the performance impact but given the use case (tool
vs. long running process) this seems like a good trade-off.
  • Loading branch information
raphael committed Jul 2, 2020
1 parent 86e0281 commit 695a32c
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 11 deletions.
20 changes: 20 additions & 0 deletions eval/expression.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package eval

import "reflect"

type (
// Expression built by the engine through the DSL functions.
Expression interface {
Expand Down Expand Up @@ -95,3 +97,21 @@ func (f DSLFunc) DSL() func() {

// EvalName is the name is the qualified name of the expression.
func (t TopExpr) EvalName() string { return string(t) }

// ToExpressionSet is a convenience function that accepts a slice of expressions
// and builds the corresponding ExpressionSet.
func ToExpressionSet(slice interface{}) ExpressionSet {
if slice == nil {
return nil
}
s := reflect.ValueOf(slice)
if s.Kind() != reflect.Slice {
panic("ToExpressionSet() given a non-slice type") // bug
}
ret := make(ExpressionSet, s.Len())
for i := 0; i < s.Len(); i++ {
ret[i] = s.Index(i).Interface().(Expression)
}

return ret
}
40 changes: 40 additions & 0 deletions eval/expression_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package eval

import (
"testing"
)

type Expr int

func (e Expr) EvalName() string { return "test expression" }

func TestToExpressionSet(t *testing.T) {
cases := []struct {
Name string
Slice []interface{}
ExpectPanic bool
}{
{"simple", []interface{}{Expr(42)}, false},
{"nil", nil, false},
{"invalid", []interface{}{42}, true},
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
defer func() {
if r := recover(); r == nil && c.ExpectPanic {
t.Errorf("test did not panic")
}
}()
set := ToExpressionSet(c.Slice)
if len(set) != len(c.Slice) {
t.Errorf("got set of length %d, expected %d.", len(set), len(c.Slice))
} else {
for i, e := range set {
if e != c.Slice[i] {
t.Errorf("got value %v at index %d, expected %v", e, i, c.Slice[i])
}
}
}
})
}
}
14 changes: 3 additions & 11 deletions expr/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,7 @@ func (r *RootExpr) WalkSets(walk eval.SetWalker) {
walk(eval.ExpressionSet{r.API})

// Servers
servers := make(eval.ExpressionSet, len(r.API.Servers))
for i, s := range r.API.Servers {
servers[i] = s
}
walk(servers)
walk(eval.ToExpressionSet(r.API.Servers))

// User types
types := make(eval.ExpressionSet, len(r.Types))
Expand All @@ -84,14 +80,10 @@ func (r *RootExpr) WalkSets(walk eval.SetWalker) {
walk(mtypes)

// Services
services := make(eval.ExpressionSet, len(r.Services))
var methods eval.ExpressionSet
for i, s := range r.Services {
services[i] = s
}
walk(services)
walk(eval.ToExpressionSet(r.Services))

// Methods (must be done after services)
var methods eval.ExpressionSet
for _, s := range r.Services {
for _, m := range s.Methods {
methods = append(methods, m)
Expand Down

0 comments on commit 695a32c

Please sign in to comment.