diff --git a/go.mod b/go.mod index 8cd52d3b3533422183a392bd755566f2090087d5..c1c371718e1900db44be42b040fd4d19424ce424 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module git.perx.ru/perxis/perxis-go go 1.21 require ( - github.com/antonmedv/expr v1.9.0 github.com/avast/retry-go/v4 v4.5.1 github.com/bep/gowebp v0.2.0 + github.com/expr-lang/expr v1.15.8 github.com/go-kit/kit v0.13.0 github.com/gosimple/slug v1.13.1 github.com/hashicorp/go-multierror v1.1.1 diff --git a/go.sum b/go.sum index fcb538d7446ef905cc38573b2c88d2496b342ac9..4fa995ca097f0a961d0c4fc9d10cbe640e972c00 100644 --- a/go.sum +++ b/go.sum @@ -2,9 +2,6 @@ cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiV cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/antonmedv/expr v1.9.0 h1:j4HI3NHEdgDnN9p6oI6Ndr0G5QryMY0FNxT4ONrFDGU= -github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmHhwGEk8= github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= github.com/bep/gowebp v0.2.0 h1:ZVfK8i9PpZqKHEmthQSt3qCnnHycbLzBPEsVtk2ch2Q= @@ -12,12 +9,11 @@ github.com/bep/gowebp v0.2.0/go.mod h1:ZhFodwdiFp8ehGJpF4LdPl6unxZm9lLFjxD3z2h2A github.com/brianvoe/gofakeit/v6 v6.26.3 h1:3ljYrjPwsUNAUFdUIr2jVg5EhKdcke/ZLop7uVg1Er8= github.com/brianvoe/gofakeit/v6 v6.26.3/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= +github.com/expr-lang/expr v1.15.8 h1:FL8+d3rSSP4tmK9o+vKfSMqqpGL8n15pEPiHcnBpxoI= +github.com/expr-lang/expr v1.15.8/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ= github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= @@ -64,10 +60,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= -github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -87,25 +79,19 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= -github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -160,8 +146,6 @@ golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -172,7 +156,6 @@ golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -198,7 +181,6 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/environments/environment.go b/pkg/environments/environment.go index cbd468dc361951e048a0840f4158a42c962e7af1..e9d2b96e8337ac2ad94315b671aeea42e54c47cb 100644 --- a/pkg/environments/environment.go +++ b/pkg/environments/environment.go @@ -91,24 +91,4 @@ func (e Environment) Clone() *Environment { } return clone -} - -func (e Environment) Fetch(i interface{}) interface{} { - p, _ := i.(string) - switch p { - case "ID": - return e.ID - case "SpaceID": - return e.SpaceID - case "Description": - return e.Description - case "StateInfo": - return e.StateInfo - case "Aliases": - return e.Aliases - case "Config": - return e.Config - default: - panic("unknown parameter") - } -} +} \ No newline at end of file diff --git a/pkg/expr/config.go b/pkg/expr/config.go index 628111173d92f81ccdcc5e4c40b1aecc2ff27626..e6ba9d90b4f4e04c56c17b8591d4a92e30984e6c 100644 --- a/pkg/expr/config.go +++ b/pkg/expr/config.go @@ -1,8 +1,8 @@ package expr import ( - "github.com/antonmedv/expr" - "github.com/antonmedv/expr/conf" + "github.com/expr-lang/expr" + "github.com/expr-lang/expr/conf" ) type ExprConfig struct { diff --git a/pkg/expr/expr.go b/pkg/expr/expr.go index 93d6ee612968ac0a7e1c963a05cda4955532f110..1969b58c6bec892b05f8eff38eec86f3d4099e01 100644 --- a/pkg/expr/expr.go +++ b/pkg/expr/expr.go @@ -5,9 +5,9 @@ import ( "strings" "git.perx.ru/perxis/perxis-go/pkg/data" - compiler2 "github.com/antonmedv/expr/compiler" - "github.com/antonmedv/expr/parser" - "github.com/antonmedv/expr/vm" + exprcompiler "github.com/expr-lang/expr/compiler" + "github.com/expr-lang/expr/parser" + "github.com/expr-lang/expr/vm" "golang.org/x/net/context" ) @@ -39,7 +39,7 @@ func Eval(ctx context.Context, input string, env map[string]interface{}) (interf env, _ = cfg.Env.(map[string]interface{}) - program, err := compiler2.Compile(tree, nil) + program, err := exprcompiler.Compile(tree, cfg) if err != nil { return nil, err } @@ -78,4 +78,4 @@ func IsExpression(input string) bool { } return false -} +} \ No newline at end of file diff --git a/pkg/expr/expr_test.go b/pkg/expr/expr_test.go index 5eafc368bed934e0bd0805f4818442a75256931c..35153da271daace9f9acb6c7a02411f948f47054 100644 --- a/pkg/expr/expr_test.go +++ b/pkg/expr/expr_test.go @@ -1,11 +1,13 @@ package expr import ( + "context" "fmt" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestIsExpression(t *testing.T) { @@ -49,3 +51,75 @@ func TestIsExpression(t *testing.T) { }) } } + +type testEnvStruct struct { + ID string `expr:"id"` + Size int `expr:"size"` + Data interface{} `expr:"data"` +} + +func (s *testEnvStruct) Equal(other *testEnvStruct) bool { + return s.ID == other.ID +} + +func TestExpr_Example(t *testing.T) { + ctx := context.Background() + + tests := []struct { + name string + exp string + env map[string]interface{} + wantErr bool + wantResult interface{} + }{ + { + name: "get field by expr tag", + exp: "s.id", + env: map[string]interface{}{"s": &testEnvStruct{ID: "id1"}}, + wantResult: "id1", + }, + { + name: "get field by field name", + exp: "s.ID", + env: map[string]interface{}{"s": &testEnvStruct{ID: "id1"}}, + wantResult: "id1", + }, + { + name: "get nested field", + exp: "m.s.size", + env: map[string]interface{}{"m": map[string]interface{}{"s": &testEnvStruct{Size: 1}}}, + wantResult: 1, + }, + { + name: "check field", + exp: "s.data.size < 100", + env: map[string]interface{}{"s": &testEnvStruct{Data: &testEnvStruct{Size: 0}}}, + wantResult: true, + }, + { + name: "use method", + exp: "s1.Equal(s2)", + env: map[string]interface{}{"s1": &testEnvStruct{ID: "id1"}, "s2": &testEnvStruct{ID: "id2"}}, + wantResult: false, + }, + { + name: "field not exists", + exp: "s.not_exists", + env: map[string]interface{}{"s": &testEnvStruct{}}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := Eval(ctx, tt.exp, tt.env) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.wantResult, result) + }) + } +} diff --git a/pkg/expr/mongo.go b/pkg/expr/mongo.go index 989454178f640144791a9e6fd84d39317fd1e283..397043c2fc9e99ad26498b3aecbf87280ac795a1 100644 --- a/pkg/expr/mongo.go +++ b/pkg/expr/mongo.go @@ -6,11 +6,11 @@ import ( "regexp" "strings" - "github.com/antonmedv/expr" - "github.com/antonmedv/expr/ast" - compiler2 "github.com/antonmedv/expr/compiler" - "github.com/antonmedv/expr/conf" - "github.com/antonmedv/expr/parser" + "github.com/expr-lang/expr" + "github.com/expr-lang/expr/ast" + exprcompiler "github.com/expr-lang/expr/compiler" + "github.com/expr-lang/expr/conf" + "github.com/expr-lang/expr/parser" "go.mongodb.org/mongo-driver/bson" ) @@ -76,7 +76,7 @@ func (c *compiler) eval(node ast.Node) interface{} { Node: node, Source: c.tree.Source, } - prg, err := compiler2.Compile(t, c.config) + prg, err := exprcompiler.Compile(t, c.config) if err != nil { panic(fmt.Sprintf("compile error %s", err.Error())) } @@ -107,18 +107,14 @@ func (c *compiler) compile(node ast.Node) interface{} { return c.UnaryNode(n) case *ast.BinaryNode: return c.BinaryNode(n) - case *ast.MatchesNode: - return c.MatchesNode(n) - case *ast.PropertyNode: - return c.PropertyNode(n) - case *ast.IndexNode: - return c.IndexNode(n) + case *ast.MemberNode: + return c.MemberNode(n) + case *ast.ChainNode: + return c.ChainNode(n) case *ast.SliceNode: return c.SliceNode(n) - case *ast.MethodNode: - return c.MethodNode(n) - case *ast.FunctionNode: - return c.FunctionNode(n) + case *ast.CallNode: + return c.CallNode(n) case *ast.BuiltinNode: return c.BuiltinNode(n) case *ast.ClosureNode: @@ -127,6 +123,8 @@ func (c *compiler) compile(node ast.Node) interface{} { return c.PointerNode(n) case *ast.ConditionalNode: return c.ConditionalNode(n) + case *ast.VariableDeclaratorNode: + return c.VariableDeclaratorNode(n) case *ast.ArrayNode: return c.ArrayNode(n) case *ast.MapNode: @@ -208,12 +206,14 @@ func (c *compiler) ConstantNode(node *ast.ConstantNode) interface{} { } func (c *compiler) UnaryNode(node *ast.UnaryNode) interface{} { - op := c.compile(node.Node) - switch node.Operator { - case "!", "not": - return bson.M{"$not": op} + nodeIn, ok := node.Node.(*ast.BinaryNode) + if ok && nodeIn.Operator == "in" { + return bson.M{c.identifier(nodeIn.Left): bson.M{"$nin": c.eval(nodeIn.Right)}} + } + + return bson.M{"$not": c.compile(node.Node)} default: panic(fmt.Sprintf("unknown operator (%v)", node.Operator)) } @@ -221,8 +221,8 @@ func (c *compiler) UnaryNode(node *ast.UnaryNode) interface{} { func (c *compiler) identifier(node ast.Node) string { switch l := node.(type) { - case *ast.PropertyNode: - return c.PropertyNode(l) + case *ast.MemberNode: + return c.MemberNode(l) case *ast.IdentifierNode: return c.IdentifierNode(l) } @@ -324,45 +324,24 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) interface{} { } } -func (c *compiler) MatchesNode(node *ast.MatchesNode) interface{} { - panic("unsupported match node") - //if node.Regexp != nil { - // c.compile(node.Left) - // c.emit(OpMatchesConst, c.makeConstant(node.Regexp)...) - // return - //} - //c.compile(node.Left) - //c.compile(node.Right) - //c.emit(OpMatches) +func (c *compiler) ChainNode(node *ast.ChainNode) string { + panic("unsupported chain node") } -func (c *compiler) PropertyNode(node *ast.PropertyNode) string { +func (c *compiler) MemberNode(node *ast.MemberNode) string { v := c.compile(node.Node) if val, ok := v.(string); ok { - return fmt.Sprintf("%s.%s", val, node.Property) + return fmt.Sprintf("%s.%s", val, c.compile(node.Property)) } panic(fmt.Sprintf("unsupported property for %v", ast.Dump(node.Node))) } -func (c *compiler) IndexNode(node *ast.IndexNode) string { - return fmt.Sprintf("{index-%v}", c.compile(node.Index)) -} - func (c *compiler) SliceNode(node *ast.SliceNode) interface{} { panic("unsupported slice node") } -func (c *compiler) MethodNode(node *ast.MethodNode) interface{} { - panic("unsupported method node") - //c.compile(node.Node) - //for _, arg := range node.Arguments { - // c.compile(arg) - //} - //c.emit(OpMethod, c.makeConstant(Call{Name: node.Method, Size: len(node.Arguments)})...) -} - -func (c *compiler) FunctionNode(node *ast.FunctionNode) interface{} { - switch node.Name { +func (c *compiler) CallNode(node *ast.CallNode) interface{} { + switch node.Callee.String() { case "search", "q": val := c.compile(node.Arguments[0]) return bson.M{"$text": bson.M{"$search": val}} @@ -633,6 +612,10 @@ func (c *compiler) ConditionalNode(node *ast.ConditionalNode) interface{} { //c.patchJump(end) } +func (c *compiler) VariableDeclaratorNode(node *ast.VariableDeclaratorNode) int { + panic("unsupported variable declarator node ") +} + func (c *compiler) ArrayNode(node *ast.ArrayNode) interface{} { panic("unsupported array node") //for _, node := range node.Nodes { diff --git a/pkg/expr/mongo_test.go b/pkg/expr/mongo_test.go index 75ec627c124cc0a24768d3cec30a4eac0ae34c15..dadf30e720ce88cd51591989dd4e0082f3f0883f 100644 --- a/pkg/expr/mongo_test.go +++ b/pkg/expr/mongo_test.go @@ -6,8 +6,8 @@ import ( "time" "git.perx.ru/perxis/perxis-go/pkg/id" - "github.com/antonmedv/expr" - "github.com/antonmedv/expr/ast" + "github.com/expr-lang/expr" + "github.com/expr-lang/expr/ast" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" @@ -29,6 +29,10 @@ func TestConvertToMongo(t *testing.T) { }{ {"equal", "s == 3", nil, bson.M{"s": 3}, false}, {"in array", "s in [1,2,3]", nil, bson.M{"s": bson.M{"$in": []interface{}{1, 2, 3}}}, false}, + {"not in array", "s not in [1,2,3]", nil, bson.M{"s": bson.M{"$nin": []interface{}{1, 2, 3}}}, false}, + {"field#1", "s.test > 3", nil, bson.M{"s.test": bson.M{"$gt": 3}}, false}, + {"field#2", "s['test'] > 3", nil, bson.M{"s.test": bson.M{"$gt": 3}}, false}, + {"field#3", "s[test] > 3", nil, bson.M{"s.test": bson.M{"$gt": 3}}, false}, {"contains", "s contains 'some'", nil, bson.M{"s": bson.M{"$regex": "some"}}, false}, {"contains with . + () $ {} ^", "value contains 'something with . + () $ {} ^'", nil, bson.M{"value": bson.M{"$regex": "something with \\. \\+ \\(\\) \\$ \\{\\} \\^"}}, false}, {"startsWith", "s startsWith 'some'", nil, bson.M{"s": bson.M{"$regex": "^some.*"}}, false}, @@ -51,6 +55,7 @@ func TestConvertToMongo(t *testing.T) { {"in", "In(s, [1,2,3])", nil, bson.M{"s": bson.M{"$in": []interface{}{1, 2, 3}}}, false}, {"in", "In(s, 1)", nil, bson.M{"s": bson.M{"$in": []interface{}{1}}}, false}, {"text search or id", "id", nil, nil, true}, + {"struct env", "db_item.id == env_item.id", map[string]interface{}{"env_item": &testEnvStruct{ID: "id1"}}, bson.M{"db_item.id": "id1"}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -83,8 +88,7 @@ func BenchmarkConvertToMongo(b *testing.B) { type testVisitor struct{} -func (v *testVisitor) Enter(node *ast.Node) {} -func (v *testVisitor) Exit(node *ast.Node) { +func (v *testVisitor) Visit(node *ast.Node) { if n, ok := (*node).(*ast.IdentifierNode); ok { n.Value = "some" + "." + n.Value } diff --git a/pkg/expr/time.go b/pkg/expr/time.go index 740da301a70b65a15f83ac70b1f512c47c171b35..b72292c0c9f4d85bbe42d282f52dcde060cdf8e4 100644 --- a/pkg/expr/time.go +++ b/pkg/expr/time.go @@ -3,7 +3,7 @@ package expr import ( "time" - "github.com/antonmedv/expr" + "github.com/expr-lang/expr" ) const DefaultTimeLayout = time.RFC3339 @@ -18,7 +18,6 @@ func init() { expr.Operator("<", "Time.Before"), expr.Operator(">", "Time.After"), expr.Operator("<=", "Time.BeforeOrEqual"), - expr.Operator(">", "Time.After"), expr.Operator(">=", "Time.AfterOrEqual"), // Time and duration manipulation. diff --git a/pkg/files/file.go b/pkg/files/file.go index 0e4d89f7004413b2c2a0ae9eff663cdcfc0ef3a1..d2236b83368e3d5efc763fd6f7e53664e15cc827 100644 --- a/pkg/files/file.go +++ b/pkg/files/file.go @@ -16,13 +16,13 @@ const ( // File - описание файла РІ системе хранения perxis type File struct { - ID string `mapstructure:"id,omitempty" json:"id"` // Уникальный идентификатор файла РІ хранилище - Name string `mapstructure:"name,omitempty" json:"name" bson:"name,omitempty"` // РРјСЏ файла - Size int `mapstructure:"size,omitempty" json:"size" bson:"size,omitempty"` // Размер файла - MimeType string `mapstructure:"mimeType,omitempty" json:"mimeType" bson:"mimeType,omitempty"` // Mime-type файла - URL string `mapstructure:"url,omitempty" json:"url" bson:"url,omitempty"` // Адрес для загрузки файла - Key string `mapstructure:"key,omitempty" json:"key" bson:"key,omitempty"` // Ключ для хранения файла РІ хранилище - File fs.File `mapstructure:"-" json:"-" bson:"-"` // Файл для загрузки(РёР· файловой системы) + ID string `mapstructure:"id,omitempty" json:"id" expr:"id"` // Уникальный идентификатор файла РІ хранилище + Name string `mapstructure:"name,omitempty" json:"name" bson:"name,omitempty" expr:"name"` // РРјСЏ файла + Size int `mapstructure:"size,omitempty" json:"size" bson:"size,omitempty" expr:"size"` // Размер файла + MimeType string `mapstructure:"mimeType,omitempty" json:"mimeType" bson:"mimeType,omitempty" expr:"mime_type"` // Mime-type файла + URL string `mapstructure:"url,omitempty" json:"url" bson:"url,omitempty" expr:"url"` // Адрес для загрузки файла + Key string `mapstructure:"key,omitempty" json:"key" bson:"key,omitempty" expr:"key"` // Ключ для хранения файла РІ хранилище + File fs.File `mapstructure:"-" json:"-" bson:"-"` // Файл для загрузки(РёР· файловой системы) } func (f File) Clone() *File { @@ -47,26 +47,6 @@ func (f *File) SetURLWithTemplate(t *template.Template) error { return nil } -func (f File) Fetch(i interface{}) interface{} { - p, _ := i.(string) - switch p { - case "id": - return f.ID - case "name": - return f.Name - case "size": - return f.Size - case "mime_type": - return f.MimeType - case "url": - return f.URL - case "key": - return f.Key - default: - panic("unknown parameter") - } -} - func NewFile(name, mimeType string, size int, temp bool) *File { i := id.GenerateNewID() if temp { diff --git a/pkg/files/file_test.go b/pkg/files/file_test.go index 14fb89ce8c054d57ea708a10eb27318b474ff99c..617bed4c09ef95ab58874d856a36c5584abd4589 100644 --- a/pkg/files/file_test.go +++ b/pkg/files/file_test.go @@ -1,9 +1,11 @@ package files import ( + "context" "testing" "text/template" + "git.perx.ru/perxis/perxis-go/pkg/expr" "github.com/stretchr/testify/require" ) @@ -55,3 +57,35 @@ func TestFile_SetURLWithTemplate(t *testing.T) { }) } } + +func TestFile_InExpr(t *testing.T) { + ctx := context.Background() + + tests := []struct { + exp string + env map[string]interface{} + wantResult interface{} + wantErr bool + }{ + {"f.id", map[string]interface{}{"f": &File{ID: "some_id"}}, "some_id", false}, + {"f.name", map[string]interface{}{"f": &File{Name: "some_name"}}, "some_name", false}, + {"f.size", map[string]interface{}{"f": &File{Size: 1}}, 1, false}, + {"f.mime_type", map[string]interface{}{"f": &File{MimeType: "some_mime_type"}}, "some_mime_type", false}, + {"f.url", map[string]interface{}{"f": &File{URL: "some_url"}}, "some_url", false}, + {"f.key", map[string]interface{}{"f": &File{Key: "some_key"}}, "some_key", false}, + {"f.not_exists", map[string]interface{}{"f": &File{}}, "", true}, + } + + for _, tt := range tests { + t.Run(tt.exp, func(t *testing.T) { + result, err := expr.Eval(ctx, tt.exp, tt.env) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.wantResult, result) + }) + } +} diff --git a/pkg/references/reference.go b/pkg/references/reference.go index 5740c929bfde2c0c11abcd96d75ce3122448f50e..171ded420c3fd5601cac9ad9f74ad69883f964ea 100644 --- a/pkg/references/reference.go +++ b/pkg/references/reference.go @@ -7,9 +7,9 @@ import ( ) type Reference struct { - ID string `json:"id" bson:"id" mapstructure:"id"` - CollectionID string `json:"collection_id" bson:"collection_id" mapstructure:"collection_id"` - Disabled bool `json:"disabled,omitempty" bson:"disabled,omitempty" mapstructure:"disabled"` + ID string `json:"id" bson:"id" mapstructure:"id" expr:"id"` + CollectionID string `json:"collection_id" bson:"collection_id" mapstructure:"collection_id" expr:"collection_id"` + Disabled bool `json:"disabled,omitempty" bson:"disabled,omitempty" mapstructure:"disabled" expr:"disabled"` } func (r *Reference) MarshalBSON() ([]byte, error) { @@ -108,17 +108,3 @@ func EqualArrays(sr1, sr2 []*Reference) bool { func (r *Reference) IsValid() bool { return r != nil && r.ID != "" && r.CollectionID != "" && !r.Disabled } - -func (r *Reference) Fetch(i interface{}) interface{} { - p, _ := i.(string) - switch p { - case "id": - return r.ID - case "collection_id": - return r.CollectionID - case "disabled": - return r.Disabled - default: - panic("unknown parameter") - } -} diff --git a/pkg/references/reference_test.go b/pkg/references/reference_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5e79de47ab96cc9e48b8ba0eb644f9519e9ee914 --- /dev/null +++ b/pkg/references/reference_test.go @@ -0,0 +1,41 @@ +package references + +import ( + "context" + "testing" + + "git.perx.ru/perxis/perxis-go/pkg/expr" + "github.com/stretchr/testify/require" +) + +func TestReference_InExpr(t *testing.T) { + ctx := context.Background() + + tests := []struct { + exp string + env map[string]interface{} + wantResult interface{} + wantErr bool + }{ + {"r.id", map[string]interface{}{"r": &Reference{ID: "some_id"}}, "some_id", false}, + {"r.collection_id", map[string]interface{}{"r": &Reference{CollectionID: "some_coll_id"}}, "some_coll_id", false}, + {"r.disabled", map[string]interface{}{"r": &Reference{Disabled: true}}, true, false}, + {"r.String()", map[string]interface{}{"r": &Reference{ID: "id", CollectionID: "collID"}}, "collID.id", false}, + {"r1.Equal(r2)", map[string]interface{}{"r1": &Reference{"id", "collID", false}, "r2": &Reference{"id", "collID", false}}, true, false}, + {"r.IsValid()", map[string]interface{}{"r": &Reference{}}, false, false}, + {"r.not_exists", map[string]interface{}{"r": &Reference{}}, false, true}, + } + + for _, tt := range tests { + t.Run(tt.exp, func(t *testing.T) { + result, err := expr.Eval(ctx, tt.exp, tt.env) + if tt.wantErr { + require.Error(t, err) + return + } + + require.NoError(t, err) + require.Equal(t, tt.wantResult, result) + }) + } +} diff --git a/pkg/spaces/space.go b/pkg/spaces/space.go index aa039c46f95e6c1ec3b0e49b65e766dc7451cd9f..83edb3f14706793aed6077adcee856598358b044 100644 --- a/pkg/spaces/space.go +++ b/pkg/spaces/space.go @@ -42,24 +42,4 @@ type StateInfo struct { func (s Space) Clone() *Space { return &s -} - -func (s Space) Fetch(i interface{}) interface{} { - p, _ := i.(string) - switch p { - case "ID": - return s.ID - case "OrgID": - return s.OrgID - case "Name": - return s.Name - case "Description": - return s.Description - case "Config": - return s.Config - case "StateInfo": - return s.StateInfo - default: - panic("unknown parameter") - } -} +} \ No newline at end of file