mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 23:40:35 +01:00
make macros even faster and smart catch common :string and do not execute anything at all if not really needed, more clean code as well
Former-commit-id: 589c23d1f92cf36b7677dfe78b60d51252c979fb
This commit is contained in:
parent
972dff8729
commit
bf880033cd
|
@ -65,6 +65,24 @@ func main() {
|
||||||
return fmt.Sprintf("Value of the parameters are: %s:%d\n", myparam1, myparam2)
|
return fmt.Sprintf("Value of the parameters are: %s:%d\n", myparam1, myparam2)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
app.Get("/test_string/{myparam1}/{myparam2 prefix(a)}", func(ctx context.Context) {
|
||||||
|
var (
|
||||||
|
myparam1 = ctx.Params().Get("myparam1")
|
||||||
|
myparam2 = ctx.Params().Get("myparam2")
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.Writef("myparam1: %s | myparam2: %s", myparam1, myparam2)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Get("/test_string2/{myparam1}/{myparam2}", func(ctx context.Context) {
|
||||||
|
var (
|
||||||
|
myparam1 = ctx.Params().Get("myparam1")
|
||||||
|
myparam2 = ctx.Params().Get("myparam2")
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.Writef("myparam1: %s | myparam2: %s", myparam1, myparam2)
|
||||||
|
})
|
||||||
|
|
||||||
app.Get("test_uint64/{myparam1:string}/{myparam2:uint64}", func(ctx context.Context) {
|
app.Get("test_uint64/{myparam1:string}/{myparam2:uint64}", func(ctx context.Context) {
|
||||||
// works: ctx.Writef("Value of the parameter is: %s\n", ctx.Params().Get("myparam"))
|
// works: ctx.Writef("Value of the parameter is: %s\n", ctx.Params().Get("myparam"))
|
||||||
// but better and faster because the macro converts the string to uint64 automatically:
|
// but better and faster because the macro converts the string to uint64 automatically:
|
||||||
|
|
|
@ -2,8 +2,6 @@ package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kataras/iris/context"
|
"github.com/kataras/iris/context"
|
||||||
|
@ -61,21 +59,20 @@ func convertTmplToNodePath(tmpl *macro.Template) (string, error) {
|
||||||
|
|
||||||
// Note: returns nil if not needed, the caller(router) should check for that before adding that on route's Middleware.
|
// Note: returns nil if not needed, the caller(router) should check for that before adding that on route's Middleware.
|
||||||
func convertTmplToHandler(tmpl *macro.Template) context.Handler {
|
func convertTmplToHandler(tmpl *macro.Template) context.Handler {
|
||||||
|
|
||||||
needMacroHandler := false
|
|
||||||
|
|
||||||
// check if we have params like: {name:string} or {name} or {anything:path} without else keyword or any functions used inside these params.
|
// check if we have params like: {name:string} or {name} or {anything:path} without else keyword or any functions used inside these params.
|
||||||
// 1. if we don't have, then we don't need to add a handler before the main route's handler (as I said, no performance if macro is not really used)
|
// 1. if we don't have, then we don't need to add a handler before the main route's handler (as I said, no performance if macro is not really used)
|
||||||
// 2. if we don't have any named params then we don't need a handler too.
|
// 2. if we don't have any named params then we don't need a handler too.
|
||||||
|
|
||||||
|
needsMacroHandler := false
|
||||||
for _, p := range tmpl.Params {
|
for _, p := range tmpl.Params {
|
||||||
if len(p.Funcs) == 0 && (ast.IsMaster(p.Type) || ast.IsTrailing(p.Type)) && p.ErrCode == http.StatusNotFound {
|
if p.CanEval() {
|
||||||
} else {
|
// if at least one needs it, then create the handler.
|
||||||
// println("we need handler for: " + tmpl.Src)
|
needsMacroHandler = true
|
||||||
needMacroHandler = true
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !needMacroHandler {
|
if !needsMacroHandler {
|
||||||
// println("we don't need handler for: " + tmpl.Src)
|
// println("we don't need handler for: " + tmpl.Src)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -83,38 +80,19 @@ func convertTmplToHandler(tmpl *macro.Template) context.Handler {
|
||||||
return func(tmpl macro.Template) context.Handler {
|
return func(tmpl macro.Template) context.Handler {
|
||||||
return func(ctx context.Context) {
|
return func(ctx context.Context) {
|
||||||
for _, p := range tmpl.Params {
|
for _, p := range tmpl.Params {
|
||||||
if p.TypeEvaluator == nil {
|
if !p.CanEval() {
|
||||||
// allow.
|
// println(p.Src + " no need to evaluate anything")
|
||||||
continue
|
continue // allow.
|
||||||
}
|
}
|
||||||
|
|
||||||
// first, check for type evaluator.
|
if !p.Eval(ctx.Params().Get(p.Name), ctx.Params().Set) {
|
||||||
newValue, passed := p.TypeEvaluator(ctx.Params().Get(p.Name))
|
|
||||||
if !passed {
|
|
||||||
ctx.StatusCode(p.ErrCode)
|
ctx.StatusCode(p.ErrCode)
|
||||||
ctx.StopExecution()
|
ctx.StopExecution()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(p.Funcs) > 0 {
|
|
||||||
paramIn := []reflect.Value{reflect.ValueOf(newValue)}
|
|
||||||
// then check for all of its functions
|
|
||||||
for _, evalFunc := range p.Funcs {
|
|
||||||
// or make it as func(interface{}) bool and pass directly the "newValue"
|
|
||||||
// but that would not be as easy for end-developer, so keep that "slower":
|
|
||||||
if !evalFunc.Call(paramIn)[0].Interface().(bool) { // i.e func(paramValue int) bool
|
|
||||||
ctx.StatusCode(p.ErrCode)
|
|
||||||
ctx.StopExecution()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Params().Store.Set(p.Name, newValue)
|
|
||||||
}
|
}
|
||||||
// if all passed, just continue.
|
// if all passed, just continue.
|
||||||
ctx.Next()
|
ctx.Next()
|
||||||
}
|
}
|
||||||
}(*tmpl)
|
}(*tmpl)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
var (
|
var (
|
||||||
// String type
|
// String type
|
||||||
// Allows anything (single path segment, as everything except the `Path`).
|
// Allows anything (single path segment, as everything except the `Path`).
|
||||||
String = NewMacro("string", "", true, false, nil). // if nil allows everything.
|
String = NewMacro("string", "", true, false, nil).
|
||||||
RegisterFunc("regexp", func(expr string) func(string) bool {
|
RegisterFunc("regexp", func(expr string) func(string) bool {
|
||||||
return MustRegexp(expr)
|
return MustRegexp(expr)
|
||||||
}).
|
}).
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package macro
|
package macro
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/kataras/iris/core/memstore"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
||||||
|
@ -31,6 +32,61 @@ type TemplateParam struct {
|
||||||
ErrCode int `json:"errCode"`
|
ErrCode int `json:"errCode"`
|
||||||
TypeEvaluator ParamEvaluator `json:"-"`
|
TypeEvaluator ParamEvaluator `json:"-"`
|
||||||
Funcs []reflect.Value `json:"-"`
|
Funcs []reflect.Value `json:"-"`
|
||||||
|
|
||||||
|
stringInFuncs []func(string) bool
|
||||||
|
canEval bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p TemplateParam) preComputed() TemplateParam {
|
||||||
|
for _, pfn := range p.Funcs {
|
||||||
|
if fn, ok := pfn.Interface().(func(string) bool); ok {
|
||||||
|
p.stringInFuncs = append(p.stringInFuncs, fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if true then it should be execute the type parameter or its functions
|
||||||
|
// else it can be ignored,
|
||||||
|
// i.e {myparam} or {myparam:string} or {myparam:path} ->
|
||||||
|
// their type evaluator is nil because they don't do any checks and they don't change
|
||||||
|
// the default parameter value's type (string) so no need for any work).
|
||||||
|
p.canEval = p.TypeEvaluator != nil || len(p.Funcs) > 0 || p.ErrCode != parser.DefaultParamErrorCode
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TemplateParam) CanEval() bool {
|
||||||
|
return p.canEval
|
||||||
|
}
|
||||||
|
|
||||||
|
// paramChanger is the same form of context's Params().Set
|
||||||
|
func (p *TemplateParam) Eval(paramValue string, paramChanger func(key string, newValue interface{}) (memstore.Entry, bool)) bool {
|
||||||
|
if p.TypeEvaluator == nil {
|
||||||
|
for _, fn := range p.stringInFuncs {
|
||||||
|
if !fn(paramValue) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
newValue, passed := p.TypeEvaluator(paramValue)
|
||||||
|
if !passed {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.Funcs) > 0 {
|
||||||
|
paramIn := []reflect.Value{reflect.ValueOf(newValue)}
|
||||||
|
for _, evalFunc := range p.Funcs {
|
||||||
|
// or make it as func(interface{}) bool and pass directly the "newValue"
|
||||||
|
// but that would not be as easy for end-developer, so keep that "slower":
|
||||||
|
if !evalFunc.Call(paramIn)[0].Interface().(bool) { // i.e func(paramValue int) bool
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paramChanger(p.Name, newValue)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse takes a full route path and a macro map (macro map contains the macro types with their registered param functions)
|
// Parse takes a full route path and a macro map (macro map contains the macro types with their registered param functions)
|
||||||
|
@ -82,7 +138,7 @@ func Parse(src string, macros Macros) (*Template, error) {
|
||||||
tmplParam.Funcs = append(tmplParam.Funcs, evalFn)
|
tmplParam.Funcs = append(tmplParam.Funcs, evalFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Params = append(t.Params, tmplParam)
|
t.Params = append(t.Params, tmplParam.preComputed())
|
||||||
}
|
}
|
||||||
|
|
||||||
return t, nil
|
return t, nil
|
||||||
|
|
Loading…
Reference in New Issue
Block a user