mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
Conversion once at macros and their functions, internal changes required
Former-commit-id: 7b778cccfb7c0e30ca5e8106017ada065993aba5
This commit is contained in:
parent
dc3c38b189
commit
d6d27b2605
|
@ -114,7 +114,7 @@ func main() {
|
||||||
| Param Type | Go Type | Validation | Retrieve Helper |
|
| Param Type | Go Type | Validation | Retrieve Helper |
|
||||||
| -----------------|------|-------------|------|
|
| -----------------|------|-------------|------|
|
||||||
| `:string` | string | anything | `Params().Get` |
|
| `:string` | string | anything | `Params().Get` |
|
||||||
| `:number` | uint, uint8, uint16, uint32, uint64, int, int8, int32, int64 | positive or negative number, any number of digits | `Params().GetInt/Int64`...|
|
| `:int` | uint, uint8, uint16, uint32, uint64, int, int8, int32, int64 | positive or negative number, depends on the arch | `Params().GetInt`...|
|
||||||
| `:int64` | int64 | -9223372036854775808 to 9223372036854775807 | `Params().GetInt64` |
|
| `:int64` | int64 | -9223372036854775808 to 9223372036854775807 | `Params().GetInt64` |
|
||||||
| `:uint8` | uint8 | 0 to 255 | `Params().GetUint8` |
|
| `:uint8` | uint8 | 0 to 255 | `Params().GetUint8` |
|
||||||
| `:uint64` | uint64 | 0 to 18446744073709551615 | `Params().GetUint64` |
|
| `:uint64` | uint64 | 0 to 18446744073709551615 | `Params().GetUint64` |
|
||||||
|
@ -127,7 +127,7 @@ func main() {
|
||||||
|
|
||||||
```go
|
```go
|
||||||
app.Get("/users/{id:uint64}", func(ctx iris.Context){
|
app.Get("/users/{id:uint64}", func(ctx iris.Context){
|
||||||
id, _ := ctx.Params().GetUint64("id")
|
id := ctx.Params().GetUint64Default("id", 0)
|
||||||
// [...]
|
// [...]
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
@ -226,7 +226,7 @@ func main() {
|
||||||
// but will not match /users/-1 because uint should be bigger than zero
|
// but will not match /users/-1 because uint should be bigger than zero
|
||||||
// neither /users or /users/.
|
// neither /users or /users/.
|
||||||
app.Get("/users/{id:uint64}", func(ctx iris.Context) {
|
app.Get("/users/{id:uint64}", func(ctx iris.Context) {
|
||||||
id, _ := ctx.Params().GetUint64("id")
|
id := ctx.Params().GetUint64Default("id", 0)
|
||||||
ctx.Writef("User with ID: %d", id)
|
ctx.Writef("User with ID: %d", id)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/kataras/iris"
|
"github.com/kataras/iris"
|
||||||
)
|
)
|
||||||
|
@ -122,17 +121,12 @@ func main() {
|
||||||
// Let's register our first macro attached to uint64 macro type.
|
// Let's register our first macro attached to uint64 macro type.
|
||||||
// "min" = the function
|
// "min" = the function
|
||||||
// "minValue" = the argument of the function
|
// "minValue" = the argument of the function
|
||||||
// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
|
// func(uint64) bool = our func's evaluator, this executes in serve time when
|
||||||
// a user requests a path which contains the :uint64 macro parameter type with the min(...) macro parameter function.
|
// a user requests a path which contains the :uint64 macro parameter type with the min(...) macro parameter function.
|
||||||
app.Macros().Uint64.RegisterFunc("min", func(minValue uint64) func(string) bool {
|
app.Macros().Get("uint64").RegisterFunc("min", func(minValue uint64) func(uint64) bool {
|
||||||
// do anything before serve here [...]
|
// type of "paramValue" should match the type of the internal macro's evaluator function, which in this case is "uint64".
|
||||||
// at this case we don't need to do anything
|
return func(paramValue uint64) bool {
|
||||||
return func(paramValue string) bool {
|
return paramValue >= minValue
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n >= minValue
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -142,14 +136,14 @@ func main() {
|
||||||
app.Get("/profile/{id:uint64 min(20)}", func(ctx iris.Context) {
|
app.Get("/profile/{id:uint64 min(20)}", func(ctx iris.Context) {
|
||||||
// second parameter is the error but it will always nil because we use macros,
|
// second parameter is the error but it will always nil because we use macros,
|
||||||
// the validaton already happened.
|
// the validaton already happened.
|
||||||
id, _ := ctx.Params().GetInt("id")
|
id := ctx.Params().GetUint64Default("id", 0)
|
||||||
ctx.Writef("Hello id: %d", id)
|
ctx.Writef("Hello id: %d", id)
|
||||||
})
|
})
|
||||||
|
|
||||||
// to change the error code per route's macro evaluator:
|
// to change the error code per route's macro evaluator:
|
||||||
app.Get("/profile/{id:uint64 min(1)}/friends/{friendid:uint64 min(1) else 504}", func(ctx iris.Context) {
|
app.Get("/profile/{id:uint64 min(1)}/friends/{friendid:uint64 min(1) else 504}", func(ctx iris.Context) {
|
||||||
id, _ := ctx.Params().GetInt("id") // or GetUint64.
|
id := ctx.Params().GetUint64Default("id", 0)
|
||||||
friendid, _ := ctx.Params().GetInt("friendid")
|
friendid := ctx.Params().GetUint64Default("friendid", 0)
|
||||||
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
|
ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
|
||||||
}) // this will throw e 504 error code instead of 404 if all route's macros not passed.
|
}) // this will throw e 504 error code instead of 404 if all route's macros not passed.
|
||||||
|
|
||||||
|
@ -169,7 +163,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchString is a type of func(string) bool, so we use it as it is.
|
// MatchString is a type of func(string) bool, so we use it as it is.
|
||||||
app.Macros().String.RegisterFunc("coordinate", latLonRegex.MatchString)
|
app.Macros().Get("string").RegisterFunc("coordinate", latLonRegex.MatchString)
|
||||||
|
|
||||||
app.Get("/coordinates/{lat:string coordinate() else 502}/{lon:string coordinate() else 502}", func(ctx iris.Context) {
|
app.Get("/coordinates/{lat:string coordinate() else 502}/{lon:string coordinate() else 502}", func(ctx iris.Context) {
|
||||||
ctx.Writef("Lat: %s | Lon: %s", ctx.Params().Get("lat"), ctx.Params().Get("lon"))
|
ctx.Writef("Lat: %s | Lon: %s", ctx.Params().Get("lat"), ctx.Params().Get("lon"))
|
||||||
|
@ -178,7 +172,7 @@ func main() {
|
||||||
//
|
//
|
||||||
|
|
||||||
// Another one is by using a custom body.
|
// Another one is by using a custom body.
|
||||||
app.Macros().String.RegisterFunc("range", func(minLength, maxLength int) func(string) bool {
|
app.Macros().Get("string").RegisterFunc("range", func(minLength, maxLength int) func(string) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
return len(paramValue) >= minLength && len(paramValue) <= maxLength
|
return len(paramValue) >= minLength && len(paramValue) <= maxLength
|
||||||
}
|
}
|
||||||
|
@ -193,7 +187,7 @@ func main() {
|
||||||
//
|
//
|
||||||
|
|
||||||
// Register your custom macro function which accepts a slice of strings `[...,...]`.
|
// Register your custom macro function which accepts a slice of strings `[...,...]`.
|
||||||
app.Macros().String.RegisterFunc("has", func(validNames []string) func(string) bool {
|
app.Macros().Get("string").RegisterFunc("has", func(validNames []string) func(string) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
for _, validName := range validNames {
|
for _, validName := range validNames {
|
||||||
if validName == paramValue {
|
if validName == paramValue {
|
||||||
|
|
|
@ -16,37 +16,49 @@ func main() {
|
||||||
app.Logger().SetLevel("debug")
|
app.Logger().SetLevel("debug")
|
||||||
|
|
||||||
// Let's see how we can register a custom macro such as ":uint32" or ":small" for its alias (optionally) for Uint32 types.
|
// Let's see how we can register a custom macro such as ":uint32" or ":small" for its alias (optionally) for Uint32 types.
|
||||||
app.Macros().Register("uint32", "small", false, false, func(paramValue string) bool {
|
// app.Macros().Register("uint32", "small", false, false, func(paramValue string) bool {
|
||||||
_, err := strconv.ParseUint(paramValue, 10, 32)
|
// _, err := strconv.ParseUint(paramValue, 10, 32)
|
||||||
return err == nil
|
// return err == nil
|
||||||
}).
|
// }).
|
||||||
RegisterFunc("min", func(min uint32) func(string) bool {
|
// RegisterFunc("min", func(min uint32) func(string) bool {
|
||||||
return func(paramValue string) bool {
|
// return func(paramValue string) bool {
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 32)
|
// n, err := strconv.ParseUint(paramValue, 10, 32)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
|
|
||||||
return uint32(n) >= min
|
// return uint32(n) >= min
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
somehow define one-time how the parameter should be parsed to a particular type (go std or custom)
|
||||||
|
tip: we can change the original value from string to X using the entry's.ValueRaw
|
||||||
|
^ Done 27 sep 2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
app.Macros().Register("uint32", "small", false, false, func(paramValue string) (interface{}, bool) {
|
||||||
|
v, err := strconv.ParseUint(paramValue, 10, 32)
|
||||||
|
return uint32(v), err == nil
|
||||||
|
}).
|
||||||
|
RegisterFunc("min", func(min uint32) func(uint32) bool {
|
||||||
|
return func(paramValue uint32) bool {
|
||||||
|
return paramValue >= min
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/* TODO:
|
// optionally, only when mvc or hero features are used for this custom macro/parameter type.
|
||||||
somehow define one-time how the parameter should be parsed to a particular type (go std or custom)
|
|
||||||
tip: we can change the original value from string to X using the entry's.ValueRaw
|
|
||||||
*/
|
|
||||||
|
|
||||||
context.ParamResolvers[reflect.Uint32] = func(paramIndex int) interface{} {
|
context.ParamResolvers[reflect.Uint32] = func(paramIndex int) interface{} {
|
||||||
// return func(store memstore.Store) uint32 {
|
/* both works but second is faster, we omit the duplication of the type conversion over and over as of 27 Sep of 2018 (this patch)*/
|
||||||
// param, _ := store.GetEntryAt(paramIndex)
|
// return func(ctx context.Context) uint32 {
|
||||||
|
// param := ctx.Params().GetEntryAt(paramIndex)
|
||||||
// paramValueAsUint32, _ := strconv.ParseUint(param.String(), 10, 32)
|
// paramValueAsUint32, _ := strconv.ParseUint(param.String(), 10, 32)
|
||||||
// return uint32(paramValueAsUint32)
|
// return uint32(paramValueAsUint32)
|
||||||
// }
|
// }
|
||||||
return func(ctx context.Context) uint32 {
|
return func(ctx context.Context) uint32 {
|
||||||
param := ctx.Params().GetEntryAt(paramIndex)
|
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(uint32)
|
||||||
paramValueAsUint32, _ := strconv.ParseUint(param.String(), 10, 32)
|
} /* TODO: find a way to automative it based on the macro's first return value type, if thats the case then we must not return nil even if not found,
|
||||||
return uint32(paramValueAsUint32)
|
we must return a value i.e 0 for int for its interface{} */
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@ -54,11 +66,11 @@ func main() {
|
||||||
return fmt.Sprintf("Value of the parameter is: %d\n", paramValue)
|
return fmt.Sprintf("Value of the parameter is: %d\n", paramValue)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
app.Get("test_uint64/{myparam:uint64}", handler)
|
app.Get("test_uint64/{myparam:uint64 min(5)}", func(ctx context.Context) {
|
||||||
|
// 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:
|
||||||
|
ctx.Writef("Value of the parameter is: %d\n", ctx.Params().GetUint64Default("myparam", 0))
|
||||||
|
})
|
||||||
|
|
||||||
app.Run(iris.Addr(":8080"))
|
app.Run(iris.Addr(":8080"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler(ctx context.Context) {
|
|
||||||
ctx.Writef("Value of the parameter is: %s\n", ctx.Params().Get("myparam"))
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -33,7 +34,7 @@ func (r *RequestParams) GetEntry(key string) memstore.Entry {
|
||||||
// by the key-value params.
|
// by the key-value params.
|
||||||
func (r *RequestParams) Visit(visitor func(key string, value string)) {
|
func (r *RequestParams) Visit(visitor func(key string, value string)) {
|
||||||
r.Store.Visit(func(k string, v interface{}) {
|
r.Store.Visit(func(k string, v interface{}) {
|
||||||
visitor(k, v.(string)) // always string here.
|
visitor(k, fmt.Sprintf("%v", v)) // always string here.
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package memstore
|
package memstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -67,11 +68,19 @@ func (e Entry) GetByKindOrNil(k reflect.Kind) interface{} {
|
||||||
// If not found returns "def".
|
// If not found returns "def".
|
||||||
func (e Entry) StringDefault(def string) string {
|
func (e Entry) StringDefault(def string) string {
|
||||||
v := e.ValueRaw
|
v := e.ValueRaw
|
||||||
|
if v == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
if vString, ok := v.(string); ok {
|
if vString, ok := v.(string); ok {
|
||||||
return vString
|
return vString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val := fmt.Sprintf("%v", v)
|
||||||
|
if val != "" {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +114,20 @@ func (e Entry) IntDefault(def int) (int, error) {
|
||||||
return val, nil
|
return val, nil
|
||||||
case int:
|
case int:
|
||||||
return vv, nil
|
return vv, nil
|
||||||
|
case int8:
|
||||||
|
return int(vv), nil
|
||||||
|
case int32:
|
||||||
|
return int(vv), nil
|
||||||
|
case int64:
|
||||||
|
return int(vv), nil
|
||||||
|
case uint:
|
||||||
|
return int(vv), nil
|
||||||
|
case uint8:
|
||||||
|
return int(vv), nil
|
||||||
|
case uint32:
|
||||||
|
return int(vv), nil
|
||||||
|
case uint64:
|
||||||
|
return int(vv), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return def, errFindParse.Format("int", e.Key)
|
return def, errFindParse.Format("int", e.Key)
|
||||||
|
@ -123,6 +146,10 @@ func (e Entry) Int64Default(def int64) (int64, error) {
|
||||||
return strconv.ParseInt(vv, 10, 64)
|
return strconv.ParseInt(vv, 10, 64)
|
||||||
case int64:
|
case int64:
|
||||||
return vv, nil
|
return vv, nil
|
||||||
|
case int32:
|
||||||
|
return int64(vv), nil
|
||||||
|
case int8:
|
||||||
|
return int64(vv), nil
|
||||||
case int:
|
case int:
|
||||||
return int64(vv), nil
|
return int64(vv), nil
|
||||||
}
|
}
|
||||||
|
@ -151,6 +178,12 @@ func (e Entry) Float64Default(def float64) (float64, error) {
|
||||||
return vv, nil
|
return vv, nil
|
||||||
case int:
|
case int:
|
||||||
return float64(vv), nil
|
return float64(vv), nil
|
||||||
|
case int64:
|
||||||
|
return float64(vv), nil
|
||||||
|
case uint:
|
||||||
|
return float64(vv), nil
|
||||||
|
case uint64:
|
||||||
|
return float64(vv), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return def, errFindParse.Format("float64", e.Key)
|
return def, errFindParse.Format("float64", e.Key)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package router
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kataras/iris/context"
|
"github.com/kataras/iris/context"
|
||||||
|
@ -83,24 +84,37 @@ func convertTmplToHandler(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 {
|
||||||
paramValue := ctx.Params().Get(p.Name)
|
paramValue := ctx.Params().Get(p.Name)
|
||||||
// first, check for type evaluator
|
if p.TypeEvaluator == nil {
|
||||||
if !p.TypeEvaluator(paramValue) {
|
// allow.
|
||||||
|
ctx.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// first, check for type evaluator.
|
||||||
|
newValue, passed := p.TypeEvaluator(paramValue)
|
||||||
|
if !passed {
|
||||||
ctx.StatusCode(p.ErrCode)
|
ctx.StatusCode(p.ErrCode)
|
||||||
ctx.StopExecution()
|
ctx.StopExecution()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// then check for all of its functions
|
if len(p.Funcs) > 0 {
|
||||||
for _, evalFunc := range p.Funcs {
|
paramIn := []reflect.Value{reflect.ValueOf(newValue)}
|
||||||
if !evalFunc(paramValue) {
|
// then check for all of its functions
|
||||||
ctx.StatusCode(p.ErrCode)
|
for _, evalFunc := range p.Funcs {
|
||||||
ctx.StopExecution()
|
// or make it as func(interface{}) bool and pass directly the "newValue"
|
||||||
return
|
// 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)
|
||||||
|
|
|
@ -12,14 +12,51 @@ import (
|
||||||
// EvaluatorFunc is the signature for both param types and param funcs.
|
// EvaluatorFunc is the signature for both param types and param funcs.
|
||||||
// It should accepts the param's value as string
|
// It should accepts the param's value as string
|
||||||
// and return true if validated otherwise false.
|
// and return true if validated otherwise false.
|
||||||
type EvaluatorFunc func(paramValue string) bool
|
// type EvaluatorFunc func(paramValue string) bool
|
||||||
|
// type BinderFunc func(paramValue string) interface{}
|
||||||
|
|
||||||
// NewEvaluatorFromRegexp accepts a regexp "expr" expression
|
type (
|
||||||
// and returns an EvaluatorFunc based on that regexp.
|
ParamEvaluator func(paramValue string) (interface{}, bool)
|
||||||
// the regexp is compiled before return.
|
// FuncEvaluator interface{} // i.e func(paramValue int) bool
|
||||||
|
)
|
||||||
|
|
||||||
|
var goodEvaluatorFuncs = []reflect.Type{
|
||||||
|
reflect.TypeOf(func(string) (interface{}, bool) { return nil, false }),
|
||||||
|
reflect.TypeOf(ParamEvaluator(func(string) (interface{}, bool) { return nil, false })),
|
||||||
|
}
|
||||||
|
|
||||||
|
func goodParamFunc(typ reflect.Type) bool {
|
||||||
|
if typ.Kind() == reflect.Func { // it should be a func which returns a func (see below check).
|
||||||
|
if typ.NumOut() == 1 {
|
||||||
|
typOut := typ.Out(0)
|
||||||
|
if typOut.Kind() != reflect.Func {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if typOut.NumOut() == 2 { // if it's a type of EvaluatorFunc, used for param evaluator.
|
||||||
|
for _, fType := range goodEvaluatorFuncs {
|
||||||
|
if typOut == fType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if typOut.NumIn() == 1 && typOut.NumOut() == 1 { // if it's a type of func(paramValue [int,string...]) bool, used for param funcs.
|
||||||
|
return typOut.Out(0).Kind() == reflect.Bool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regexp accepts a regexp "expr" expression
|
||||||
|
// and returns its MatchString.
|
||||||
|
// The regexp is compiled before return.
|
||||||
//
|
//
|
||||||
// Returns a not-nil error on regexp compile failure.
|
// Returns a not-nil error on regexp compile failure.
|
||||||
func NewEvaluatorFromRegexp(expr string) (EvaluatorFunc, error) {
|
func Regexp(expr string) (func(string) bool, error) {
|
||||||
if expr == "" {
|
if expr == "" {
|
||||||
return nil, fmt.Errorf("empty regex expression")
|
return nil, fmt.Errorf("empty regex expression")
|
||||||
}
|
}
|
||||||
|
@ -37,36 +74,16 @@ func NewEvaluatorFromRegexp(expr string) (EvaluatorFunc, error) {
|
||||||
return r.MatchString, nil
|
return r.MatchString, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustNewEvaluatorFromRegexp same as NewEvaluatorFromRegexp
|
// MustRegexp same as Regexp
|
||||||
// but it panics on the "expr" parse failure.
|
// but it panics on the "expr" parse failure.
|
||||||
func MustNewEvaluatorFromRegexp(expr string) EvaluatorFunc {
|
func MustRegexp(expr string) func(string) bool {
|
||||||
r, err := NewEvaluatorFromRegexp(expr)
|
r, err := Regexp(expr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
goodParamFuncReturnType = reflect.TypeOf(func(string) bool { return false })
|
|
||||||
goodParamFuncReturnType2 = reflect.TypeOf(EvaluatorFunc(func(string) bool { return false }))
|
|
||||||
)
|
|
||||||
|
|
||||||
func goodParamFunc(typ reflect.Type) bool {
|
|
||||||
// should be a func
|
|
||||||
// which returns a func(string) bool
|
|
||||||
if typ.Kind() == reflect.Func {
|
|
||||||
if typ.NumOut() == 1 {
|
|
||||||
typOut := typ.Out(0)
|
|
||||||
if typOut == goodParamFuncReturnType || typOut == goodParamFuncReturnType2 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// goodParamFuncName reports whether the function name is a valid identifier.
|
// goodParamFuncName reports whether the function name is a valid identifier.
|
||||||
func goodParamFuncName(name string) bool {
|
func goodParamFuncName(name string) bool {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
|
@ -85,7 +102,7 @@ func goodParamFuncName(name string) bool {
|
||||||
|
|
||||||
// the convertBuilderFunc return value is generating at boot time.
|
// the convertBuilderFunc return value is generating at boot time.
|
||||||
// convertFunc converts an interface to a valid full param function.
|
// convertFunc converts an interface to a valid full param function.
|
||||||
func convertBuilderFunc(fn interface{}) ParamEvaluatorBuilder {
|
func convertBuilderFunc(fn interface{}) ParamFuncBuilder {
|
||||||
|
|
||||||
typFn := reflect.TypeOf(fn)
|
typFn := reflect.TypeOf(fn)
|
||||||
if !goodParamFunc(typFn) {
|
if !goodParamFunc(typFn) {
|
||||||
|
@ -94,7 +111,7 @@ func convertBuilderFunc(fn interface{}) ParamEvaluatorBuilder {
|
||||||
|
|
||||||
numFields := typFn.NumIn()
|
numFields := typFn.NumIn()
|
||||||
|
|
||||||
return func(args []string) EvaluatorFunc {
|
return func(args []string) reflect.Value {
|
||||||
if len(args) != numFields {
|
if len(args) != numFields {
|
||||||
// no variadics support, for now.
|
// no variadics support, for now.
|
||||||
panic("args should be the same len as numFields")
|
panic("args should be the same len as numFields")
|
||||||
|
@ -179,24 +196,25 @@ func convertBuilderFunc(fn interface{}) ParamEvaluatorBuilder {
|
||||||
|
|
||||||
argValue := reflect.ValueOf(val)
|
argValue := reflect.ValueOf(val)
|
||||||
if expected, got := field.Kind(), argValue.Kind(); expected != got {
|
if expected, got := field.Kind(), argValue.Kind(); expected != got {
|
||||||
panic(fmt.Sprintf("fields should have the same type: [%d] expected %s but got %s", i, expected, got))
|
panic(fmt.Sprintf("func's input arguments should have the same type: [%d] expected %s but got %s", i, expected, got))
|
||||||
}
|
}
|
||||||
|
|
||||||
argValues = append(argValues, argValue)
|
argValues = append(argValues, argValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
evalFn := reflect.ValueOf(fn).Call(argValues)[0].Interface()
|
evalFn := reflect.ValueOf(fn).Call(argValues)[0]
|
||||||
|
|
||||||
var evaluator EvaluatorFunc
|
// var evaluator EvaluatorFunc
|
||||||
// check for typed and not typed
|
// // check for typed and not typed
|
||||||
if _v, ok := evalFn.(EvaluatorFunc); ok {
|
// if _v, ok := evalFn.(EvaluatorFunc); ok {
|
||||||
evaluator = _v
|
// evaluator = _v
|
||||||
} else if _v, ok = evalFn.(func(string) bool); ok {
|
// } else if _v, ok = evalFn.(func(string) bool); ok {
|
||||||
evaluator = _v
|
// evaluator = _v
|
||||||
}
|
// }
|
||||||
return func(paramValue string) bool {
|
// return func(paramValue interface{}) bool {
|
||||||
return evaluator(paramValue)
|
// return evaluator(paramValue)
|
||||||
}
|
// }
|
||||||
|
return evalFn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,16 +236,16 @@ type (
|
||||||
master bool
|
master bool
|
||||||
trailing bool
|
trailing bool
|
||||||
|
|
||||||
Evaluator EvaluatorFunc
|
Evaluator ParamEvaluator
|
||||||
funcs []ParamFunc
|
funcs []ParamFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParamEvaluatorBuilder is a func
|
// ParamFuncBuilder is a func
|
||||||
// which accepts a param function's arguments (values)
|
// which accepts a param function's arguments (values)
|
||||||
// and returns an EvaluatorFunc, its job
|
// and returns a function as value, its job
|
||||||
// is to make the macros to be registered
|
// is to make the macros to be registered
|
||||||
// by user at the most generic possible way.
|
// by user at the most generic possible way.
|
||||||
ParamEvaluatorBuilder func([]string) EvaluatorFunc
|
ParamFuncBuilder func([]string) reflect.Value // the func
|
||||||
|
|
||||||
// ParamFunc represents the parsed
|
// ParamFunc represents the parsed
|
||||||
// parameter function, it holds
|
// parameter function, it holds
|
||||||
|
@ -236,13 +254,13 @@ type (
|
||||||
// the evaluator func.
|
// the evaluator func.
|
||||||
ParamFunc struct {
|
ParamFunc struct {
|
||||||
Name string
|
Name string
|
||||||
Func ParamEvaluatorBuilder
|
Func ParamFuncBuilder
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewMacro creates and returns a Macro that can be used as a registry for
|
// NewMacro creates and returns a Macro that can be used as a registry for
|
||||||
// a new customized parameter type and its functions.
|
// a new customized parameter type and its functions.
|
||||||
func NewMacro(indent, alias string, master, trailing bool, evaluator EvaluatorFunc) *Macro {
|
func NewMacro(indent, alias string, master, trailing bool, evaluator ParamEvaluator) *Macro {
|
||||||
return &Macro{
|
return &Macro{
|
||||||
indent: indent,
|
indent: indent,
|
||||||
alias: alias,
|
alias: alias,
|
||||||
|
@ -287,7 +305,7 @@ func (m *Macro) RegisterFunc(funcName string, fn interface{}) *Macro {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Macro) registerFunc(funcName string, fullFn ParamEvaluatorBuilder) {
|
func (m *Macro) registerFunc(funcName string, fullFn ParamFuncBuilder) {
|
||||||
if !goodParamFuncName(funcName) {
|
if !goodParamFuncName(funcName) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -305,7 +323,7 @@ func (m *Macro) registerFunc(funcName string, fullFn ParamEvaluatorBuilder) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Macro) getFunc(funcName string) ParamEvaluatorBuilder {
|
func (m *Macro) getFunc(funcName string) ParamFuncBuilder {
|
||||||
for _, fn := range m.funcs {
|
for _, fn := range m.funcs {
|
||||||
if fn.Name == funcName {
|
if fn.Name == funcName {
|
||||||
if fn.Func == nil {
|
if fn.Func == nil {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package macro
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -64,9 +65,25 @@ func TestGoodParamFuncName(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testEvaluatorRaw(t *testing.T, macroEvaluator *Macro, input string, pass bool, i int) {
|
func testEvaluatorRaw(t *testing.T, macroEvaluator *Macro, input string, expectedType reflect.Kind, pass bool, i int) {
|
||||||
if got := macroEvaluator.Evaluator(input); pass != got {
|
if macroEvaluator.Evaluator == nil && pass {
|
||||||
t.Fatalf("%s - tests[%d] - expecting %v but got %v", t.Name(), i, pass, got)
|
return // if not evaluator defined then it should allow everything.
|
||||||
|
}
|
||||||
|
value, passed := macroEvaluator.Evaluator(input)
|
||||||
|
if pass != passed {
|
||||||
|
t.Fatalf("%s - tests[%d] - expecting[pass] %v but got %v", t.Name(), i, pass, passed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !passed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == nil && expectedType != reflect.Invalid {
|
||||||
|
t.Fatalf("%s - tests[%d] - expecting[value] to not be nil", t.Name(), i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := reflect.ValueOf(value); v.Kind() != expectedType {
|
||||||
|
t.Fatalf("%s - tests[%d] - expecting[value.Kind] %v but got %v", t.Name(), i, expectedType, v.Kind())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,30 +101,32 @@ func TestStringEvaluatorRaw(t *testing.T) {
|
||||||
} // 0
|
} // 0
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, String, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, String, tt.input, reflect.String, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNumberEvaluatorRaw(t *testing.T) {
|
func TestIntEvaluatorRaw(t *testing.T) {
|
||||||
|
x64 := strconv.IntSize == 64
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pass bool
|
pass bool
|
||||||
input string
|
input string
|
||||||
}{
|
}{
|
||||||
{false, "astring"}, // 0
|
{false, "astring"}, // 0
|
||||||
{false, "astringwith_numb3rS_and_symbol$"}, // 1
|
{false, "astringwith_numb3rS_and_symbol$"}, // 1
|
||||||
{true, "32321"}, // 2
|
{true, "32321"}, // 2
|
||||||
{true, "18446744073709551615"}, // 3
|
{x64, "9223372036854775807" /*max int64*/}, // 3
|
||||||
{true, "-18446744073709551615"}, // 4
|
{x64, "-9223372036854775808" /*min int64 */}, // 4
|
||||||
{true, "-18446744073709553213213213213213121615"}, // 5
|
{false, "-18446744073709553213213213213213121615"}, // 5
|
||||||
{false, "42 18446744073709551615"}, // 6
|
{false, "42 18446744073709551615"}, // 6
|
||||||
{false, "--42"}, // 7
|
{false, "--42"}, // 7
|
||||||
{false, "+42"}, // 8
|
{false, "+42"}, // 8
|
||||||
{false, "main.css"}, // 9
|
{false, "main.css"}, // 9
|
||||||
{false, "/assets/main.css"}, // 10
|
{false, "/assets/main.css"}, // 10
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, Number, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Int, tt.input, reflect.Int, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +151,7 @@ func TestInt64EvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, Int64, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Int64, tt.input, reflect.Int64, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +180,7 @@ func TestUint8EvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, Uint8, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Uint8, tt.input, reflect.Uint8, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +205,7 @@ func TestUint64EvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, Uint64, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Uint64, tt.input, reflect.Uint64, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +222,7 @@ func TestAlphabeticalEvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, Alphabetical, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Alphabetical, tt.input, reflect.String, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +239,7 @@ func TestFileEvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, File, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, File, tt.input, reflect.String, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +257,7 @@ func TestPathEvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range pathTests {
|
for i, tt := range pathTests {
|
||||||
testEvaluatorRaw(t, Path, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Path, tt.input, reflect.String, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,8 +289,7 @@ func TestConvertBuilderFunc(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
evalFunc := convertBuilderFunc(fn)
|
evalFunc := convertBuilderFunc(fn)
|
||||||
|
if !evalFunc([]string{"1", "[name1,name2]"}).Call([]reflect.Value{reflect.ValueOf("ok")})[0].Interface().(bool) {
|
||||||
if !evalFunc([]string{"1", "[name1,name2]"})("ok") {
|
|
||||||
t.Fatalf("failed, it should fail already")
|
t.Fatalf("failed, it should fail already")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,233 +10,205 @@ 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, func(string) bool { return true }).
|
String = NewMacro("string", "", true, false, nil). // if nil allows everything.
|
||||||
RegisterFunc("regexp", func(expr string) EvaluatorFunc {
|
RegisterFunc("regexp", func(expr string) func(string) bool {
|
||||||
return MustNewEvaluatorFromRegexp(expr)
|
return MustRegexp(expr)
|
||||||
}).
|
}).
|
||||||
// checks if param value starts with the 'prefix' arg
|
// checks if param value starts with the 'prefix' arg
|
||||||
RegisterFunc("prefix", func(prefix string) EvaluatorFunc {
|
RegisterFunc("prefix", func(prefix string) func(string) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
return strings.HasPrefix(paramValue, prefix)
|
return strings.HasPrefix(paramValue, prefix)
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if param value ends with the 'suffix' arg
|
// checks if param value ends with the 'suffix' arg
|
||||||
RegisterFunc("suffix", func(suffix string) EvaluatorFunc {
|
RegisterFunc("suffix", func(suffix string) func(string) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
return strings.HasSuffix(paramValue, suffix)
|
return strings.HasSuffix(paramValue, suffix)
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if param value contains the 's' arg
|
// checks if param value contains the 's' arg
|
||||||
RegisterFunc("contains", func(s string) EvaluatorFunc {
|
RegisterFunc("contains", func(s string) func(string) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
return strings.Contains(paramValue, s)
|
return strings.Contains(paramValue, s)
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if param value's length is at least 'min'
|
// checks if param value's length is at least 'min'
|
||||||
RegisterFunc("min", func(min int) EvaluatorFunc {
|
RegisterFunc("min", func(min int) func(string) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
return len(paramValue) >= min
|
return len(paramValue) >= min
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if param value's length is not bigger than 'max'
|
// checks if param value's length is not bigger than 'max'
|
||||||
RegisterFunc("max", func(max int) EvaluatorFunc {
|
RegisterFunc("max", func(max int) func(string) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
return max >= len(paramValue)
|
return max >= len(paramValue)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
simpleNumberEvalutator = MustNewEvaluatorFromRegexp("^-?[0-9]+$")
|
simpleNumberEval = MustRegexp("^-?[0-9]+$")
|
||||||
// Number or int type
|
// Int or int type
|
||||||
// both positive and negative numbers, any number of digits.
|
// both positive and negative numbers, actual value can be min-max int64 or min-max int32 depends on the arch.
|
||||||
Number = NewMacro("number", "int", false, false, simpleNumberEvalutator).
|
Int = NewMacro("int", "number", false, false, func(paramValue string) (interface{}, bool) {
|
||||||
|
if !simpleNumberEval(paramValue) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
v, err := strconv.Atoi(paramValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, true
|
||||||
|
}).
|
||||||
// checks if the param value's int representation is
|
// checks if the param value's int representation is
|
||||||
// bigger or equal than 'min'
|
// bigger or equal than 'min'
|
||||||
RegisterFunc("min", func(min int) EvaluatorFunc {
|
RegisterFunc("min", func(min int) func(int) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue int) bool {
|
||||||
n, err := strconv.Atoi(paramValue)
|
return paramValue >= min
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n >= min
|
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if the param value's int representation is
|
// checks if the param value's int representation is
|
||||||
// smaller or equal than 'max'.
|
// smaller or equal than 'max'.
|
||||||
RegisterFunc("max", func(max int) EvaluatorFunc {
|
RegisterFunc("max", func(max int) func(int) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue int) bool {
|
||||||
n, err := strconv.Atoi(paramValue)
|
return paramValue <= max
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n <= max
|
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if the param value's int representation is
|
// checks if the param value's int representation is
|
||||||
// between min and max, including 'min' and 'max'.
|
// between min and max, including 'min' and 'max'.
|
||||||
RegisterFunc("range", func(min, max int) EvaluatorFunc {
|
RegisterFunc("range", func(min, max int) func(int) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue int) bool {
|
||||||
n, err := strconv.Atoi(paramValue)
|
return !(paramValue < min || paramValue > max)
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if n < min || n > max {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Int64 as int64 type
|
// Int64 as int64 type
|
||||||
// -9223372036854775808 to 9223372036854775807.
|
// -9223372036854775808 to 9223372036854775807.
|
||||||
Int64 = NewMacro("int64", "long", false, false, func(paramValue string) bool {
|
Int64 = NewMacro("int64", "long", false, false, func(paramValue string) (interface{}, bool) {
|
||||||
if !simpleNumberEvalutator(paramValue) {
|
if !simpleNumberEval(paramValue) {
|
||||||
return false
|
return nil, false
|
||||||
}
|
}
|
||||||
_, err := strconv.ParseInt(paramValue, 10, 64)
|
v, err := strconv.ParseInt(paramValue, 10, 64)
|
||||||
// if err == strconv.ErrRange...
|
if err != nil { // if err == strconv.ErrRange...
|
||||||
return err == nil
|
return nil, false
|
||||||
|
}
|
||||||
|
return v, true
|
||||||
}).
|
}).
|
||||||
// checks if the param value's int64 representation is
|
// checks if the param value's int64 representation is
|
||||||
// bigger or equal than 'min'.
|
// bigger or equal than 'min'.
|
||||||
RegisterFunc("min", func(min int64) EvaluatorFunc {
|
RegisterFunc("min", func(min int64) func(int64) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue int64) bool {
|
||||||
n, err := strconv.ParseInt(paramValue, 10, 64)
|
return paramValue >= min
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n >= min
|
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if the param value's int64 representation is
|
// checks if the param value's int64 representation is
|
||||||
// smaller or equal than 'max'.
|
// smaller or equal than 'max'.
|
||||||
RegisterFunc("max", func(max int64) EvaluatorFunc {
|
RegisterFunc("max", func(max int64) func(int64) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue int64) bool {
|
||||||
n, err := strconv.ParseInt(paramValue, 10, 64)
|
return paramValue <= max
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n <= max
|
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if the param value's int64 representation is
|
// checks if the param value's int64 representation is
|
||||||
// between min and max, including 'min' and 'max'.
|
// between min and max, including 'min' and 'max'.
|
||||||
RegisterFunc("range", func(min, max int64) EvaluatorFunc {
|
RegisterFunc("range", func(min, max int64) func(int64) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue int64) bool {
|
||||||
n, err := strconv.ParseInt(paramValue, 10, 64)
|
return !(paramValue < min || paramValue > max)
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if n < min || n > max {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
uint8Eval = MustRegexp("^([0-9]|[1-8][0-9]|9[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$")
|
||||||
// Uint8 as uint8 type
|
// Uint8 as uint8 type
|
||||||
// 0 to 255.
|
// 0 to 255.
|
||||||
Uint8 = NewMacro("uint8", "", false, false, MustNewEvaluatorFromRegexp("^([0-9]|[1-8][0-9]|9[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$")).
|
Uint8 = NewMacro("uint8", "", false, false, func(paramValue string) (interface{}, bool) {
|
||||||
|
if !uint8Eval(paramValue) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := strconv.ParseUint(paramValue, 10, 8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return uint8(v), true
|
||||||
|
}).
|
||||||
// checks if the param value's uint8 representation is
|
// checks if the param value's uint8 representation is
|
||||||
// bigger or equal than 'min'.
|
// bigger or equal than 'min'.
|
||||||
RegisterFunc("min", func(min uint8) EvaluatorFunc {
|
RegisterFunc("min", func(min uint8) func(uint8) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue uint8) bool {
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 8)
|
return paramValue >= min
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return uint8(n) >= min
|
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if the param value's uint8 representation is
|
// checks if the param value's uint8 representation is
|
||||||
// smaller or equal than 'max'.
|
// smaller or equal than 'max'.
|
||||||
RegisterFunc("max", func(max uint8) EvaluatorFunc {
|
RegisterFunc("max", func(max uint8) func(uint8) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue uint8) bool {
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 8)
|
return paramValue <= max
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return uint8(n) <= max
|
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if the param value's uint8 representation is
|
// checks if the param value's uint8 representation is
|
||||||
// between min and max, including 'min' and 'max'.
|
// between min and max, including 'min' and 'max'.
|
||||||
RegisterFunc("range", func(min, max uint8) EvaluatorFunc {
|
RegisterFunc("range", func(min, max uint8) func(uint8) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue uint8) bool {
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 8)
|
return !(paramValue < min || paramValue > max)
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := uint8(n); v < min || v > max {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Uint64 as uint64 type
|
// Uint64 as uint64 type
|
||||||
// 0 to 18446744073709551615.
|
// 0 to 18446744073709551615.
|
||||||
Uint64 = NewMacro("uint64", "", false, false, func(paramValue string) bool {
|
Uint64 = NewMacro("uint64", "", false, false, func(paramValue string) (interface{}, bool) {
|
||||||
if !simpleNumberEvalutator(paramValue) {
|
if !simpleNumberEval(paramValue) {
|
||||||
return false
|
return nil, false
|
||||||
}
|
}
|
||||||
_, err := strconv.ParseUint(paramValue, 10, 64)
|
v, err := strconv.ParseUint(paramValue, 10, 64)
|
||||||
return err == nil
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return v, true
|
||||||
}).
|
}).
|
||||||
// checks if the param value's uint64 representation is
|
// checks if the param value's uint64 representation is
|
||||||
// bigger or equal than 'min'.
|
// bigger or equal than 'min'.
|
||||||
RegisterFunc("min", func(min uint64) EvaluatorFunc {
|
RegisterFunc("min", func(min uint64) func(uint64) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue uint64) bool {
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 64)
|
return paramValue >= min
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n >= min
|
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if the param value's uint64 representation is
|
// checks if the param value's uint64 representation is
|
||||||
// smaller or equal than 'max'.
|
// smaller or equal than 'max'.
|
||||||
RegisterFunc("max", func(max uint64) EvaluatorFunc {
|
RegisterFunc("max", func(max uint64) func(uint64) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue uint64) bool {
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 64)
|
return paramValue <= max
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n <= max
|
|
||||||
}
|
}
|
||||||
}).
|
}).
|
||||||
// checks if the param value's uint64 representation is
|
// checks if the param value's uint64 representation is
|
||||||
// between min and max, including 'min' and 'max'.
|
// between min and max, including 'min' and 'max'.
|
||||||
RegisterFunc("range", func(min, max uint64) EvaluatorFunc {
|
RegisterFunc("range", func(min, max uint64) func(uint64) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue uint64) bool {
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 64)
|
return !(paramValue < min || paramValue > max)
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if n < min || n > max {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Bool or boolean as bool type
|
// Bool or boolean as bool type
|
||||||
// a string which is "1" or "t" or "T" or "TRUE" or "true" or "True"
|
// a string which is "1" or "t" or "T" or "TRUE" or "true" or "True"
|
||||||
// or "0" or "f" or "F" or "FALSE" or "false" or "False".
|
// or "0" or "f" or "F" or "FALSE" or "false" or "False".
|
||||||
Bool = NewMacro("bool", "boolean", false, false, func(paramValue string) bool {
|
Bool = NewMacro("bool", "boolean", false, false, func(paramValue string) (interface{}, bool) {
|
||||||
// a simple if statement is faster than regex ^(true|false|True|False|t|0|f|FALSE|TRUE)$
|
// a simple if statement is faster than regex ^(true|false|True|False|t|0|f|FALSE|TRUE)$
|
||||||
// in this case.
|
// in this case.
|
||||||
_, err := strconv.ParseBool(paramValue)
|
v, err := strconv.ParseBool(paramValue)
|
||||||
return err == nil
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return v, true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
alphabeticalEval = MustRegexp("^[a-zA-Z ]+$")
|
||||||
// Alphabetical letter type
|
// Alphabetical letter type
|
||||||
// letters only (upper or lowercase)
|
// letters only (upper or lowercase)
|
||||||
Alphabetical = NewMacro("alphabetical", "", false, false, MustNewEvaluatorFromRegexp("^[a-zA-Z ]+$"))
|
Alphabetical = NewMacro("alphabetical", "", false, false, func(paramValue string) (interface{}, bool) {
|
||||||
|
if !alphabeticalEval(paramValue) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return paramValue, true
|
||||||
|
})
|
||||||
|
|
||||||
|
fileEval = MustRegexp("^[a-zA-Z0-9_.-]*$")
|
||||||
// File type
|
// File type
|
||||||
// letters (upper or lowercase)
|
// letters (upper or lowercase)
|
||||||
// numbers (0-9)
|
// numbers (0-9)
|
||||||
|
@ -244,7 +216,12 @@ var (
|
||||||
// dash (-)
|
// dash (-)
|
||||||
// point (.)
|
// point (.)
|
||||||
// no spaces! or other character
|
// no spaces! or other character
|
||||||
File = NewMacro("file", "", false, false, MustNewEvaluatorFromRegexp("^[a-zA-Z0-9_.-]*$"))
|
File = NewMacro("file", "", false, false, func(paramValue string) (interface{}, bool) {
|
||||||
|
if !fileEval(paramValue) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return paramValue, true
|
||||||
|
})
|
||||||
// Path type
|
// Path type
|
||||||
// anything, should be the last part
|
// anything, should be the last part
|
||||||
//
|
//
|
||||||
|
@ -252,11 +229,11 @@ var (
|
||||||
// types because I want to give the opportunity to the user
|
// types because I want to give the opportunity to the user
|
||||||
// to organise the macro functions based on wildcard or single dynamic named path parameter.
|
// to organise the macro functions based on wildcard or single dynamic named path parameter.
|
||||||
// Should be living in the latest path segment of a route path.
|
// Should be living in the latest path segment of a route path.
|
||||||
Path = NewMacro("path", "", false, true, func(string) bool { return true })
|
Path = NewMacro("path", "", false, true, nil)
|
||||||
|
|
||||||
Defaults = &Macros{
|
Defaults = &Macros{
|
||||||
String,
|
String,
|
||||||
Number,
|
Int,
|
||||||
Int64,
|
Int64,
|
||||||
Uint8,
|
Uint8,
|
||||||
Uint64,
|
Uint64,
|
||||||
|
@ -268,7 +245,7 @@ var (
|
||||||
|
|
||||||
type Macros []*Macro
|
type Macros []*Macro
|
||||||
|
|
||||||
func (ms *Macros) Register(indent, alias string, isMaster, isTrailing bool, evaluator EvaluatorFunc) *Macro {
|
func (ms *Macros) Register(indent, alias string, isMaster, isTrailing bool, evaluator ParamEvaluator) *Macro {
|
||||||
macro := NewMacro(indent, alias, isMaster, isTrailing, evaluator)
|
macro := NewMacro(indent, alias, isMaster, isTrailing, evaluator)
|
||||||
if ms.register(macro) {
|
if ms.register(macro) {
|
||||||
return macro
|
return macro
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package macro
|
package macro
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
||||||
"github.com/kataras/iris/core/router/macro/interpreter/parser"
|
"github.com/kataras/iris/core/router/macro/interpreter/parser"
|
||||||
)
|
)
|
||||||
|
@ -27,8 +29,8 @@ type TemplateParam struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Index int `json:"index"`
|
Index int `json:"index"`
|
||||||
ErrCode int `json:"errCode"`
|
ErrCode int `json:"errCode"`
|
||||||
TypeEvaluator EvaluatorFunc `json:"-"`
|
TypeEvaluator ParamEvaluator `json:"-"`
|
||||||
Funcs []EvaluatorFunc `json:"-"`
|
Funcs []reflect.Value `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
|
@ -74,7 +76,7 @@ func Parse(src string, macros Macros) (*Template, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
evalFn := tmplFn(paramfn.Args)
|
evalFn := tmplFn(paramfn.Args)
|
||||||
if evalFn == nil {
|
if evalFn.IsNil() || !evalFn.IsValid() || evalFn.Kind() != reflect.Func {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tmplParam.Funcs = append(tmplParam.Funcs, evalFn)
|
tmplParam.Funcs = append(tmplParam.Funcs, evalFn)
|
||||||
|
|
|
@ -6,15 +6,13 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/kataras/iris/context"
|
"github.com/kataras/iris/context"
|
||||||
"github.com/kataras/iris/core/memstore"
|
|
||||||
"github.com/kataras/iris/hero/di"
|
"github.com/kataras/iris/hero/di"
|
||||||
|
|
||||||
"github.com/kataras/golog"
|
"github.com/kataras/golog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
contextTyp = reflect.TypeOf((*context.Context)(nil)).Elem()
|
contextTyp = reflect.TypeOf((*context.Context)(nil)).Elem()
|
||||||
memstoreTyp = reflect.TypeOf(memstore.Store{})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsContext returns true if the "inTyp" is a type of Context.
|
// IsContext returns true if the "inTyp" is a type of Context.
|
||||||
|
@ -22,14 +20,6 @@ func IsContext(inTyp reflect.Type) bool {
|
||||||
return inTyp.Implements(contextTyp)
|
return inTyp.Implements(contextTyp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsExpectingStore returns true if the "inTyp" is a type of memstore.Store.
|
|
||||||
func IsExpectingStore(inTyp reflect.Type) bool {
|
|
||||||
print("di/handler.go: " + inTyp.String() + " vs " + memstoreTyp.String() + " : ")
|
|
||||||
println(inTyp == memstoreTyp)
|
|
||||||
|
|
||||||
return inTyp == memstoreTyp
|
|
||||||
}
|
|
||||||
|
|
||||||
// checks if "handler" is context.Handler: func(context.Context).
|
// checks if "handler" is context.Handler: func(context.Context).
|
||||||
func isContextHandler(handler interface{}) (context.Handler, bool) {
|
func isContextHandler(handler interface{}) (context.Handler, bool) {
|
||||||
h, is := handler.(context.Handler)
|
h, is := handler.(context.Handler)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user