mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 23:40:35 +01:00
add the ability to add custom parameter types to the interpreter and mapped macros with any number of macro functions - example added - although it's working it is not ready yet - I have to do some cleanup, doc comments and a TODO
Former-commit-id: 8ac751b649a3b8e59948fd4c89ad53d25f49d0d5
This commit is contained in:
parent
52a07df0f4
commit
dc3c38b189
|
@ -164,7 +164,7 @@ latLonRegex, _ := regexp.Compile(latLonExpr)
|
||||||
|
|
||||||
// Register your custom argument-less macro function to the :string param type.
|
// Register your custom argument-less macro function to the :string param type.
|
||||||
// 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()}/{lon:string coordinate()}", func(ctx iris.Context) {
|
app.Get("/coordinates/{lat:string coordinate()}/{lon:string coordinate()}", 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"))
|
||||||
|
@ -175,7 +175,7 @@ Register your custom macro function which accepts two int arguments.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ app.Get("/limitchar/{name:string range(1,200) else 400}", func(ctx iris.Context)
|
||||||
Register your custom macro function which accepts a slice of strings `[...,...]`.
|
Register your custom macro function which accepts a slice of strings `[...,...]`.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
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 {
|
||||||
|
|
64
_examples/routing/macros/main.go
Normal file
64
_examples/routing/macros/main.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/kataras/iris"
|
||||||
|
"github.com/kataras/iris/context"
|
||||||
|
// "github.com/kataras/iris/core/memstore"
|
||||||
|
"github.com/kataras/iris/hero"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := iris.New()
|
||||||
|
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.
|
||||||
|
app.Macros().Register("uint32", "small", false, false, func(paramValue string) bool {
|
||||||
|
_, err := strconv.ParseUint(paramValue, 10, 32)
|
||||||
|
return err == nil
|
||||||
|
}).
|
||||||
|
RegisterFunc("min", func(min uint32) func(string) bool {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseUint(paramValue, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
context.ParamResolvers[reflect.Uint32] = func(paramIndex int) interface{} {
|
||||||
|
// return func(store memstore.Store) uint32 {
|
||||||
|
// param, _ := store.GetEntryAt(paramIndex)
|
||||||
|
// paramValueAsUint32, _ := strconv.ParseUint(param.String(), 10, 32)
|
||||||
|
// return uint32(paramValueAsUint32)
|
||||||
|
// }
|
||||||
|
return func(ctx context.Context) uint32 {
|
||||||
|
param := ctx.Params().GetEntryAt(paramIndex)
|
||||||
|
paramValueAsUint32, _ := strconv.ParseUint(param.String(), 10, 32)
|
||||||
|
return uint32(paramValueAsUint32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
|
||||||
|
app.Get("/test_uint32/{myparam:uint32 min(10)}", hero.Handler(func(paramValue uint32) string {
|
||||||
|
return fmt.Sprintf("Value of the parameter is: %d\n", paramValue)
|
||||||
|
}))
|
||||||
|
|
||||||
|
app.Get("test_uint64/{myparam:uint64}", handler)
|
||||||
|
|
||||||
|
app.Run(iris.Addr(":8080"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handler(ctx context.Context) {
|
||||||
|
ctx.Writef("Value of the parameter is: %s\n", ctx.Params().Get("myparam"))
|
||||||
|
}
|
|
@ -76,138 +76,6 @@ func (u UnmarshalerFunc) Unmarshal(data []byte, v interface{}) error {
|
||||||
return u(data, v)
|
return u(data, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestParams is a key string - value string storage which
|
|
||||||
// context's request dynamic path params are being kept.
|
|
||||||
// Empty if the route is static.
|
|
||||||
type RequestParams struct {
|
|
||||||
store memstore.Store
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set adds a key-value pair to the path parameters values
|
|
||||||
// it's being called internally so it shouldn't be used as a local storage by the user, use `ctx.Values()` instead.
|
|
||||||
func (r *RequestParams) Set(key, value string) {
|
|
||||||
r.store.Set(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visit accepts a visitor which will be filled
|
|
||||||
// by the key-value params.
|
|
||||||
func (r *RequestParams) Visit(visitor func(key string, value string)) {
|
|
||||||
r.store.Visit(func(k string, v interface{}) {
|
|
||||||
visitor(k, v.(string)) // always string here.
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var emptyEntry memstore.Entry
|
|
||||||
|
|
||||||
// GetEntryAt returns the internal Entry of the memstore based on its index,
|
|
||||||
// the stored index by the router.
|
|
||||||
// If not found then it returns a zero Entry and false.
|
|
||||||
func (r RequestParams) GetEntryAt(index int) (memstore.Entry, bool) {
|
|
||||||
if len(r.store) > index {
|
|
||||||
return r.store[index], true
|
|
||||||
}
|
|
||||||
return emptyEntry, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEntry returns the internal Entry of the memstore based on its "key".
|
|
||||||
// If not found then it returns a zero Entry and false.
|
|
||||||
func (r RequestParams) GetEntry(key string) (memstore.Entry, bool) {
|
|
||||||
// we don't return the pointer here, we don't want to give the end-developer
|
|
||||||
// the strength to change the entry that way.
|
|
||||||
if e := r.store.GetEntry(key); e != nil {
|
|
||||||
return *e, true
|
|
||||||
}
|
|
||||||
return emptyEntry, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns a path parameter's value based on its route's dynamic path key.
|
|
||||||
func (r RequestParams) Get(key string) string {
|
|
||||||
return r.store.GetString(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTrim returns a path parameter's value without trailing spaces based on its route's dynamic path key.
|
|
||||||
func (r RequestParams) GetTrim(key string) string {
|
|
||||||
return strings.TrimSpace(r.Get(key))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEscape returns a path parameter's double-url-query-escaped value based on its route's dynamic path key.
|
|
||||||
func (r RequestParams) GetEscape(key string) string {
|
|
||||||
return DecodeQuery(DecodeQuery(r.Get(key)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDecoded returns a path parameter's double-url-query-escaped value based on its route's dynamic path key.
|
|
||||||
// same as `GetEscape`.
|
|
||||||
func (r RequestParams) GetDecoded(key string) string {
|
|
||||||
return r.GetEscape(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInt returns the path parameter's value as int, based on its key.
|
|
||||||
// It checks for all available types of int, including int64, strings etc.
|
|
||||||
// It will return -1 and a non-nil error if parameter wasn't found.
|
|
||||||
func (r RequestParams) GetInt(key string) (int, error) {
|
|
||||||
return r.store.GetInt(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInt64 returns the path paramete's value as int64, based on its key.
|
|
||||||
// It checks for all available types of int, including int, strings etc.
|
|
||||||
// It will return -1 and a non-nil error if parameter wasn't found.
|
|
||||||
func (r RequestParams) GetInt64(key string) (int64, error) {
|
|
||||||
return r.store.GetInt64(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFloat64 returns a path parameter's value based as float64 on its route's dynamic path key.
|
|
||||||
// It checks for all available types of int, including float64, int, strings etc.
|
|
||||||
// It will return -1 and a non-nil error if parameter wasn't found.
|
|
||||||
func (r RequestParams) GetFloat64(key string) (float64, error) {
|
|
||||||
return r.store.GetFloat64(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUint8 returns the path parameter's value as uint8, based on its key.
|
|
||||||
// It checks for all available types of int, including int, string.
|
|
||||||
// It will return 0 and a non-nil error if parameter wasn't found.
|
|
||||||
func (r RequestParams) GetUint8(key string) (uint8, error) {
|
|
||||||
return r.store.GetUint8(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUint64 returns the path parameter's value as uint64, based on its key.
|
|
||||||
// It checks for all available types of int, including int, uint64, int64, strings etc.
|
|
||||||
// It will return 0 and a non-nil error if parameter wasn't found.
|
|
||||||
func (r RequestParams) GetUint64(key string) (uint64, error) {
|
|
||||||
return r.store.GetUint64(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBool returns the path parameter's value as bool, based on its key.
|
|
||||||
// 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".
|
|
||||||
// Any other value returns an error.
|
|
||||||
func (r RequestParams) GetBool(key string) (bool, error) {
|
|
||||||
return r.store.GetBool(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIntUnslashed same as Get but it removes the first slash if found.
|
|
||||||
// Usage: Get an id from a wildcard path.
|
|
||||||
//
|
|
||||||
// Returns -1 with an error if the parameter couldn't be found.
|
|
||||||
func (r RequestParams) GetIntUnslashed(key string) (int, error) {
|
|
||||||
v := r.Get(key)
|
|
||||||
if v != "" {
|
|
||||||
if len(v) > 1 {
|
|
||||||
if v[0] == '/' {
|
|
||||||
v = v[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strconv.Atoi(v)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1, fmt.Errorf("unable to find int for '%s'", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the full length of the parameters.
|
|
||||||
func (r RequestParams) Len() int {
|
|
||||||
return r.store.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Context is the midle-man server's "object" for the clients.
|
// Context is the midle-man server's "object" for the clients.
|
||||||
//
|
//
|
||||||
// A New context is being acquired from a sync.Pool on each connection.
|
// A New context is being acquired from a sync.Pool on each connection.
|
||||||
|
@ -1123,7 +991,7 @@ func NewContext(app Application) Context {
|
||||||
func (ctx *context) BeginRequest(w http.ResponseWriter, r *http.Request) {
|
func (ctx *context) BeginRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx.handlers = nil // will be filled by router.Serve/HTTP
|
ctx.handlers = nil // will be filled by router.Serve/HTTP
|
||||||
ctx.values = ctx.values[0:0] // >> >> by context.Values().Set
|
ctx.values = ctx.values[0:0] // >> >> by context.Values().Set
|
||||||
ctx.params.store = ctx.params.store[0:0]
|
ctx.params.Store = ctx.params.Store[0:0]
|
||||||
ctx.request = r
|
ctx.request = r
|
||||||
ctx.currentHandlerIndex = 0
|
ctx.currentHandlerIndex = 0
|
||||||
ctx.writer = AcquireResponseWriter()
|
ctx.writer = AcquireResponseWriter()
|
||||||
|
|
159
context/request_params.go
Normal file
159
context/request_params.go
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kataras/iris/core/memstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequestParams is a key string - value string storage which
|
||||||
|
// context's request dynamic path params are being kept.
|
||||||
|
// Empty if the route is static.
|
||||||
|
type RequestParams struct {
|
||||||
|
memstore.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEntryAt will return the parameter's internal store's `Entry` based on the index.
|
||||||
|
// If not found it will return an emptry `Entry`.
|
||||||
|
func (r *RequestParams) GetEntryAt(index int) memstore.Entry {
|
||||||
|
entry, _ := r.Store.GetEntryAt(index)
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEntry will return the parameter's internal store's `Entry` based on its name/key.
|
||||||
|
// If not found it will return an emptry `Entry`.
|
||||||
|
func (r *RequestParams) GetEntry(key string) memstore.Entry {
|
||||||
|
entry, _ := r.Store.GetEntry(key)
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visit accepts a visitor which will be filled
|
||||||
|
// by the key-value params.
|
||||||
|
func (r *RequestParams) Visit(visitor func(key string, value string)) {
|
||||||
|
r.Store.Visit(func(k string, v interface{}) {
|
||||||
|
visitor(k, v.(string)) // always string here.
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a path parameter's value based on its route's dynamic path key.
|
||||||
|
func (r RequestParams) Get(key string) string {
|
||||||
|
return r.GetString(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTrim returns a path parameter's value without trailing spaces based on its route's dynamic path key.
|
||||||
|
func (r RequestParams) GetTrim(key string) string {
|
||||||
|
return strings.TrimSpace(r.Get(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEscape returns a path parameter's double-url-query-escaped value based on its route's dynamic path key.
|
||||||
|
func (r RequestParams) GetEscape(key string) string {
|
||||||
|
return DecodeQuery(DecodeQuery(r.Get(key)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDecoded returns a path parameter's double-url-query-escaped value based on its route's dynamic path key.
|
||||||
|
// same as `GetEscape`.
|
||||||
|
func (r RequestParams) GetDecoded(key string) string {
|
||||||
|
return r.GetEscape(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIntUnslashed same as Get but it removes the first slash if found.
|
||||||
|
// Usage: Get an id from a wildcard path.
|
||||||
|
//
|
||||||
|
// Returns -1 and false if not path parameter with that "key" found.
|
||||||
|
func (r RequestParams) GetIntUnslashed(key string) (int, bool) {
|
||||||
|
v := r.Get(key)
|
||||||
|
if v != "" {
|
||||||
|
if len(v) > 1 {
|
||||||
|
if v[0] == '/' {
|
||||||
|
v = v[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vInt, err := strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
return -1, false
|
||||||
|
}
|
||||||
|
return vInt, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1, false
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ParamResolvers = map[reflect.Kind]func(paramIndex int) interface{}{
|
||||||
|
reflect.String: func(paramIndex int) interface{} {
|
||||||
|
return func(ctx Context) string {
|
||||||
|
return ctx.Params().GetEntryAt(paramIndex).ValueRaw.(string)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reflect.Int: func(paramIndex int) interface{} {
|
||||||
|
return func(ctx Context) int {
|
||||||
|
v, _ := ctx.Params().GetEntryAt(paramIndex).IntDefault(0)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reflect.Int64: func(paramIndex int) interface{} {
|
||||||
|
return func(ctx Context) int64 {
|
||||||
|
v, _ := ctx.Params().GetEntryAt(paramIndex).Int64Default(0)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reflect.Uint8: func(paramIndex int) interface{} {
|
||||||
|
return func(ctx Context) uint8 {
|
||||||
|
v, _ := ctx.Params().GetEntryAt(paramIndex).Uint8Default(0)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reflect.Uint64: func(paramIndex int) interface{} {
|
||||||
|
return func(ctx Context) uint64 {
|
||||||
|
v, _ := ctx.Params().GetEntryAt(paramIndex).Uint64Default(0)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reflect.Bool: func(paramIndex int) interface{} {
|
||||||
|
return func(ctx Context) bool {
|
||||||
|
v, _ := ctx.Params().GetEntryAt(paramIndex).BoolDefault(false)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParamResolverByKindAndIndex will return a function that can be used to bind path parameter's exact value by its Go std type
|
||||||
|
// and the parameter's index based on the registered path.
|
||||||
|
// Usage: nameResolver := ParamResolverByKindAndKey(reflect.String, 0)
|
||||||
|
// Inside a Handler: nameResolver.Call(ctx)[0]
|
||||||
|
// it will return the reflect.Value Of the exact type of the parameter(based on the path parameters and macros).
|
||||||
|
// It is only useful for dynamic binding of the parameter, it is used on "hero" package and it should be modified
|
||||||
|
// only when Macros are modified in such way that the default selections for the available go std types are not enough.
|
||||||
|
//
|
||||||
|
// Returns empty value and false if "k" does not match any valid parameter resolver.
|
||||||
|
func ParamResolverByKindAndIndex(k reflect.Kind, paramIndex int) (reflect.Value, bool) {
|
||||||
|
/* NO:
|
||||||
|
// This could work but its result is not exact type, so direct binding is not possible.
|
||||||
|
resolver := m.ParamResolver
|
||||||
|
fn := func(ctx context.Context) interface{} {
|
||||||
|
entry, _ := ctx.Params().GetEntry(paramName)
|
||||||
|
return resolver(entry)
|
||||||
|
}
|
||||||
|
//
|
||||||
|
|
||||||
|
// This works but it is slower on serve-time.
|
||||||
|
paramNameValue := []reflect.Value{reflect.ValueOf(paramName)}
|
||||||
|
var fnSignature func(context.Context) string
|
||||||
|
return reflect.MakeFunc(reflect.ValueOf(&fnSignature).Elem().Type(), func(in []reflect.Value) []reflect.Value {
|
||||||
|
return in[0].MethodByName("Params").Call(emptyIn)[0].MethodByName("Get").Call(paramNameValue)
|
||||||
|
// return []reflect.Value{reflect.ValueOf(in[0].Interface().(context.Context).Params().Get(paramName))}
|
||||||
|
})
|
||||||
|
//
|
||||||
|
*/
|
||||||
|
|
||||||
|
r, ok := ParamResolvers[k]
|
||||||
|
if !ok || r == nil {
|
||||||
|
return reflect.Value{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return reflect.ValueOf(r(paramIndex)), true
|
||||||
|
}
|
|
@ -94,15 +94,17 @@ func (e Entry) IntDefault(def int) (int, error) {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return def, errFindParse.Format("int", e.Key)
|
return def, errFindParse.Format("int", e.Key)
|
||||||
}
|
}
|
||||||
if vint, ok := v.(int); ok {
|
|
||||||
return vint, nil
|
switch vv := v.(type) {
|
||||||
} else if vstring, sok := v.(string); sok && vstring != "" {
|
case string:
|
||||||
vint, err := strconv.Atoi(vstring)
|
val, err := strconv.Atoi(vv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return def, err
|
return def, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return vint, nil
|
return val, nil
|
||||||
|
case int:
|
||||||
|
return vv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return def, errFindParse.Format("int", e.Key)
|
return def, errFindParse.Format("int", e.Key)
|
||||||
|
@ -116,16 +118,13 @@ func (e Entry) Int64Default(def int64) (int64, error) {
|
||||||
return def, errFindParse.Format("int64", e.Key)
|
return def, errFindParse.Format("int64", e.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vint64, ok := v.(int64); ok {
|
switch vv := v.(type) {
|
||||||
return vint64, nil
|
case string:
|
||||||
}
|
return strconv.ParseInt(vv, 10, 64)
|
||||||
|
case int64:
|
||||||
if vint, ok := v.(int); ok {
|
return vv, nil
|
||||||
return int64(vint), nil
|
case int:
|
||||||
}
|
return int64(vv), nil
|
||||||
|
|
||||||
if vstring, sok := v.(string); sok {
|
|
||||||
return strconv.ParseInt(vstring, 10, 64)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return def, errFindParse.Format("int64", e.Key)
|
return def, errFindParse.Format("int64", e.Key)
|
||||||
|
@ -135,30 +134,23 @@ func (e Entry) Int64Default(def int64) (int64, error) {
|
||||||
// If not found returns "def" and a non-nil error.
|
// If not found returns "def" and a non-nil error.
|
||||||
func (e Entry) Float64Default(def float64) (float64, error) {
|
func (e Entry) Float64Default(def float64) (float64, error) {
|
||||||
v := e.ValueRaw
|
v := e.ValueRaw
|
||||||
|
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return def, errFindParse.Format("float64", e.Key)
|
return def, errFindParse.Format("float64", e.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vfloat32, ok := v.(float32); ok {
|
switch vv := v.(type) {
|
||||||
return float64(vfloat32), nil
|
case string:
|
||||||
}
|
val, err := strconv.ParseFloat(vv, 64)
|
||||||
|
|
||||||
if vfloat64, ok := v.(float64); ok {
|
|
||||||
return vfloat64, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if vint, ok := v.(int); ok {
|
|
||||||
return float64(vint), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if vstring, sok := v.(string); sok {
|
|
||||||
vfloat64, err := strconv.ParseFloat(vstring, 64)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return def, err
|
return def, err
|
||||||
}
|
}
|
||||||
|
return val, nil
|
||||||
return vfloat64, nil
|
case float32:
|
||||||
|
return float64(vv), nil
|
||||||
|
case float64:
|
||||||
|
return vv, nil
|
||||||
|
case int:
|
||||||
|
return float64(vv), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return def, errFindParse.Format("float64", e.Key)
|
return def, errFindParse.Format("float64", e.Key)
|
||||||
|
@ -168,30 +160,24 @@ func (e Entry) Float64Default(def float64) (float64, error) {
|
||||||
// If not found returns "def" and a non-nil error.
|
// If not found returns "def" and a non-nil error.
|
||||||
func (e Entry) Float32Default(key string, def float32) (float32, error) {
|
func (e Entry) Float32Default(key string, def float32) (float32, error) {
|
||||||
v := e.ValueRaw
|
v := e.ValueRaw
|
||||||
|
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return def, errFindParse.Format("float32", e.Key)
|
return def, errFindParse.Format("float32", e.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vfloat32, ok := v.(float32); ok {
|
switch vv := v.(type) {
|
||||||
return vfloat32, nil
|
case string:
|
||||||
}
|
val, err := strconv.ParseFloat(vv, 32)
|
||||||
|
|
||||||
if vfloat64, ok := v.(float64); ok {
|
|
||||||
return float32(vfloat64), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if vint, ok := v.(int); ok {
|
|
||||||
return float32(vint), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if vstring, sok := v.(string); sok {
|
|
||||||
vfloat32, err := strconv.ParseFloat(vstring, 32)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return def, err
|
return def, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return float32(vfloat32), nil
|
return float32(val), nil
|
||||||
|
case float32:
|
||||||
|
return vv, nil
|
||||||
|
case float64:
|
||||||
|
return float32(vv), nil
|
||||||
|
case int:
|
||||||
|
return float32(vv), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return def, errFindParse.Format("float32", e.Key)
|
return def, errFindParse.Format("float32", e.Key)
|
||||||
|
@ -205,26 +191,23 @@ func (e Entry) Uint8Default(def uint8) (uint8, error) {
|
||||||
return def, errFindParse.Format("uint8", e.Key)
|
return def, errFindParse.Format("uint8", e.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vuint8, ok := v.(uint8); ok {
|
switch vv := v.(type) {
|
||||||
return vuint8, nil
|
case string:
|
||||||
}
|
val, err := strconv.ParseUint(vv, 10, 8)
|
||||||
|
|
||||||
if vint, ok := v.(int); ok {
|
|
||||||
if vint < 0 || vint > 255 {
|
|
||||||
return def, errFindParse.Format("uint8", e.Key)
|
|
||||||
}
|
|
||||||
return uint8(vint), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if vstring, sok := v.(string); sok {
|
|
||||||
vuint64, err := strconv.ParseUint(vstring, 10, 8)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return def, err
|
return def, err
|
||||||
}
|
}
|
||||||
if vuint64 > 255 {
|
if val > 255 {
|
||||||
return def, errFindParse.Format("uint8", e.Key)
|
return def, errFindParse.Format("uint8", e.Key)
|
||||||
}
|
}
|
||||||
return uint8(vuint64), nil
|
return uint8(val), nil
|
||||||
|
case uint8:
|
||||||
|
return vv, nil
|
||||||
|
case int:
|
||||||
|
if vv < 0 || vv > 255 {
|
||||||
|
return def, errFindParse.Format("uint8", e.Key)
|
||||||
|
}
|
||||||
|
return uint8(vv), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return def, errFindParse.Format("uint8", e.Key)
|
return def, errFindParse.Format("uint8", e.Key)
|
||||||
|
@ -238,20 +221,15 @@ func (e Entry) Uint64Default(def uint64) (uint64, error) {
|
||||||
return def, errFindParse.Format("uint64", e.Key)
|
return def, errFindParse.Format("uint64", e.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vuint64, ok := v.(uint64); ok {
|
switch vv := v.(type) {
|
||||||
return vuint64, nil
|
case string:
|
||||||
}
|
return strconv.ParseUint(vv, 10, 64)
|
||||||
|
case uint64:
|
||||||
if vint64, ok := v.(int64); ok {
|
return vv, nil
|
||||||
return uint64(vint64), nil
|
case int64:
|
||||||
}
|
return uint64(vv), nil
|
||||||
|
case int:
|
||||||
if vint, ok := v.(int); ok {
|
return uint64(vv), nil
|
||||||
return uint64(vint), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if vstring, sok := v.(string); sok {
|
|
||||||
return strconv.ParseUint(vstring, 10, 64)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return def, errFindParse.Format("uint64", e.Key)
|
return def, errFindParse.Format("uint64", e.Key)
|
||||||
|
@ -269,20 +247,17 @@ func (e Entry) BoolDefault(def bool) (bool, error) {
|
||||||
return def, errFindParse.Format("bool", e.Key)
|
return def, errFindParse.Format("bool", e.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vBoolean, ok := v.(bool); ok {
|
switch vv := v.(type) {
|
||||||
return vBoolean, nil
|
case string:
|
||||||
}
|
val, err := strconv.ParseBool(vv)
|
||||||
|
|
||||||
if vString, ok := v.(string); ok {
|
|
||||||
b, err := strconv.ParseBool(vString)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return def, err
|
return def, err
|
||||||
}
|
}
|
||||||
return b, nil
|
return val, nil
|
||||||
}
|
case bool:
|
||||||
|
return vv, nil
|
||||||
if vInt, ok := v.(int); ok {
|
case int:
|
||||||
if vInt == 1 {
|
if vv == 1 {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
|
@ -394,28 +369,39 @@ func (r *Store) SetImmutable(key string, value interface{}) (Entry, bool) {
|
||||||
return r.Save(key, value, true)
|
return r.Save(key, value, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var emptyEntry Entry
|
||||||
|
|
||||||
// GetEntry returns a pointer to the "Entry" found with the given "key"
|
// GetEntry returns a pointer to the "Entry" found with the given "key"
|
||||||
// if nothing found then it returns nil, so be careful with that,
|
// if nothing found then it returns an empty Entry and false.
|
||||||
// it's not supposed to be used by end-developers.
|
func (r *Store) GetEntry(key string) (Entry, bool) {
|
||||||
func (r *Store) GetEntry(key string) *Entry {
|
|
||||||
args := *r
|
args := *r
|
||||||
n := len(args)
|
n := len(args)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
kv := &args[i]
|
if kv := args[i]; kv.Key == key {
|
||||||
if kv.Key == key {
|
return kv, true
|
||||||
return kv
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return emptyEntry, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEntryAt returns the internal Entry of the memstore based on its index,
|
||||||
|
// the stored index by the router.
|
||||||
|
// If not found then it returns a zero Entry and false.
|
||||||
|
func (r *Store) GetEntryAt(index int) (Entry, bool) {
|
||||||
|
args := *r
|
||||||
|
if len(args) > index {
|
||||||
|
return args[index], true
|
||||||
|
}
|
||||||
|
return emptyEntry, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefault returns the entry's value based on its key.
|
// GetDefault returns the entry's value based on its key.
|
||||||
// If not found returns "def".
|
// If not found returns "def".
|
||||||
// This function checks for immutability as well, the rest don't.
|
// This function checks for immutability as well, the rest don't.
|
||||||
func (r *Store) GetDefault(key string, def interface{}) interface{} {
|
func (r *Store) GetDefault(key string, def interface{}) interface{} {
|
||||||
v := r.GetEntry(key)
|
v, ok := r.GetEntry(key)
|
||||||
if v == nil || v.ValueRaw == nil {
|
if !ok || v.ValueRaw == nil {
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
vv := v.Value()
|
vv := v.Value()
|
||||||
|
@ -444,8 +430,8 @@ func (r *Store) Visit(visitor func(key string, value interface{})) {
|
||||||
// GetStringDefault returns the entry's value as string, based on its key.
|
// GetStringDefault returns the entry's value as string, based on its key.
|
||||||
// If not found returns "def".
|
// If not found returns "def".
|
||||||
func (r *Store) GetStringDefault(key string, def string) string {
|
func (r *Store) GetStringDefault(key string, def string) string {
|
||||||
v := r.GetEntry(key)
|
v, ok := r.GetEntry(key)
|
||||||
if v == nil {
|
if !ok {
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,8 +451,8 @@ func (r *Store) GetStringTrim(name string) string {
|
||||||
// GetInt returns the entry's value as int, based on its key.
|
// GetInt returns the entry's value as int, based on its key.
|
||||||
// If not found returns -1 and a non-nil error.
|
// If not found returns -1 and a non-nil error.
|
||||||
func (r *Store) GetInt(key string) (int, error) {
|
func (r *Store) GetInt(key string) (int, error) {
|
||||||
v := r.GetEntry(key)
|
v, ok := r.GetEntry(key)
|
||||||
if v == nil {
|
if !ok {
|
||||||
return 0, errFindParse.Format("int", key)
|
return 0, errFindParse.Format("int", key)
|
||||||
}
|
}
|
||||||
return v.IntDefault(-1)
|
return v.IntDefault(-1)
|
||||||
|
@ -485,8 +471,8 @@ func (r *Store) GetIntDefault(key string, def int) int {
|
||||||
// GetUint8 returns the entry's value as uint8, based on its key.
|
// GetUint8 returns the entry's value as uint8, based on its key.
|
||||||
// If not found returns 0 and a non-nil error.
|
// If not found returns 0 and a non-nil error.
|
||||||
func (r *Store) GetUint8(key string) (uint8, error) {
|
func (r *Store) GetUint8(key string) (uint8, error) {
|
||||||
v := r.GetEntry(key)
|
v, ok := r.GetEntry(key)
|
||||||
if v == nil {
|
if !ok {
|
||||||
return 0, errFindParse.Format("uint8", key)
|
return 0, errFindParse.Format("uint8", key)
|
||||||
}
|
}
|
||||||
return v.Uint8Default(0)
|
return v.Uint8Default(0)
|
||||||
|
@ -505,8 +491,8 @@ func (r *Store) GetUint8Default(key string, def uint8) uint8 {
|
||||||
// GetUint64 returns the entry's value as uint64, based on its key.
|
// GetUint64 returns the entry's value as uint64, based on its key.
|
||||||
// If not found returns 0 and a non-nil error.
|
// If not found returns 0 and a non-nil error.
|
||||||
func (r *Store) GetUint64(key string) (uint64, error) {
|
func (r *Store) GetUint64(key string) (uint64, error) {
|
||||||
v := r.GetEntry(key)
|
v, ok := r.GetEntry(key)
|
||||||
if v == nil {
|
if !ok {
|
||||||
return 0, errFindParse.Format("uint64", key)
|
return 0, errFindParse.Format("uint64", key)
|
||||||
}
|
}
|
||||||
return v.Uint64Default(0)
|
return v.Uint64Default(0)
|
||||||
|
@ -525,8 +511,8 @@ func (r *Store) GetUint64Default(key string, def uint64) uint64 {
|
||||||
// GetInt64 returns the entry's value as int64, based on its key.
|
// GetInt64 returns the entry's value as int64, based on its key.
|
||||||
// If not found returns -1 and a non-nil error.
|
// If not found returns -1 and a non-nil error.
|
||||||
func (r *Store) GetInt64(key string) (int64, error) {
|
func (r *Store) GetInt64(key string) (int64, error) {
|
||||||
v := r.GetEntry(key)
|
v, ok := r.GetEntry(key)
|
||||||
if v == nil {
|
if !ok {
|
||||||
return -1, errFindParse.Format("int64", key)
|
return -1, errFindParse.Format("int64", key)
|
||||||
}
|
}
|
||||||
return v.Int64Default(-1)
|
return v.Int64Default(-1)
|
||||||
|
@ -545,8 +531,8 @@ func (r *Store) GetInt64Default(key string, def int64) int64 {
|
||||||
// GetFloat64 returns the entry's value as float64, based on its key.
|
// GetFloat64 returns the entry's value as float64, based on its key.
|
||||||
// If not found returns -1 and a non nil error.
|
// If not found returns -1 and a non nil error.
|
||||||
func (r *Store) GetFloat64(key string) (float64, error) {
|
func (r *Store) GetFloat64(key string) (float64, error) {
|
||||||
v := r.GetEntry(key)
|
v, ok := r.GetEntry(key)
|
||||||
if v == nil {
|
if !ok {
|
||||||
return -1, errFindParse.Format("float64", key)
|
return -1, errFindParse.Format("float64", key)
|
||||||
}
|
}
|
||||||
return v.Float64Default(-1)
|
return v.Float64Default(-1)
|
||||||
|
@ -569,8 +555,8 @@ func (r *Store) GetFloat64Default(key string, def float64) float64 {
|
||||||
//
|
//
|
||||||
// If not found returns false and a non-nil error.
|
// If not found returns false and a non-nil error.
|
||||||
func (r *Store) GetBool(key string) (bool, error) {
|
func (r *Store) GetBool(key string) (bool, error) {
|
||||||
v := r.GetEntry(key)
|
v, ok := r.GetEntry(key)
|
||||||
if v == nil {
|
if !ok {
|
||||||
return false, errFindParse.Format("bool", key)
|
return false, errFindParse.Format("bool", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ func (r *repository) getAll() []*Route {
|
||||||
// and child routers.
|
// and child routers.
|
||||||
type APIBuilder struct {
|
type APIBuilder struct {
|
||||||
// the api builder global macros registry
|
// the api builder global macros registry
|
||||||
macros *macro.Map
|
macros *macro.Macros
|
||||||
// the api builder global handlers per status code registry (used for custom http errors)
|
// the api builder global handlers per status code registry (used for custom http errors)
|
||||||
errorCodeHandlers *ErrorCodeHandlers
|
errorCodeHandlers *ErrorCodeHandlers
|
||||||
// the api builder global routes repository
|
// the api builder global routes repository
|
||||||
|
@ -116,7 +116,7 @@ var _ RoutesProvider = (*APIBuilder)(nil) // passed to the default request handl
|
||||||
// which is responsible to build the API and the router handler.
|
// which is responsible to build the API and the router handler.
|
||||||
func NewAPIBuilder() *APIBuilder {
|
func NewAPIBuilder() *APIBuilder {
|
||||||
api := &APIBuilder{
|
api := &APIBuilder{
|
||||||
macros: defaultMacros(),
|
macros: macro.Defaults,
|
||||||
errorCodeHandlers: defaultErrorCodeHandlers(),
|
errorCodeHandlers: defaultErrorCodeHandlers(),
|
||||||
reporter: errors.NewReporter(),
|
reporter: errors.NewReporter(),
|
||||||
relativePath: "/",
|
relativePath: "/",
|
||||||
|
@ -246,7 +246,7 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, m := range methods {
|
for _, m := range methods {
|
||||||
route, err = NewRoute(m, subdomain, path, possibleMainHandlerName, routeHandlers, api.macros)
|
route, err = NewRoute(m, subdomain, path, possibleMainHandlerName, routeHandlers, *api.macros)
|
||||||
if err != nil { // template path parser errors:
|
if err != nil { // template path parser errors:
|
||||||
api.reporter.Add("%v -> %s:%s:%s", err, method, subdomain, path)
|
api.reporter.Add("%v -> %s:%s:%s", err, method, subdomain, path)
|
||||||
return nil // fail on first error.
|
return nil // fail on first error.
|
||||||
|
@ -411,11 +411,11 @@ func (api *APIBuilder) WildcardSubdomain(middleware ...context.Handler) Party {
|
||||||
return api.Subdomain(SubdomainWildcardIndicator, middleware...)
|
return api.Subdomain(SubdomainWildcardIndicator, middleware...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Macros returns the macro map which is responsible
|
// Macros returns the macro collection that is responsible
|
||||||
// to register custom macro functions for all routes.
|
// to register custom macros with their own parameter types and their macro functions for all routes.
|
||||||
//
|
//
|
||||||
// Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path
|
// Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path
|
||||||
func (api *APIBuilder) Macros() *macro.Map {
|
func (api *APIBuilder) Macros() *macro.Macros {
|
||||||
return api.macros
|
return api.macros
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,295 +1,15 @@
|
||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kataras/iris/context"
|
"github.com/kataras/iris/context"
|
||||||
"github.com/kataras/iris/core/errors"
|
|
||||||
"github.com/kataras/iris/core/router/macro"
|
"github.com/kataras/iris/core/router/macro"
|
||||||
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defaultMacros returns a new macro map which
|
|
||||||
// contains the default router's named param types functions.
|
|
||||||
func defaultMacros() *macro.Map {
|
|
||||||
macros := macro.NewMap()
|
|
||||||
// registers the String and Int default macro funcs
|
|
||||||
// user can add or override of his own funcs later on
|
|
||||||
// i.e:
|
|
||||||
// app.Macro.String.RegisterFunc("equal", func(eqWith string) func(string) bool {
|
|
||||||
// return func(paramValue string) bool {
|
|
||||||
// return eqWith == paramValue
|
|
||||||
// }})
|
|
||||||
registerBuiltinsMacroFuncs(macros)
|
|
||||||
|
|
||||||
return macros
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerBuiltinsMacroFuncs(out *macro.Map) {
|
|
||||||
// register the String which is the default type if not
|
|
||||||
// parameter type is specified or
|
|
||||||
// if a given parameter into path given but the func doesn't exist on the
|
|
||||||
// parameter type's function list.
|
|
||||||
//
|
|
||||||
// these can be overridden by the user, later on.
|
|
||||||
registerStringMacroFuncs(out.String)
|
|
||||||
registerNumberMacroFuncs(out.Number)
|
|
||||||
registerInt64MacroFuncs(out.Int64)
|
|
||||||
registerUint8MacroFuncs(out.Uint8)
|
|
||||||
registerUint64MacroFuncs(out.Uint64)
|
|
||||||
registerAlphabeticalMacroFuncs(out.Alphabetical)
|
|
||||||
registerFileMacroFuncs(out.File)
|
|
||||||
registerPathMacroFuncs(out.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// String
|
|
||||||
// anything one part
|
|
||||||
func registerStringMacroFuncs(out *macro.Macro) {
|
|
||||||
// this can be used everywhere, it's to help users to define custom regexp expressions
|
|
||||||
// on all macros
|
|
||||||
out.RegisterFunc("regexp", func(expr string) macro.EvaluatorFunc {
|
|
||||||
regexpEvaluator := macro.MustNewEvaluatorFromRegexp(expr)
|
|
||||||
return regexpEvaluator
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if param value starts with the 'prefix' arg
|
|
||||||
out.RegisterFunc("prefix", func(prefix string) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
return strings.HasPrefix(paramValue, prefix)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if param value ends with the 'suffix' arg
|
|
||||||
out.RegisterFunc("suffix", func(suffix string) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
return strings.HasSuffix(paramValue, suffix)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if param value contains the 's' arg
|
|
||||||
out.RegisterFunc("contains", func(s string) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
return strings.Contains(paramValue, s)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if param value's length is at least 'min'
|
|
||||||
out.RegisterFunc("min", func(min int) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
return len(paramValue) >= min
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// checks if param value's length is not bigger than 'max'
|
|
||||||
out.RegisterFunc("max", func(max int) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
return max >= len(paramValue)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Number
|
|
||||||
// positive and negative numbers, number of digits depends on the arch.
|
|
||||||
func registerNumberMacroFuncs(out *macro.Macro) {
|
|
||||||
// checks if the param value's int representation is
|
|
||||||
// bigger or equal than 'min'
|
|
||||||
out.RegisterFunc("min", func(min int) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.Atoi(paramValue)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n >= min
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if the param value's int representation is
|
|
||||||
// smaller or equal than 'max'
|
|
||||||
out.RegisterFunc("max", func(max int) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.Atoi(paramValue)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n <= max
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if the param value's int representation is
|
|
||||||
// between min and max, including 'min' and 'max'
|
|
||||||
out.RegisterFunc("range", func(min, max int) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.Atoi(paramValue)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if n < min || n > max {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int64
|
|
||||||
// -9223372036854775808 to 9223372036854775807.
|
|
||||||
func registerInt64MacroFuncs(out *macro.Macro) {
|
|
||||||
// checks if the param value's int64 representation is
|
|
||||||
// bigger or equal than 'min'
|
|
||||||
out.RegisterFunc("min", func(min int64) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.ParseInt(paramValue, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n >= min
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if the param value's int64 representation is
|
|
||||||
// smaller or equal than 'max'
|
|
||||||
out.RegisterFunc("max", func(max int64) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.ParseInt(paramValue, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n <= max
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if the param value's int64 representation is
|
|
||||||
// between min and max, including 'min' and 'max'
|
|
||||||
out.RegisterFunc("range", func(min, max int64) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.ParseInt(paramValue, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if n < min || n > max {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint8
|
|
||||||
// 0 to 255.
|
|
||||||
func registerUint8MacroFuncs(out *macro.Macro) {
|
|
||||||
// checks if the param value's uint8 representation is
|
|
||||||
// bigger or equal than 'min'
|
|
||||||
out.RegisterFunc("min", func(min uint8) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 8)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return uint8(n) >= min
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if the param value's uint8 representation is
|
|
||||||
// smaller or equal than 'max'
|
|
||||||
out.RegisterFunc("max", func(max uint8) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 8)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return uint8(n) <= max
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if the param value's uint8 representation is
|
|
||||||
// between min and max, including 'min' and 'max'
|
|
||||||
out.RegisterFunc("range", func(min, max uint8) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 8)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := uint8(n); v < min || v > max {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uint64
|
|
||||||
// 0 to 18446744073709551615.
|
|
||||||
func registerUint64MacroFuncs(out *macro.Macro) {
|
|
||||||
// checks if the param value's uint64 representation is
|
|
||||||
// bigger or equal than 'min'
|
|
||||||
out.RegisterFunc("min", func(min uint64) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n >= min
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if the param value's uint64 representation is
|
|
||||||
// smaller or equal than 'max'
|
|
||||||
out.RegisterFunc("max", func(max uint64) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return n <= max
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// checks if the param value's uint64 representation is
|
|
||||||
// between min and max, including 'min' and 'max'
|
|
||||||
out.RegisterFunc("range", func(min, max uint64) macro.EvaluatorFunc {
|
|
||||||
return func(paramValue string) bool {
|
|
||||||
n, err := strconv.ParseUint(paramValue, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if n < min || n > max {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alphabetical
|
|
||||||
// letters only (upper or lowercase)
|
|
||||||
func registerAlphabeticalMacroFuncs(out *macro.Macro) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// File
|
|
||||||
// letters (upper or lowercase)
|
|
||||||
// numbers (0-9)
|
|
||||||
// underscore (_)
|
|
||||||
// dash (-)
|
|
||||||
// point (.)
|
|
||||||
// no spaces! or other character
|
|
||||||
func registerFileMacroFuncs(out *macro.Macro) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Path
|
|
||||||
// File+slashes(anywhere)
|
|
||||||
// should be the latest param, it's the wildcard
|
|
||||||
func registerPathMacroFuncs(out *macro.Macro) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// compileRoutePathAndHandlers receives a route info and returns its parsed/"compiled" path
|
// compileRoutePathAndHandlers receives a route info and returns its parsed/"compiled" path
|
||||||
// and the new handlers (prepend all the macro's handler, if any).
|
// and the new handlers (prepend all the macro's handler, if any).
|
||||||
//
|
//
|
||||||
|
@ -325,9 +45,9 @@ func convertTmplToNodePath(tmpl *macro.Template) (string, error) {
|
||||||
// then the tmpl.Params will be filled,
|
// then the tmpl.Params will be filled,
|
||||||
// so no any further check needed
|
// so no any further check needed
|
||||||
for i, p := range tmpl.Params {
|
for i, p := range tmpl.Params {
|
||||||
if p.Type == ast.ParamTypePath {
|
if ast.IsTrailing(p.Type) {
|
||||||
if i != len(tmpl.Params)-1 {
|
if i != len(tmpl.Params)-1 {
|
||||||
return "", errors.New("parameter type \"ParamTypePath\" should be putted to the very last of a path")
|
return "", fmt.Errorf("parameter type \"%s\" should be putted to the very last of a path", p.Type.Indent())
|
||||||
}
|
}
|
||||||
routePath = strings.Replace(routePath, p.Src, WildcardParam(p.Name), 1)
|
routePath = strings.Replace(routePath, p.Src, WildcardParam(p.Name), 1)
|
||||||
} else {
|
} else {
|
||||||
|
@ -338,7 +58,7 @@ func convertTmplToNodePath(tmpl *macro.Template) (string, error) {
|
||||||
return routePath, nil
|
return routePath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: returns nil if not needed, the caller(router) should be 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
|
needMacroHandler := false
|
||||||
|
@ -347,7 +67,7 @@ func convertTmplToHandler(tmpl *macro.Template) context.Handler {
|
||||||
// 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.
|
||||||
for _, p := range tmpl.Params {
|
for _, p := range tmpl.Params {
|
||||||
if len(p.Funcs) == 0 && (p.Type == ast.ParamTypeUnExpected || p.Type == ast.ParamTypeString || p.Type == ast.ParamTypePath) && p.ErrCode == http.StatusNotFound {
|
if len(p.Funcs) == 0 && (ast.IsMaster(p.Type) || ast.IsTrailing(p.Type)) && p.ErrCode == http.StatusNotFound {
|
||||||
} else {
|
} else {
|
||||||
// println("we need handler for: " + tmpl.Src)
|
// println("we need handler for: " + tmpl.Src)
|
||||||
needMacroHandler = true
|
needMacroHandler = true
|
||||||
|
|
|
@ -1,43 +1,68 @@
|
||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
type (
|
||||||
"reflect"
|
// ParamType holds the necessary information about a parameter type for the parser to lookup for.
|
||||||
"strings"
|
ParamType interface {
|
||||||
|
// The name of the parameter type.
|
||||||
|
// Indent should contain the characters for the parser.
|
||||||
|
Indent() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// MasterParamType if implemented and its `Master()` returns true then empty type param will be translated to this param type.
|
||||||
|
// Also its functions will be available to the rest of the macro param type's funcs.
|
||||||
|
//
|
||||||
|
// Only one Master is allowed.
|
||||||
|
MasterParamType interface {
|
||||||
|
ParamType
|
||||||
|
Master() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrailingParamType if implemented and its `Trailing()` returns true
|
||||||
|
// then it should be declared at the end of a route path and can accept any trailing path segment as one parameter.
|
||||||
|
TrailingParamType interface {
|
||||||
|
ParamType
|
||||||
|
Trailing() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// AliasParamType if implemeneted nad its `Alias()` returns a non-empty string
|
||||||
|
// then the param type can be written with that string literal too.
|
||||||
|
AliasParamType interface {
|
||||||
|
ParamType
|
||||||
|
Alias() string
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParamType holds the necessary information about a parameter type.
|
// IsMaster returns true if the "pt" param type is a master one.
|
||||||
type ParamType struct {
|
func IsMaster(pt ParamType) bool {
|
||||||
Indent string // the name of the parameter type.
|
p, ok := pt.(MasterParamType)
|
||||||
Aliases []string // any aliases, can be empty.
|
return ok && p.Master()
|
||||||
|
|
||||||
GoType reflect.Kind // the go type useful for "mvc" and "hero" bindings.
|
|
||||||
|
|
||||||
Default bool // if true then empty type param will target this and its functions will be available to the rest of the param type's funcs.
|
|
||||||
End bool // if true then it should be declared at the end of a route path and can accept any trailing path segment as one parameter.
|
|
||||||
|
|
||||||
invalid bool // only true if returned by the parser via `LookupParamType`.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParamTypeUnExpected is the unexpected parameter type.
|
// IsTrailing returns true if the "pt" param type is a marked as trailing,
|
||||||
var ParamTypeUnExpected = ParamType{invalid: true}
|
// which should accept more than one path segment when in the end.
|
||||||
|
func IsTrailing(pt ParamType) bool {
|
||||||
func (pt ParamType) String() string {
|
p, ok := pt.(TrailingParamType)
|
||||||
return pt.Indent
|
return ok && p.Trailing()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assignable returns true if the "k" standard type
|
// HasAlias returns any alias of the "pt" param type.
|
||||||
// is assignabled to this ParamType.
|
// If alias is empty or not found then it returns false as its second output argument.
|
||||||
func (pt ParamType) Assignable(k reflect.Kind) bool {
|
func HasAlias(pt ParamType) (string, bool) {
|
||||||
return pt.GoType == k
|
if p, ok := pt.(AliasParamType); ok {
|
||||||
|
alias := p.Alias()
|
||||||
|
return alias, len(alias) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaultParamType accepts a list of ParamType and returns its default.
|
// GetMasterParamType accepts a list of ParamType and returns its master.
|
||||||
// If no `Default` specified:
|
// If no `Master` specified:
|
||||||
// and len(paramTypes) > 0 then it will return the first one,
|
// and len(paramTypes) > 0 then it will return the first one,
|
||||||
// otherwise it returns a "string" parameter type.
|
// otherwise it returns nil.
|
||||||
func GetDefaultParamType(paramTypes ...ParamType) ParamType {
|
func GetMasterParamType(paramTypes ...ParamType) ParamType {
|
||||||
for _, pt := range paramTypes {
|
for _, pt := range paramTypes {
|
||||||
if pt.Default == true {
|
if IsMaster(pt) {
|
||||||
return pt
|
return pt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,24 +71,12 @@ func GetDefaultParamType(paramTypes ...ParamType) ParamType {
|
||||||
return paramTypes[0]
|
return paramTypes[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParamType{Indent: "string", GoType: reflect.String, Default: true}
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
// ValidKind will return true if at least one param type is supported
|
|
||||||
// for this std kind.
|
|
||||||
func ValidKind(k reflect.Kind, paramTypes ...ParamType) bool {
|
|
||||||
for _, pt := range paramTypes {
|
|
||||||
if pt.GoType == k {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupParamType accepts the string
|
// LookupParamType accepts the string
|
||||||
// representation of a parameter type.
|
// representation of a parameter type.
|
||||||
// Available:
|
// Example:
|
||||||
// "string"
|
// "string"
|
||||||
// "number" or "int"
|
// "number" or "int"
|
||||||
// "long" or "int64"
|
// "long" or "int64"
|
||||||
|
@ -73,41 +86,20 @@ func ValidKind(k reflect.Kind, paramTypes ...ParamType) bool {
|
||||||
// "alphabetical"
|
// "alphabetical"
|
||||||
// "file"
|
// "file"
|
||||||
// "path"
|
// "path"
|
||||||
func LookupParamType(indent string, paramTypes ...ParamType) (ParamType, bool) {
|
func LookupParamType(indentOrAlias string, paramTypes ...ParamType) (ParamType, bool) {
|
||||||
for _, pt := range paramTypes {
|
for _, pt := range paramTypes {
|
||||||
if pt.Indent == indent {
|
if pt.Indent() == indentOrAlias {
|
||||||
return pt, true
|
return pt, true
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, alias := range pt.Aliases {
|
if alias, has := HasAlias(pt); has {
|
||||||
if alias == indent {
|
if alias == indentOrAlias {
|
||||||
return pt, true
|
return pt, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParamTypeUnExpected, false
|
return nil, false
|
||||||
}
|
|
||||||
|
|
||||||
// LookupParamTypeFromStd accepts the string representation of a standard go type.
|
|
||||||
// It returns a ParamType, but it may differs for example
|
|
||||||
// the alphabetical, file, path and string are all string go types, so
|
|
||||||
// make sure that caller resolves these types before this call.
|
|
||||||
//
|
|
||||||
// string matches to string
|
|
||||||
// int matches to int/number
|
|
||||||
// int64 matches to int64/long
|
|
||||||
// uint64 matches to uint64
|
|
||||||
// bool matches to bool/boolean
|
|
||||||
func LookupParamTypeFromStd(goType string, paramTypes ...ParamType) (ParamType, bool) {
|
|
||||||
goType = strings.ToLower(goType)
|
|
||||||
for _, pt := range paramTypes {
|
|
||||||
if strings.ToLower(pt.GoType.String()) == goType {
|
|
||||||
return pt, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParamTypeUnExpected, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParamStatement is a struct
|
// ParamStatement is a struct
|
||||||
|
|
|
@ -2,7 +2,6 @@ package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -11,67 +10,12 @@ import (
|
||||||
"github.com/kataras/iris/core/router/macro/interpreter/token"
|
"github.com/kataras/iris/core/router/macro/interpreter/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// paramTypeString is the string type.
|
|
||||||
// If parameter type is missing then it defaults to String type.
|
|
||||||
// Allows anything
|
|
||||||
// Declaration: /mypath/{myparam:string} or {myparam}
|
|
||||||
paramTypeString = ast.ParamType{Indent: "string", GoType: reflect.String, Default: true}
|
|
||||||
// ParamTypeNumber is the integer, a number type.
|
|
||||||
// Allows both positive and negative numbers, any number of digits.
|
|
||||||
// Declaration: /mypath/{myparam:number} or {myparam:int} for backwards-compatibility
|
|
||||||
paramTypeNumber = ast.ParamType{Indent: "number", Aliases: []string{"int"}, GoType: reflect.Int}
|
|
||||||
// ParamTypeInt64 is a number type.
|
|
||||||
// Allows only -9223372036854775808 to 9223372036854775807.
|
|
||||||
// Declaration: /mypath/{myparam:int64} or {myparam:long}
|
|
||||||
paramTypeInt64 = ast.ParamType{Indent: "int64", Aliases: []string{"long"}, GoType: reflect.Int64}
|
|
||||||
// ParamTypeUint8 a number type.
|
|
||||||
// Allows only 0 to 255.
|
|
||||||
// Declaration: /mypath/{myparam:uint8}
|
|
||||||
paramTypeUint8 = ast.ParamType{Indent: "uint8", GoType: reflect.Uint8}
|
|
||||||
// ParamTypeUint64 a number type.
|
|
||||||
// Allows only 0 to 18446744073709551615.
|
|
||||||
// Declaration: /mypath/{myparam:uint64}
|
|
||||||
paramTypeUint64 = ast.ParamType{Indent: "uint64", GoType: reflect.Uint64}
|
|
||||||
// ParamTypeBool is the bool type.
|
|
||||||
// Allows only "1" or "t" or "T" or "TRUE" or "true" or "True"
|
|
||||||
// or "0" or "f" or "F" or "FALSE" or "false" or "False".
|
|
||||||
// Declaration: /mypath/{myparam:bool} or {myparam:boolean}
|
|
||||||
paramTypeBool = ast.ParamType{Indent: "bool", Aliases: []string{"boolean"}, GoType: reflect.Bool}
|
|
||||||
// ParamTypeAlphabetical is the alphabetical/letter type type.
|
|
||||||
// Allows letters only (upper or lowercase)
|
|
||||||
// Declaration: /mypath/{myparam:alphabetical}
|
|
||||||
paramTypeAlphabetical = ast.ParamType{Indent: "alphabetical", GoType: reflect.String}
|
|
||||||
// ParamTypeFile is the file single path type.
|
|
||||||
// Allows:
|
|
||||||
// letters (upper or lowercase)
|
|
||||||
// numbers (0-9)
|
|
||||||
// underscore (_)
|
|
||||||
// dash (-)
|
|
||||||
// point (.)
|
|
||||||
// no spaces! or other character
|
|
||||||
// Declaration: /mypath/{myparam:file}
|
|
||||||
paramTypeFile = ast.ParamType{Indent: "file", GoType: reflect.String}
|
|
||||||
// ParamTypePath is the multi path (or wildcard) type.
|
|
||||||
// Allows anything, should be the last part
|
|
||||||
// Declaration: /mypath/{myparam:path}
|
|
||||||
paramTypePath = ast.ParamType{Indent: "path", GoType: reflect.String, End: true}
|
|
||||||
)
|
|
||||||
|
|
||||||
// DefaultParamTypes are the built'n parameter types.
|
|
||||||
var DefaultParamTypes = []ast.ParamType{
|
|
||||||
paramTypeString,
|
|
||||||
paramTypeNumber, paramTypeInt64, paramTypeUint8, paramTypeUint64,
|
|
||||||
paramTypeBool,
|
|
||||||
paramTypeAlphabetical, paramTypeFile, paramTypePath,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse takes a route "fullpath"
|
// Parse takes a route "fullpath"
|
||||||
// and returns its param statements
|
// and returns its param statements
|
||||||
// and an error on failure.
|
// or an error if failed.
|
||||||
func Parse(fullpath string, paramTypes ...ast.ParamType) ([]*ast.ParamStatement, error) {
|
func Parse(fullpath string, paramTypes []ast.ParamType) ([]*ast.ParamStatement, error) {
|
||||||
if len(paramTypes) == 0 {
|
if len(paramTypes) == 0 {
|
||||||
paramTypes = DefaultParamTypes
|
return nil, fmt.Errorf("empty parameter types")
|
||||||
}
|
}
|
||||||
|
|
||||||
pathParts := strings.SplitN(fullpath, "/", -1)
|
pathParts := strings.SplitN(fullpath, "/", -1)
|
||||||
|
@ -94,7 +38,7 @@ func Parse(fullpath string, paramTypes ...ast.ParamType) ([]*ast.ParamStatement,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// if we have param type path but it's not the last path part
|
// if we have param type path but it's not the last path part
|
||||||
if stmt.Type.End && i < len(pathParts)-1 {
|
if ast.IsTrailing(stmt.Type) && i < len(pathParts)-1 {
|
||||||
return nil, fmt.Errorf("param type '%s' should be lived only inside the last path segment, but was inside: %s", stmt.Type, s)
|
return nil, fmt.Errorf("param type '%s' should be lived only inside the last path segment, but was inside: %s", stmt.Type, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +110,7 @@ func (p *ParamParser) Parse(paramTypes []ast.ParamType) (*ast.ParamStatement, er
|
||||||
|
|
||||||
stmt := &ast.ParamStatement{
|
stmt := &ast.ParamStatement{
|
||||||
ErrorCode: DefaultParamErrorCode,
|
ErrorCode: DefaultParamErrorCode,
|
||||||
Type: ast.GetDefaultParamType(paramTypes...),
|
Type: ast.GetMasterParamType(paramTypes...),
|
||||||
Src: p.src,
|
Src: p.src,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +134,7 @@ func (p *ParamParser) Parse(paramTypes []ast.ParamType) (*ast.ParamStatement, er
|
||||||
// type can accept both letters and numbers but not symbols ofc.
|
// type can accept both letters and numbers but not symbols ofc.
|
||||||
nextTok := l.NextToken()
|
nextTok := l.NextToken()
|
||||||
paramType, found := ast.LookupParamType(nextTok.Literal, paramTypes...)
|
paramType, found := ast.LookupParamType(nextTok.Literal, paramTypes...)
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
p.appendErr("[%d:%d] unexpected parameter type: %s", t.Start, t.End, nextTok.Literal)
|
p.appendErr("[%d:%d] unexpected parameter type: %s", t.Start, t.End, nextTok.Literal)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,44 @@ import (
|
||||||
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type simpleParamType string
|
||||||
|
|
||||||
|
func (pt simpleParamType) Indent() string { return string(pt) }
|
||||||
|
|
||||||
|
type masterParamType simpleParamType
|
||||||
|
|
||||||
|
func (pt masterParamType) Indent() string { return string(pt) }
|
||||||
|
func (pt masterParamType) Master() bool { return true }
|
||||||
|
|
||||||
|
type wildcardParamType string
|
||||||
|
|
||||||
|
func (pt wildcardParamType) Indent() string { return string(pt) }
|
||||||
|
func (pt wildcardParamType) Trailing() bool { return true }
|
||||||
|
|
||||||
|
type aliasedParamType []string
|
||||||
|
|
||||||
|
func (pt aliasedParamType) Indent() string { return string(pt[0]) }
|
||||||
|
func (pt aliasedParamType) Alias() string { return pt[1] }
|
||||||
|
|
||||||
|
var (
|
||||||
|
paramTypeString = masterParamType("string")
|
||||||
|
paramTypeNumber = aliasedParamType{"number", "int"}
|
||||||
|
paramTypeInt64 = aliasedParamType{"int64", "long"}
|
||||||
|
paramTypeUint8 = simpleParamType("uint8")
|
||||||
|
paramTypeUint64 = simpleParamType("uint64")
|
||||||
|
paramTypeBool = aliasedParamType{"bool", "boolean"}
|
||||||
|
paramTypeAlphabetical = simpleParamType("alphabetical")
|
||||||
|
paramTypeFile = simpleParamType("file")
|
||||||
|
paramTypePath = wildcardParamType("path")
|
||||||
|
)
|
||||||
|
|
||||||
|
var testParamTypes = []ast.ParamType{
|
||||||
|
paramTypeString,
|
||||||
|
paramTypeNumber, paramTypeInt64, paramTypeUint8, paramTypeUint64,
|
||||||
|
paramTypeBool,
|
||||||
|
paramTypeAlphabetical, paramTypeFile, paramTypePath,
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseParamError(t *testing.T) {
|
func TestParseParamError(t *testing.T) {
|
||||||
// fail
|
// fail
|
||||||
illegalChar := '$'
|
illegalChar := '$'
|
||||||
|
@ -16,7 +54,7 @@ func TestParseParamError(t *testing.T) {
|
||||||
input := "{id" + string(illegalChar) + "int range(1,5) else 404}"
|
input := "{id" + string(illegalChar) + "int range(1,5) else 404}"
|
||||||
p := NewParamParser(input)
|
p := NewParamParser(input)
|
||||||
|
|
||||||
_, err := p.Parse(DefaultParamTypes)
|
_, err := p.Parse(testParamTypes)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expecting not empty error on input '%s'", input)
|
t.Fatalf("expecting not empty error on input '%s'", input)
|
||||||
|
@ -32,7 +70,7 @@ func TestParseParamError(t *testing.T) {
|
||||||
// success
|
// success
|
||||||
input2 := "{id:uint64 range(1,5) else 404}"
|
input2 := "{id:uint64 range(1,5) else 404}"
|
||||||
p.Reset(input2)
|
p.Reset(input2)
|
||||||
_, err = p.Parse(DefaultParamTypes)
|
_, err = p.Parse(testParamTypes)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expecting empty error on input '%s', but got: %s", input2, err.Error())
|
t.Fatalf("expecting empty error on input '%s', but got: %s", input2, err.Error())
|
||||||
|
@ -42,7 +80,7 @@ func TestParseParamError(t *testing.T) {
|
||||||
|
|
||||||
// mustLookupParamType same as `ast.LookupParamType` but it panics if "indent" does not match with a valid Param Type.
|
// mustLookupParamType same as `ast.LookupParamType` but it panics if "indent" does not match with a valid Param Type.
|
||||||
func mustLookupParamType(indent string) ast.ParamType {
|
func mustLookupParamType(indent string) ast.ParamType {
|
||||||
pt, found := ast.LookupParamType(indent, DefaultParamTypes...)
|
pt, found := ast.LookupParamType(indent, testParamTypes...)
|
||||||
if !found {
|
if !found {
|
||||||
panic("param type '" + indent + "' is not part of the provided param types")
|
panic("param type '" + indent + "' is not part of the provided param types")
|
||||||
}
|
}
|
||||||
|
@ -113,14 +151,14 @@ func TestParseParam(t *testing.T) {
|
||||||
ast.ParamStatement{
|
ast.ParamStatement{
|
||||||
Src: "{myparam_:thisianunexpected}",
|
Src: "{myparam_:thisianunexpected}",
|
||||||
Name: "myparam_",
|
Name: "myparam_",
|
||||||
Type: ast.ParamTypeUnExpected,
|
Type: nil,
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
}}, // 5
|
}}, // 5
|
||||||
{true,
|
{true,
|
||||||
ast.ParamStatement{
|
ast.ParamStatement{
|
||||||
Src: "{myparam2}",
|
Src: "{myparam2}",
|
||||||
Name: "myparam2", // we now allow integers to the parameter names.
|
Name: "myparam2", // we now allow integers to the parameter names.
|
||||||
Type: ast.GetDefaultParamType(DefaultParamTypes...),
|
Type: ast.GetMasterParamType(testParamTypes...),
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
}}, // 6
|
}}, // 6
|
||||||
{true,
|
{true,
|
||||||
|
@ -152,7 +190,7 @@ func TestParseParam(t *testing.T) {
|
||||||
ast.ParamStatement{
|
ast.ParamStatement{
|
||||||
Src: "{id:long else 404}",
|
Src: "{id:long else 404}",
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Type: mustLookupParamType("long"), // backwards-compatible test of LookupParamType.
|
Type: mustLookupParamType("int64"), // backwards-compatible test of LookupParamType.
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
}}, // 10
|
}}, // 10
|
||||||
{true,
|
{true,
|
||||||
|
@ -175,7 +213,7 @@ func TestParseParam(t *testing.T) {
|
||||||
p := new(ParamParser)
|
p := new(ParamParser)
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
p.Reset(tt.expectedStatement.Src)
|
p.Reset(tt.expectedStatement.Src)
|
||||||
resultStmt, err := p.Parse(DefaultParamTypes)
|
resultStmt, err := p.Parse(testParamTypes)
|
||||||
|
|
||||||
if tt.valid && err != nil {
|
if tt.valid && err != nil {
|
||||||
t.Fatalf("tests[%d] - error %s", i, err.Error())
|
t.Fatalf("tests[%d] - error %s", i, err.Error())
|
||||||
|
@ -216,7 +254,7 @@ func TestParse(t *testing.T) {
|
||||||
}}, // 0
|
}}, // 0
|
||||||
{"/admin/{id:uint64 range(1,5)}", true,
|
{"/admin/{id:uint64 range(1,5)}", true,
|
||||||
[]ast.ParamStatement{{
|
[]ast.ParamStatement{{
|
||||||
Src: "{id:uint64 range(1,5)}", // test alternative (backwards-compatibility) "int"
|
Src: "{id:uint64 range(1,5)}",
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Type: paramTypeUint64,
|
Type: paramTypeUint64,
|
||||||
Funcs: []ast.ParamFunc{
|
Funcs: []ast.ParamFunc{
|
||||||
|
@ -260,7 +298,7 @@ func TestParse(t *testing.T) {
|
||||||
[]ast.ParamStatement{{
|
[]ast.ParamStatement{{
|
||||||
Src: "{myparam_:thisianunexpected}",
|
Src: "{myparam_:thisianunexpected}",
|
||||||
Name: "myparam_",
|
Name: "myparam_",
|
||||||
Type: ast.ParamTypeUnExpected,
|
Type: nil,
|
||||||
ErrorCode: 404,
|
ErrorCode: 404,
|
||||||
},
|
},
|
||||||
}}, // 5
|
}}, // 5
|
||||||
|
@ -282,7 +320,7 @@ func TestParse(t *testing.T) {
|
||||||
}}, // 7
|
}}, // 7
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
statements, err := Parse(tt.path)
|
statements, err := Parse(tt.path, testParamTypes)
|
||||||
|
|
||||||
if tt.valid && err != nil {
|
if tt.valid && err != nil {
|
||||||
t.Fatalf("tests[%d] - error %s", i, err.Error())
|
t.Fatalf("tests[%d] - error %s", i, err.Error())
|
||||||
|
|
|
@ -7,8 +7,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// EvaluatorFunc is the signature for both param types and param funcs.
|
// EvaluatorFunc is the signature for both param types and param funcs.
|
||||||
|
@ -108,53 +106,78 @@ func convertBuilderFunc(fn interface{}) ParamEvaluatorBuilder {
|
||||||
|
|
||||||
// try to convert the string literal as we get it from the parser.
|
// try to convert the string literal as we get it from the parser.
|
||||||
var (
|
var (
|
||||||
v interface{}
|
val interface{}
|
||||||
err error
|
|
||||||
|
panicIfErr = func(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("on field index: %d: %v", i, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// try to get the value based on the expected type.
|
// try to get the value based on the expected type.
|
||||||
switch field.Kind() {
|
switch field.Kind() {
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
v, err = strconv.Atoi(arg)
|
v, err := strconv.Atoi(arg)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = v
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
v, err = strconv.ParseInt(arg, 10, 8)
|
v, err := strconv.ParseInt(arg, 10, 8)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = int8(v)
|
||||||
case reflect.Int16:
|
case reflect.Int16:
|
||||||
v, err = strconv.ParseInt(arg, 10, 16)
|
v, err := strconv.ParseInt(arg, 10, 16)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = int16(v)
|
||||||
case reflect.Int32:
|
case reflect.Int32:
|
||||||
v, err = strconv.ParseInt(arg, 10, 32)
|
v, err := strconv.ParseInt(arg, 10, 32)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = int32(v)
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
v, err = strconv.ParseInt(arg, 10, 64)
|
v, err := strconv.ParseInt(arg, 10, 64)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = v
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
v, err = strconv.ParseUint(arg, 10, 8)
|
v, err := strconv.ParseUint(arg, 10, 8)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = uint8(v)
|
||||||
case reflect.Uint16:
|
case reflect.Uint16:
|
||||||
v, err = strconv.ParseUint(arg, 10, 16)
|
v, err := strconv.ParseUint(arg, 10, 16)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = uint16(v)
|
||||||
case reflect.Uint32:
|
case reflect.Uint32:
|
||||||
v, err = strconv.ParseUint(arg, 10, 32)
|
v, err := strconv.ParseUint(arg, 10, 32)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = uint32(v)
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
v, err = strconv.ParseUint(arg, 10, 64)
|
v, err := strconv.ParseUint(arg, 10, 64)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = v
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
v, err = strconv.ParseFloat(arg, 32)
|
v, err := strconv.ParseFloat(arg, 32)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = float32(v)
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
v, err = strconv.ParseFloat(arg, 64)
|
v, err := strconv.ParseFloat(arg, 64)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = v
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
v, err = strconv.ParseBool(arg)
|
v, err := strconv.ParseBool(arg)
|
||||||
|
panicIfErr(err)
|
||||||
|
val = v
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
if len(arg) > 1 {
|
if len(arg) > 1 {
|
||||||
if arg[0] == '[' && arg[len(arg)-1] == ']' {
|
if arg[0] == '[' && arg[len(arg)-1] == ']' {
|
||||||
// it is a single argument but as slice.
|
// it is a single argument but as slice.
|
||||||
v = strings.Split(arg[1:len(arg)-1], ",") // only string slices.
|
val = strings.Split(arg[1:len(arg)-1], ",") // only string slices.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
v = arg
|
val = arg
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
argValue := reflect.ValueOf(val)
|
||||||
panic(fmt.Sprintf("on field index: %d: %v", i, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
argValue := reflect.ValueOf(v)
|
|
||||||
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("fields should have the same type: [%d] expected %s but got %s", i, expected, got))
|
||||||
}
|
}
|
||||||
|
@ -190,6 +213,11 @@ type (
|
||||||
// and it can register param functions
|
// and it can register param functions
|
||||||
// to that macro which maps to a parameter type.
|
// to that macro which maps to a parameter type.
|
||||||
Macro struct {
|
Macro struct {
|
||||||
|
indent string
|
||||||
|
alias string
|
||||||
|
master bool
|
||||||
|
trailing bool
|
||||||
|
|
||||||
Evaluator EvaluatorFunc
|
Evaluator EvaluatorFunc
|
||||||
funcs []ParamFunc
|
funcs []ParamFunc
|
||||||
}
|
}
|
||||||
|
@ -212,19 +240,51 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func newMacro(evaluator EvaluatorFunc) *Macro {
|
// NewMacro creates and returns a Macro that can be used as a registry for
|
||||||
return &Macro{Evaluator: evaluator}
|
// a new customized parameter type and its functions.
|
||||||
|
func NewMacro(indent, alias string, master, trailing bool, evaluator EvaluatorFunc) *Macro {
|
||||||
|
return &Macro{
|
||||||
|
indent: indent,
|
||||||
|
alias: alias,
|
||||||
|
master: master,
|
||||||
|
trailing: trailing,
|
||||||
|
|
||||||
|
Evaluator: evaluator,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Macro) Indent() string {
|
||||||
|
return m.indent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Macro) Alias() string {
|
||||||
|
return m.alias
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Macro) Master() bool {
|
||||||
|
return m.master
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Macro) Trailing() bool {
|
||||||
|
return m.trailing
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (m *Macro) SetParamResolver(fn func(memstore.Entry) interface{}) *Macro {
|
||||||
|
// m.ParamResolver = fn
|
||||||
|
// return m
|
||||||
|
// }
|
||||||
|
|
||||||
// RegisterFunc registers a parameter function
|
// RegisterFunc registers a parameter function
|
||||||
// to that macro.
|
// to that macro.
|
||||||
// Accepts the func name ("range")
|
// Accepts the func name ("range")
|
||||||
// and the function body, which should return an EvaluatorFunc
|
// and the function body, which should return an EvaluatorFunc
|
||||||
// a bool (it will be converted to EvaluatorFunc later on),
|
// a bool (it will be converted to EvaluatorFunc later on),
|
||||||
// i.e RegisterFunc("min", func(minValue int) func(paramValue string) bool){})
|
// i.e RegisterFunc("min", func(minValue int) func(paramValue string) bool){})
|
||||||
func (m *Macro) RegisterFunc(funcName string, fn interface{}) {
|
func (m *Macro) RegisterFunc(funcName string, fn interface{}) *Macro {
|
||||||
fullFn := convertBuilderFunc(fn)
|
fullFn := convertBuilderFunc(fn)
|
||||||
m.registerFunc(funcName, fullFn)
|
m.registerFunc(funcName, fullFn)
|
||||||
|
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Macro) registerFunc(funcName string, fullFn ParamEvaluatorBuilder) {
|
func (m *Macro) registerFunc(funcName string, fullFn ParamEvaluatorBuilder) {
|
||||||
|
@ -256,113 +316,3 @@ func (m *Macro) getFunc(funcName string) ParamEvaluatorBuilder {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map contains the default macros mapped to their types.
|
|
||||||
// This is the manager which is used by the caller to register custom
|
|
||||||
// parameter functions per param-type (String, Int, Long, Boolean, Alphabetical, File, Path).
|
|
||||||
type Map struct {
|
|
||||||
// string type
|
|
||||||
// anything
|
|
||||||
String *Macro
|
|
||||||
|
|
||||||
// int type
|
|
||||||
// both positive and negative numbers, any number of digits.
|
|
||||||
Number *Macro
|
|
||||||
// int64 as int64 type
|
|
||||||
// -9223372036854775808 to 9223372036854775807.
|
|
||||||
Int64 *Macro
|
|
||||||
// uint8 as uint8 type
|
|
||||||
// 0 to 255.
|
|
||||||
Uint8 *Macro
|
|
||||||
// uint64 as uint64 type
|
|
||||||
// 0 to 18446744073709551615.
|
|
||||||
Uint64 *Macro
|
|
||||||
|
|
||||||
// boolean as bool type
|
|
||||||
// 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".
|
|
||||||
Boolean *Macro
|
|
||||||
// alphabetical/letter type
|
|
||||||
// letters only (upper or lowercase)
|
|
||||||
Alphabetical *Macro
|
|
||||||
// file type
|
|
||||||
// letters (upper or lowercase)
|
|
||||||
// numbers (0-9)
|
|
||||||
// underscore (_)
|
|
||||||
// dash (-)
|
|
||||||
// point (.)
|
|
||||||
// no spaces! or other character
|
|
||||||
File *Macro
|
|
||||||
// path type
|
|
||||||
// anything, should be the last part
|
|
||||||
Path *Macro
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMap returns a new macro Map with default
|
|
||||||
// type evaluators.
|
|
||||||
//
|
|
||||||
// Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path
|
|
||||||
func NewMap() *Map {
|
|
||||||
simpleNumberEvalutator := MustNewEvaluatorFromRegexp("^-?[0-9]+$")
|
|
||||||
return &Map{
|
|
||||||
// it allows everything, so no need for a regexp here.
|
|
||||||
String: newMacro(func(string) bool { return true }),
|
|
||||||
Number: newMacro(simpleNumberEvalutator), //"^(-?0\\.[0-9]*[1-9]+[0-9]*$)|(^-?[1-9]+[0-9]*((\\.[0-9]*[1-9]+[0-9]*$)|(\\.[0-9]+)))|(^-?[1-9]+[0-9]*$)|(^0$){1}")), //("^-?[0-9]+$")),
|
|
||||||
Int64: newMacro(func(paramValue string) bool {
|
|
||||||
if !simpleNumberEvalutator(paramValue) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
_, err := strconv.ParseInt(paramValue, 10, 64)
|
|
||||||
// if err == strconv.ErrRange...
|
|
||||||
return err == nil
|
|
||||||
}), //("^-[1-9]|-?[1-9][0-9]{1,14}|-?1000000000000000|-?10000000000000000|-?100000000000000000|-?[1-9]000000000000000000|-?9[0-2]00000000000000000|-?92[0-2]0000000000000000|-?922[0-3]000000000000000|-?9223[0-3]00000000000000|-?92233[0-7]0000000000000|-?922337[0-2]000000000000|-?92233720[0-3]0000000000|-?922337203[0-6]000000000|-?9223372036[0-8]00000000|-?92233720368[0-5]0000000|-?922337203685[0-4]000000|-?9223372036854[0-7]00000|-?92233720368547[0-7]0000|-?922337203685477[0-5]000|-?922337203685477[56]000|[0-9]$")),
|
|
||||||
Uint8: newMacro(MustNewEvaluatorFromRegexp("^([0-9]|[1-8][0-9]|9[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$")),
|
|
||||||
Uint64: newMacro(func(paramValue string) bool {
|
|
||||||
if !simpleNumberEvalutator(paramValue) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
_, err := strconv.ParseUint(paramValue, 10, 64)
|
|
||||||
return err == nil
|
|
||||||
}), //("^[0-9]|[1-9][0-9]{1,14}|1000000000000000|10000000000000000|100000000000000000|1000000000000000000|1[0-8]000000000000000000|18[0-4]00000000000000000|184[0-4]0000000000000000|1844[0-6]000000000000000|18446[0-7]00000000000000|184467[0-4]0000000000000|1844674[0-4]000000000000|184467440[0-7]0000000000|1844674407[0-3]000000000|18446744073[0-7]00000000|1844674407370000000[0-9]|18446744073709[0-5]00000|184467440737095[0-5]0000|1844674407370955[0-2]000$")),
|
|
||||||
Boolean: newMacro(func(paramValue string) bool {
|
|
||||||
// a simple if statement is faster than regex ^(true|false|True|False|t|0|f|FALSE|TRUE)$
|
|
||||||
// in this case.
|
|
||||||
_, err := strconv.ParseBool(paramValue)
|
|
||||||
return err == nil
|
|
||||||
}),
|
|
||||||
Alphabetical: newMacro(MustNewEvaluatorFromRegexp("^[a-zA-Z ]+$")),
|
|
||||||
File: newMacro(MustNewEvaluatorFromRegexp("^[a-zA-Z0-9_.-]*$")),
|
|
||||||
// it allows everything, we have String and Path as different
|
|
||||||
// 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.
|
|
||||||
// Should be the last.
|
|
||||||
Path: newMacro(func(string) bool { return true }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup returns the specific Macro from the map
|
|
||||||
// based on the parameter type.
|
|
||||||
// i.e if ast.ParamTypeNumber then it will return the m.Number.
|
|
||||||
// Returns the m.String if not matched.
|
|
||||||
func (m *Map) Lookup(typ ast.ParamType) *Macro {
|
|
||||||
switch typ {
|
|
||||||
case ast.ParamTypeNumber:
|
|
||||||
return m.Number
|
|
||||||
case ast.ParamTypeInt64:
|
|
||||||
return m.Int64
|
|
||||||
case ast.ParamTypeUint8:
|
|
||||||
return m.Uint8
|
|
||||||
case ast.ParamTypeUint64:
|
|
||||||
return m.Uint64
|
|
||||||
case ast.ParamTypeBoolean:
|
|
||||||
return m.Boolean
|
|
||||||
case ast.ParamTypeAlphabetical:
|
|
||||||
return m.Alphabetical
|
|
||||||
case ast.ParamTypeFile:
|
|
||||||
return m.File
|
|
||||||
case ast.ParamTypePath:
|
|
||||||
return m.Path
|
|
||||||
default:
|
|
||||||
return m.String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -71,8 +71,6 @@ func testEvaluatorRaw(t *testing.T, macroEvaluator *Macro, input string, pass bo
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStringEvaluatorRaw(t *testing.T) {
|
func TestStringEvaluatorRaw(t *testing.T) {
|
||||||
f := NewMap()
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pass bool
|
pass bool
|
||||||
input string
|
input string
|
||||||
|
@ -86,13 +84,11 @@ func TestStringEvaluatorRaw(t *testing.T) {
|
||||||
} // 0
|
} // 0
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, f.String, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, String, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNumberEvaluatorRaw(t *testing.T) {
|
func TestNumberEvaluatorRaw(t *testing.T) {
|
||||||
f := NewMap()
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pass bool
|
pass bool
|
||||||
input string
|
input string
|
||||||
|
@ -111,13 +107,11 @@ func TestNumberEvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, f.Number, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Number, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInt64EvaluatorRaw(t *testing.T) {
|
func TestInt64EvaluatorRaw(t *testing.T) {
|
||||||
f := NewMap()
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pass bool
|
pass bool
|
||||||
input string
|
input string
|
||||||
|
@ -138,13 +132,11 @@ func TestInt64EvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, f.Int64, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Int64, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUint8EvaluatorRaw(t *testing.T) {
|
func TestUint8EvaluatorRaw(t *testing.T) {
|
||||||
f := NewMap()
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pass bool
|
pass bool
|
||||||
input string
|
input string
|
||||||
|
@ -169,13 +161,11 @@ func TestUint8EvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, f.Uint8, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Uint8, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUint64EvaluatorRaw(t *testing.T) {
|
func TestUint64EvaluatorRaw(t *testing.T) {
|
||||||
f := NewMap()
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pass bool
|
pass bool
|
||||||
input string
|
input string
|
||||||
|
@ -196,13 +186,11 @@ func TestUint64EvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, f.Uint64, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Uint64, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAlphabeticalEvaluatorRaw(t *testing.T) {
|
func TestAlphabeticalEvaluatorRaw(t *testing.T) {
|
||||||
f := NewMap()
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pass bool
|
pass bool
|
||||||
input string
|
input string
|
||||||
|
@ -215,13 +203,11 @@ func TestAlphabeticalEvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, f.Alphabetical, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Alphabetical, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileEvaluatorRaw(t *testing.T) {
|
func TestFileEvaluatorRaw(t *testing.T) {
|
||||||
f := NewMap()
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pass bool
|
pass bool
|
||||||
input string
|
input string
|
||||||
|
@ -234,13 +220,11 @@ func TestFileEvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
testEvaluatorRaw(t, f.File, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, File, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathEvaluatorRaw(t *testing.T) {
|
func TestPathEvaluatorRaw(t *testing.T) {
|
||||||
f := NewMap()
|
|
||||||
|
|
||||||
pathTests := []struct {
|
pathTests := []struct {
|
||||||
pass bool
|
pass bool
|
||||||
input string
|
input string
|
||||||
|
@ -254,28 +238,10 @@ func TestPathEvaluatorRaw(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range pathTests {
|
for i, tt := range pathTests {
|
||||||
testEvaluatorRaw(t, f.Path, tt.input, tt.pass, i)
|
testEvaluatorRaw(t, Path, tt.input, tt.pass, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func TestMapRegisterFunc(t *testing.T) {
|
|
||||||
// m := NewMap()
|
|
||||||
// m.String.RegisterFunc("prefix", func(prefix string) EvaluatorFunc {
|
|
||||||
// return func(paramValue string) bool {
|
|
||||||
// return strings.HasPrefix(paramValue, prefix)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
// p, err := Parse("/user/@iris")
|
|
||||||
// if err != nil {
|
|
||||||
// t.Fatalf(err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // p.Params = append(p.)
|
|
||||||
|
|
||||||
// testEvaluatorRaw(t, m.String, p.Src, false, 0)
|
|
||||||
// }
|
|
||||||
|
|
||||||
func TestConvertBuilderFunc(t *testing.T) {
|
func TestConvertBuilderFunc(t *testing.T) {
|
||||||
fn := func(min uint64, slice []string) func(string) bool {
|
fn := func(min uint64, slice []string) func(string) bool {
|
||||||
return func(paramValue string) bool {
|
return func(paramValue string) bool {
|
||||||
|
|
375
core/router/macro/macros.go
Normal file
375
core/router/macro/macros.go
Normal file
|
@ -0,0 +1,375 @@
|
||||||
|
package macro
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// String type
|
||||||
|
// Allows anything (single path segment, as everything except the `Path`).
|
||||||
|
String = NewMacro("string", "", true, false, func(string) bool { return true }).
|
||||||
|
RegisterFunc("regexp", func(expr string) EvaluatorFunc {
|
||||||
|
return MustNewEvaluatorFromRegexp(expr)
|
||||||
|
}).
|
||||||
|
// checks if param value starts with the 'prefix' arg
|
||||||
|
RegisterFunc("prefix", func(prefix string) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
return strings.HasPrefix(paramValue, prefix)
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if param value ends with the 'suffix' arg
|
||||||
|
RegisterFunc("suffix", func(suffix string) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
return strings.HasSuffix(paramValue, suffix)
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if param value contains the 's' arg
|
||||||
|
RegisterFunc("contains", func(s string) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
return strings.Contains(paramValue, s)
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if param value's length is at least 'min'
|
||||||
|
RegisterFunc("min", func(min int) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
return len(paramValue) >= min
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if param value's length is not bigger than 'max'
|
||||||
|
RegisterFunc("max", func(max int) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
return max >= len(paramValue)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
simpleNumberEvalutator = MustNewEvaluatorFromRegexp("^-?[0-9]+$")
|
||||||
|
// Number or int type
|
||||||
|
// both positive and negative numbers, any number of digits.
|
||||||
|
Number = NewMacro("number", "int", false, false, simpleNumberEvalutator).
|
||||||
|
// checks if the param value's int representation is
|
||||||
|
// bigger or equal than 'min'
|
||||||
|
RegisterFunc("min", func(min int) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.Atoi(paramValue)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return n >= min
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if the param value's int representation is
|
||||||
|
// smaller or equal than 'max'.
|
||||||
|
RegisterFunc("max", func(max int) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.Atoi(paramValue)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return n <= max
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if the param value's int representation is
|
||||||
|
// between min and max, including 'min' and 'max'.
|
||||||
|
RegisterFunc("range", func(min, max int) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.Atoi(paramValue)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < min || n > max {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Int64 as int64 type
|
||||||
|
// -9223372036854775808 to 9223372036854775807.
|
||||||
|
Int64 = NewMacro("int64", "long", false, false, func(paramValue string) bool {
|
||||||
|
if !simpleNumberEvalutator(paramValue) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, err := strconv.ParseInt(paramValue, 10, 64)
|
||||||
|
// if err == strconv.ErrRange...
|
||||||
|
return err == nil
|
||||||
|
}).
|
||||||
|
// checks if the param value's int64 representation is
|
||||||
|
// bigger or equal than 'min'.
|
||||||
|
RegisterFunc("min", func(min int64) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseInt(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return n >= min
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if the param value's int64 representation is
|
||||||
|
// smaller or equal than 'max'.
|
||||||
|
RegisterFunc("max", func(max int64) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseInt(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return n <= max
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if the param value's int64 representation is
|
||||||
|
// between min and max, including 'min' and 'max'.
|
||||||
|
RegisterFunc("range", func(min, max int64) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseInt(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < min || n > max {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Uint8 as uint8 type
|
||||||
|
// 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])$")).
|
||||||
|
// checks if the param value's uint8 representation is
|
||||||
|
// bigger or equal than 'min'.
|
||||||
|
RegisterFunc("min", func(min uint8) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseUint(paramValue, 10, 8)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint8(n) >= min
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if the param value's uint8 representation is
|
||||||
|
// smaller or equal than 'max'.
|
||||||
|
RegisterFunc("max", func(max uint8) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseUint(paramValue, 10, 8)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return uint8(n) <= max
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if the param value's uint8 representation is
|
||||||
|
// between min and max, including 'min' and 'max'.
|
||||||
|
RegisterFunc("range", func(min, max uint8) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseUint(paramValue, 10, 8)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := uint8(n); v < min || v > max {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Uint64 as uint64 type
|
||||||
|
// 0 to 18446744073709551615.
|
||||||
|
Uint64 = NewMacro("uint64", "", false, false, func(paramValue string) bool {
|
||||||
|
if !simpleNumberEvalutator(paramValue) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, err := strconv.ParseUint(paramValue, 10, 64)
|
||||||
|
return err == nil
|
||||||
|
}).
|
||||||
|
// checks if the param value's uint64 representation is
|
||||||
|
// bigger or equal than 'min'.
|
||||||
|
RegisterFunc("min", func(min uint64) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseUint(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return n >= min
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if the param value's uint64 representation is
|
||||||
|
// smaller or equal than 'max'.
|
||||||
|
RegisterFunc("max", func(max uint64) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseUint(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return n <= max
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
// checks if the param value's uint64 representation is
|
||||||
|
// between min and max, including 'min' and 'max'.
|
||||||
|
RegisterFunc("range", func(min, max uint64) EvaluatorFunc {
|
||||||
|
return func(paramValue string) bool {
|
||||||
|
n, err := strconv.ParseUint(paramValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < min || n > max {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Bool or boolean as bool type
|
||||||
|
// 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".
|
||||||
|
Bool = NewMacro("bool", "boolean", false, false, func(paramValue string) bool {
|
||||||
|
// a simple if statement is faster than regex ^(true|false|True|False|t|0|f|FALSE|TRUE)$
|
||||||
|
// in this case.
|
||||||
|
_, err := strconv.ParseBool(paramValue)
|
||||||
|
return err == nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Alphabetical letter type
|
||||||
|
// letters only (upper or lowercase)
|
||||||
|
Alphabetical = NewMacro("alphabetical", "", false, false, MustNewEvaluatorFromRegexp("^[a-zA-Z ]+$"))
|
||||||
|
// File type
|
||||||
|
// letters (upper or lowercase)
|
||||||
|
// numbers (0-9)
|
||||||
|
// underscore (_)
|
||||||
|
// dash (-)
|
||||||
|
// point (.)
|
||||||
|
// no spaces! or other character
|
||||||
|
File = NewMacro("file", "", false, false, MustNewEvaluatorFromRegexp("^[a-zA-Z0-9_.-]*$"))
|
||||||
|
// Path type
|
||||||
|
// anything, should be the last part
|
||||||
|
//
|
||||||
|
// It allows everything, we have String and Path as different
|
||||||
|
// 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.
|
||||||
|
// Should be living in the latest path segment of a route path.
|
||||||
|
Path = NewMacro("path", "", false, true, func(string) bool { return true })
|
||||||
|
|
||||||
|
Defaults = &Macros{
|
||||||
|
String,
|
||||||
|
Number,
|
||||||
|
Int64,
|
||||||
|
Uint8,
|
||||||
|
Uint64,
|
||||||
|
Bool,
|
||||||
|
Alphabetical,
|
||||||
|
Path,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Macros []*Macro
|
||||||
|
|
||||||
|
func (ms *Macros) Register(indent, alias string, isMaster, isTrailing bool, evaluator EvaluatorFunc) *Macro {
|
||||||
|
macro := NewMacro(indent, alias, isMaster, isTrailing, evaluator)
|
||||||
|
if ms.register(macro) {
|
||||||
|
return macro
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *Macros) register(macro *Macro) bool {
|
||||||
|
if macro.Indent() == "" || macro.Evaluator == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
cp := *ms
|
||||||
|
|
||||||
|
for _, m := range cp {
|
||||||
|
|
||||||
|
// can't add more than one with the same ast characteristics.
|
||||||
|
if macro.Indent() == m.Indent() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if macro.Alias() == m.Alias() || macro.Alias() == m.Indent() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if macro.Master() && m.Master() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = append(cp, macro)
|
||||||
|
|
||||||
|
*ms = cp
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *Macros) Unregister(indent string) bool {
|
||||||
|
cp := *ms
|
||||||
|
|
||||||
|
for i, m := range cp {
|
||||||
|
if m.Indent() == indent {
|
||||||
|
copy(cp[i:], cp[i+1:])
|
||||||
|
cp[len(cp)-1] = nil
|
||||||
|
cp = cp[:len(cp)-1]
|
||||||
|
|
||||||
|
*ms = cp
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *Macros) Lookup(pt ast.ParamType) *Macro {
|
||||||
|
if m := ms.Get(pt.Indent()); m != nil {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
if alias, has := ast.HasAlias(pt); has {
|
||||||
|
if m := ms.Get(alias); m != nil {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *Macros) Get(indentOrAlias string) *Macro {
|
||||||
|
if indentOrAlias == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range *ms {
|
||||||
|
if m.Indent() == indentOrAlias {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Alias() == indentOrAlias {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *Macros) GetMaster() *Macro {
|
||||||
|
for _, m := range *ms {
|
||||||
|
if m.Master() {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *Macros) GetTrailings() (macros []*Macro) {
|
||||||
|
for _, m := range *ms {
|
||||||
|
if m.Trailing() {
|
||||||
|
macros = append(macros, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ type TemplateParam struct {
|
||||||
// it's useful on host to decide how to convert the path template to specific router's syntax
|
// it's useful on host to decide how to convert the path template to specific router's syntax
|
||||||
Type ast.ParamType `json:"type"`
|
Type ast.ParamType `json:"type"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Index int `json:"index"`
|
||||||
ErrCode int `json:"errCode"`
|
ErrCode int `json:"errCode"`
|
||||||
TypeEvaluator EvaluatorFunc `json:"-"`
|
TypeEvaluator EvaluatorFunc `json:"-"`
|
||||||
Funcs []EvaluatorFunc `json:"-"`
|
Funcs []EvaluatorFunc `json:"-"`
|
||||||
|
@ -34,15 +35,20 @@ type TemplateParam struct {
|
||||||
// and returns a new Template.
|
// and returns a new Template.
|
||||||
// It builds all the parameter functions for that template
|
// It builds all the parameter functions for that template
|
||||||
// and their evaluators, it's the api call that makes use the interpeter's parser -> lexer.
|
// and their evaluators, it's the api call that makes use the interpeter's parser -> lexer.
|
||||||
func Parse(src string, macros *Map) (*Template, error) {
|
func Parse(src string, macros Macros) (*Template, error) {
|
||||||
params, err := parser.Parse(src)
|
types := make([]ast.ParamType, len(macros))
|
||||||
|
for i, m := range macros {
|
||||||
|
types[i] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
params, err := parser.Parse(src, types)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t := new(Template)
|
t := new(Template)
|
||||||
t.Src = src
|
t.Src = src
|
||||||
|
|
||||||
for _, p := range params {
|
for idx, p := range params {
|
||||||
funcMap := macros.Lookup(p.Type)
|
funcMap := macros.Lookup(p.Type)
|
||||||
typEval := funcMap.Evaluator
|
typEval := funcMap.Evaluator
|
||||||
|
|
||||||
|
@ -50,17 +56,23 @@ func Parse(src string, macros *Map) (*Template, error) {
|
||||||
Src: p.Src,
|
Src: p.Src,
|
||||||
Type: p.Type,
|
Type: p.Type,
|
||||||
Name: p.Name,
|
Name: p.Name,
|
||||||
|
Index: idx,
|
||||||
ErrCode: p.ErrorCode,
|
ErrCode: p.ErrorCode,
|
||||||
TypeEvaluator: typEval,
|
TypeEvaluator: typEval,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, paramfn := range p.Funcs {
|
for _, paramfn := range p.Funcs {
|
||||||
tmplFn := funcMap.getFunc(paramfn.Name)
|
tmplFn := funcMap.getFunc(paramfn.Name)
|
||||||
if tmplFn == nil { // if not find on this type, check for String's which is for global funcs too
|
if tmplFn == nil { // if not find on this type, check for Master's which is for global funcs too.
|
||||||
tmplFn = macros.String.getFunc(paramfn.Name)
|
if m := macros.GetMaster(); m != nil {
|
||||||
if tmplFn == nil { // if not found then just skip this param
|
tmplFn = m.getFunc(paramfn.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tmplFn == nil { // if not found then just skip this param.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
evalFn := tmplFn(paramfn.Args)
|
evalFn := tmplFn(paramfn.Args)
|
||||||
if evalFn == nil {
|
if evalFn == nil {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -18,11 +18,11 @@ type Party interface {
|
||||||
GetRelPath() string
|
GetRelPath() string
|
||||||
// GetReporter returns the reporter for adding errors
|
// GetReporter returns the reporter for adding errors
|
||||||
GetReporter() *errors.Reporter
|
GetReporter() *errors.Reporter
|
||||||
// Macros returns the macro map which is responsible
|
// Macros returns the macro collection that is responsible
|
||||||
// to register custom macro functions for all routes.
|
// to register custom macros with their own parameter types and their macro functions for all routes.
|
||||||
//
|
//
|
||||||
// Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path
|
// Learn more at: https://github.com/kataras/iris/tree/master/_examples/routing/dynamic-path
|
||||||
Macros() *macro.Map
|
Macros() *macro.Macros
|
||||||
|
|
||||||
// Party groups routes which may have the same prefix and share same handlers,
|
// Party groups routes which may have the same prefix and share same handlers,
|
||||||
// returns that new rich subrouter.
|
// returns that new rich subrouter.
|
||||||
|
|
|
@ -39,7 +39,7 @@ type Route struct {
|
||||||
// It parses the path based on the "macros",
|
// It parses the path based on the "macros",
|
||||||
// handlers are being changed to validate the macros at serve time, if needed.
|
// handlers are being changed to validate the macros at serve time, if needed.
|
||||||
func NewRoute(method, subdomain, unparsedPath, mainHandlerName string,
|
func NewRoute(method, subdomain, unparsedPath, mainHandlerName string,
|
||||||
handlers context.Handlers, macros *macro.Map) (*Route, error) {
|
handlers context.Handlers, macros macro.Macros) (*Route, error) {
|
||||||
|
|
||||||
tmpl, err := macro.Parse(unparsedPath, macros)
|
tmpl, err := macro.Parse(unparsedPath, macros)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
11
hero/di.go
11
hero/di.go
|
@ -8,6 +8,17 @@ import (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
di.DefaultHijacker = func(fieldOrFuncInput reflect.Type) (*di.BindObject, bool) {
|
di.DefaultHijacker = func(fieldOrFuncInput reflect.Type) (*di.BindObject, bool) {
|
||||||
|
// if IsExpectingStore(fieldOrFuncInput) {
|
||||||
|
// return &di.BindObject{
|
||||||
|
// Type: memstoreTyp,
|
||||||
|
// BindType: di.Dynamic,
|
||||||
|
// ReturnValue: func(ctxValue []reflect.Value) reflect.Value {
|
||||||
|
// // return ctxValue[0].MethodByName("Params").Call(di.EmptyIn)[0]
|
||||||
|
// return ctxValue[0].MethodByName("Params").Call(di.EmptyIn)[0].Field(0) // the Params' memstore.Store.
|
||||||
|
// },
|
||||||
|
// }, true
|
||||||
|
// }
|
||||||
|
|
||||||
if !IsContext(fieldOrFuncInput) {
|
if !IsContext(fieldOrFuncInput) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,9 +132,8 @@ func (s *FuncInjector) addValue(inputIndex int, value reflect.Value) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.IsAssignable(inTyp) {
|
if b.IsAssignable(inTyp) {
|
||||||
// println(inTyp.String() + " is assignable to " + val.Type().String())
|
|
||||||
// fmt.Printf("binded input index: %d for type: %s and value: %v with pointer: %v\n",
|
// fmt.Printf("binded input index: %d for type: %s and value: %v with pointer: %v\n",
|
||||||
// i, b.Type.String(), value.String(), val.Pointer())
|
// i, b.Type.String(), inTyp.String(), inTyp.Pointer())
|
||||||
s.inputs = append(s.inputs, &targetFuncInput{
|
s.inputs = append(s.inputs, &targetFuncInput{
|
||||||
InputIndex: inputIndex,
|
InputIndex: inputIndex,
|
||||||
Object: &b,
|
Object: &b,
|
||||||
|
@ -194,8 +193,8 @@ func (s *FuncInjector) Inject(in *[]reflect.Value, ctx ...reflect.Value) {
|
||||||
args := *in
|
args := *in
|
||||||
for _, input := range s.inputs {
|
for _, input := range s.inputs {
|
||||||
input.Object.Assign(ctx, func(v reflect.Value) {
|
input.Object.Assign(ctx, func(v reflect.Value) {
|
||||||
// fmt.Printf("assign input index: %d for value: %v\n",
|
// fmt.Printf("assign input index: %d for value: %v of type: %s\n",
|
||||||
// input.InputIndex, v.String())
|
// input.InputIndex, v.String(), v.Type().Name())
|
||||||
args[input.InputIndex] = v
|
args[input.InputIndex] = v
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,11 @@ func MakeReturnValue(fn reflect.Value, goodFunc TypeChecker) (func([]reflect.Val
|
||||||
if !v.IsValid() {
|
if !v.IsValid() {
|
||||||
return zeroOutVal
|
return zeroOutVal
|
||||||
}
|
}
|
||||||
|
// if v.String() == "<interface {} Value>" {
|
||||||
|
// println("di/object.go: " + v.String())
|
||||||
|
// // println("di/object.go: because it's interface{} it should be returned as: " + v.Elem().Type().String() + " and its value: " + v.Elem().Interface().(string))
|
||||||
|
// return v.Elem()
|
||||||
|
// }
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ func IsZero(v reflect.Value) bool {
|
||||||
// if can't interface, i.e return value from unexported field or method then return false
|
// if can't interface, i.e return value from unexported field or method then return false
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
zero := reflect.Zero(v.Type())
|
zero := reflect.Zero(v.Type())
|
||||||
return v.Interface() == zero.Interface()
|
return v.Interface() == zero.Interface()
|
||||||
}
|
}
|
||||||
|
@ -62,7 +63,10 @@ func IsZero(v reflect.Value) bool {
|
||||||
// If "v" is a nil pointer, Indirect returns a zero Value.
|
// If "v" is a nil pointer, Indirect returns a zero Value.
|
||||||
// If "v" is not a pointer, Indirect returns v.
|
// If "v" is not a pointer, Indirect returns v.
|
||||||
func IndirectValue(v reflect.Value) reflect.Value {
|
func IndirectValue(v reflect.Value) reflect.Value {
|
||||||
return reflect.Indirect(v)
|
if k := v.Kind(); k == reflect.Ptr { //|| k == reflect.Interface {
|
||||||
|
return v.Elem()
|
||||||
|
}
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValueOf returns the reflect.Value of "o".
|
// ValueOf returns the reflect.Value of "o".
|
||||||
|
@ -123,6 +127,11 @@ func equalTypes(got reflect.Type, expected reflect.Type) bool {
|
||||||
// fmt.Printf("expected interface = %s and got to set on the arg is: %s\n", expected.String(), got.String())
|
// fmt.Printf("expected interface = %s and got to set on the arg is: %s\n", expected.String(), got.String())
|
||||||
return got.Implements(expected)
|
return got.Implements(expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if got.String() == "interface {}" {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +170,6 @@ func lookupFields(elemTyp reflect.Type, skipUnexported bool, parentIndex []int)
|
||||||
|
|
||||||
for i, n := 0, elemTyp.NumField(); i < n; i++ {
|
for i, n := 0, elemTyp.NumField(); i < n; i++ {
|
||||||
f := elemTyp.Field(i)
|
f := elemTyp.Field(i)
|
||||||
|
|
||||||
if IndirectType(f.Type).Kind() == reflect.Struct &&
|
if IndirectType(f.Type).Kind() == reflect.Struct &&
|
||||||
!structFieldIgnored(f) {
|
!structFieldIgnored(f) {
|
||||||
fields = append(fields, lookupFields(f.Type, skipUnexported, append(parentIndex, i))...)
|
fields = append(fields, lookupFields(f.Type, skipUnexported, append(parentIndex, i))...)
|
||||||
|
|
|
@ -5,19 +5,31 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"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"
|
||||||
"github.com/kataras/iris/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var contextTyp = reflect.TypeOf((*context.Context)(nil)).Elem()
|
var (
|
||||||
|
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.
|
||||||
func IsContext(inTyp reflect.Type) bool {
|
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)
|
||||||
|
@ -70,7 +82,7 @@ func makeHandler(handler interface{}, values ...reflect.Value) (context.Handler,
|
||||||
// is invalid when input len and values are not match
|
// is invalid when input len and values are not match
|
||||||
// or their types are not match, we will take look at the
|
// or their types are not match, we will take look at the
|
||||||
// second statement, here we will re-try it
|
// second statement, here we will re-try it
|
||||||
// using binders for path parameters: string, int, int64, bool.
|
// using binders for path parameters: string, int, int64, uint8, uint64, bool and so on.
|
||||||
// We don't have access to the path, so neither to the macros here,
|
// We don't have access to the path, so neither to the macros here,
|
||||||
// but in mvc. So we have to do it here.
|
// but in mvc. So we have to do it here.
|
||||||
if valid = funcInjector.Retry(new(params).resolve); !valid {
|
if valid = funcInjector.Retry(new(params).resolve); !valid {
|
||||||
|
|
|
@ -19,64 +19,8 @@ type params struct {
|
||||||
|
|
||||||
func (p *params) resolve(index int, typ reflect.Type) (reflect.Value, bool) {
|
func (p *params) resolve(index int, typ reflect.Type) (reflect.Value, bool) {
|
||||||
currentParamIndex := p.next
|
currentParamIndex := p.next
|
||||||
v, ok := resolveParam(currentParamIndex, typ)
|
v, ok := context.ParamResolverByKindAndIndex(typ.Kind(), currentParamIndex)
|
||||||
|
|
||||||
p.next = p.next + 1
|
p.next = p.next + 1
|
||||||
return v, ok
|
return v, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveParam(currentParamIndex int, typ reflect.Type) (reflect.Value, bool) {
|
|
||||||
var fn interface{}
|
|
||||||
|
|
||||||
switch typ.Kind() {
|
|
||||||
case reflect.Int:
|
|
||||||
fn = func(ctx context.Context) int {
|
|
||||||
// the second "ok/found" check is not necessary,
|
|
||||||
// because even if the entry didn't found on that "index"
|
|
||||||
// it will return an empty entry which will return the
|
|
||||||
// default value passed from the xDefault(def) because its `ValueRaw` is nil.
|
|
||||||
entry, _ := ctx.Params().GetEntryAt(currentParamIndex)
|
|
||||||
v, _ := entry.IntDefault(0)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
case reflect.Int64:
|
|
||||||
fn = func(ctx context.Context) int64 {
|
|
||||||
entry, _ := ctx.Params().GetEntryAt(currentParamIndex)
|
|
||||||
v, _ := entry.Int64Default(0)
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
case reflect.Uint8:
|
|
||||||
fn = func(ctx context.Context) uint8 {
|
|
||||||
entry, _ := ctx.Params().GetEntryAt(currentParamIndex)
|
|
||||||
v, _ := entry.Uint8Default(0)
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
case reflect.Uint64:
|
|
||||||
fn = func(ctx context.Context) uint64 {
|
|
||||||
entry, _ := ctx.Params().GetEntryAt(currentParamIndex)
|
|
||||||
v, _ := entry.Uint64Default(0)
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
case reflect.Bool:
|
|
||||||
fn = func(ctx context.Context) bool {
|
|
||||||
entry, _ := ctx.Params().GetEntryAt(currentParamIndex)
|
|
||||||
v, _ := entry.BoolDefault(false)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
fn = func(ctx context.Context) string {
|
|
||||||
entry, _ := ctx.Params().GetEntryAt(currentParamIndex)
|
|
||||||
// print(entry.Key + " with index of: ")
|
|
||||||
// print(currentParamIndex)
|
|
||||||
// println(" and value: " + entry.String())
|
|
||||||
return entry.String()
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return reflect.Value{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return reflect.ValueOf(fn), true
|
|
||||||
}
|
|
||||||
|
|
|
@ -247,7 +247,7 @@ func (c *ControllerActivator) parseMethods() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ControllerActivator) parseMethod(m reflect.Method) {
|
func (c *ControllerActivator) parseMethod(m reflect.Method) {
|
||||||
httpMethod, httpPath, err := parseMethod(m, c.isReservedMethod)
|
httpMethod, httpPath, err := parseMethod(*c.router.Macros(), m, c.isReservedMethod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != errSkip {
|
if err != errSkip {
|
||||||
c.addErr(fmt.Errorf("MVC: fail to parse the route path and HTTP method for '%s.%s': %v", c.fullName, m.Name, err))
|
c.addErr(fmt.Errorf("MVC: fail to parse the route path and HTTP method for '%s.%s': %v", c.fullName, m.Name, err))
|
||||||
|
@ -283,7 +283,7 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse a route template which contains the parameters organised.
|
// parse a route template which contains the parameters organised.
|
||||||
tmpl, err := macro.Parse(path, c.router.Macros())
|
tmpl, err := macro.Parse(path, *c.router.Macros())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.addErr(fmt.Errorf("MVC: fail to parse the path for '%s.%s': %v", c.fullName, funcName, err))
|
c.addErr(fmt.Errorf("MVC: fail to parse the path for '%s.%s': %v", c.fullName, funcName, err))
|
||||||
return nil
|
return nil
|
||||||
|
@ -338,6 +338,7 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
|
||||||
}
|
}
|
||||||
|
|
||||||
// fmt.Printf("for %s | values: %s\n", funcName, funcDependencies)
|
// fmt.Printf("for %s | values: %s\n", funcName, funcDependencies)
|
||||||
|
|
||||||
funcInjector := di.Func(m.Func, funcDependencies...)
|
funcInjector := di.Func(m.Func, funcDependencies...)
|
||||||
// fmt.Printf("actual injector's inputs length: %d\n", funcInjector.Length)
|
// fmt.Printf("actual injector's inputs length: %d\n", funcInjector.Length)
|
||||||
if funcInjector.Has {
|
if funcInjector.Has {
|
||||||
|
@ -396,6 +397,11 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
|
||||||
in := make([]reflect.Value, n, n)
|
in := make([]reflect.Value, n, n)
|
||||||
in[0] = ctrl
|
in[0] = ctrl
|
||||||
funcInjector.Inject(&in, ctxValue)
|
funcInjector.Inject(&in, ctxValue)
|
||||||
|
|
||||||
|
// for idxx, inn := range in {
|
||||||
|
// println("controller.go: execution: in.Value = "+inn.String()+" and in.Type = "+inn.Type().Kind().String()+" of index: ", idxx)
|
||||||
|
// }
|
||||||
|
|
||||||
hero.DispatchFuncResult(ctx, call(in))
|
hero.DispatchFuncResult(ctx, call(in))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ type testControllerHandle struct {
|
||||||
func (c *testControllerHandle) BeforeActivation(b BeforeActivation) {
|
func (c *testControllerHandle) BeforeActivation(b BeforeActivation) {
|
||||||
b.Handle("GET", "/histatic", "HiStatic")
|
b.Handle("GET", "/histatic", "HiStatic")
|
||||||
b.Handle("GET", "/hiservice", "HiService")
|
b.Handle("GET", "/hiservice", "HiService")
|
||||||
|
b.Handle("GET", "/hiservice/{ps:string}", "HiServiceBy")
|
||||||
b.Handle("GET", "/hiparam/{ps:string}", "HiParamBy")
|
b.Handle("GET", "/hiparam/{ps:string}", "HiParamBy")
|
||||||
b.Handle("GET", "/hiparamempyinput/{ps:string}", "HiParamEmptyInputBy")
|
b.Handle("GET", "/hiparamempyinput/{ps:string}", "HiParamEmptyInputBy")
|
||||||
}
|
}
|
||||||
|
@ -84,6 +85,10 @@ func (c *testControllerHandle) HiService() string {
|
||||||
return c.Service.Say("hi")
|
return c.Service.Say("hi")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *testControllerHandle) HiServiceBy(v string) string {
|
||||||
|
return c.Service.Say("hi with param: " + v)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *testControllerHandle) HiParamBy(v string) string {
|
func (c *testControllerHandle) HiParamBy(v string) string {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
@ -116,7 +121,8 @@ func TestControllerHandle(t *testing.T) {
|
||||||
// and can be used in a user-defined, dynamic "mvc handler".
|
// and can be used in a user-defined, dynamic "mvc handler".
|
||||||
e.GET("/hiservice").Expect().Status(httptest.StatusOK).
|
e.GET("/hiservice").Expect().Status(httptest.StatusOK).
|
||||||
Body().Equal("service: hi")
|
Body().Equal("service: hi")
|
||||||
|
e.GET("/hiservice/value").Expect().Status(httptest.StatusOK).
|
||||||
|
Body().Equal("service: hi with param: value")
|
||||||
// this worked with a temporary variadic on the resolvemethodfunc which is not
|
// this worked with a temporary variadic on the resolvemethodfunc which is not
|
||||||
// correct design, I should split the path and params with the rest of implementation
|
// correct design, I should split the path and params with the rest of implementation
|
||||||
// in order a simple template.Src can be given.
|
// in order a simple template.Src can be given.
|
||||||
|
|
|
@ -4,11 +4,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/kataras/iris/core/router"
|
"github.com/kataras/iris/core/router"
|
||||||
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
"github.com/kataras/iris/core/router/macro"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -95,47 +96,25 @@ func (l *methodLexer) peekPrev() (w string) {
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
var posWords = map[int]string{
|
|
||||||
0: "",
|
|
||||||
1: "first",
|
|
||||||
2: "second",
|
|
||||||
3: "third",
|
|
||||||
4: "forth",
|
|
||||||
5: "five",
|
|
||||||
6: "sixth",
|
|
||||||
7: "seventh",
|
|
||||||
8: "eighth",
|
|
||||||
9: "ninth",
|
|
||||||
10: "tenth",
|
|
||||||
11: "eleventh",
|
|
||||||
12: "twelfth",
|
|
||||||
13: "thirteenth",
|
|
||||||
14: "fourteenth",
|
|
||||||
15: "fifteenth",
|
|
||||||
16: "sixteenth",
|
|
||||||
17: "seventeenth",
|
|
||||||
18: "eighteenth",
|
|
||||||
19: "nineteenth",
|
|
||||||
20: "twentieth",
|
|
||||||
}
|
|
||||||
|
|
||||||
func genParamKey(argIdx int) string {
|
func genParamKey(argIdx int) string {
|
||||||
return "arg" + posWords[argIdx] // argfirst, argsecond...
|
return "param" + strconv.Itoa(argIdx) // param0, param1, param2...
|
||||||
}
|
}
|
||||||
|
|
||||||
type methodParser struct {
|
type methodParser struct {
|
||||||
lexer *methodLexer
|
lexer *methodLexer
|
||||||
fn reflect.Method
|
fn reflect.Method
|
||||||
|
macros macro.Macros
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseMethod(fn reflect.Method, skipper func(string) bool) (method, path string, err error) {
|
func parseMethod(macros macro.Macros, fn reflect.Method, skipper func(string) bool) (method, path string, err error) {
|
||||||
if skipper(fn.Name) {
|
if skipper(fn.Name) {
|
||||||
return "", "", errSkip
|
return "", "", errSkip
|
||||||
}
|
}
|
||||||
|
|
||||||
p := &methodParser{
|
p := &methodParser{
|
||||||
fn: fn,
|
fn: fn,
|
||||||
lexer: newMethodLexer(fn.Name),
|
lexer: newMethodLexer(fn.Name),
|
||||||
|
macros: macros,
|
||||||
}
|
}
|
||||||
return p.parse()
|
return p.parse()
|
||||||
}
|
}
|
||||||
|
@ -211,34 +190,45 @@ func (p *methodParser) parsePathParam(path string, w string, funcArgPos int) (st
|
||||||
|
|
||||||
var (
|
var (
|
||||||
paramKey = genParamKey(funcArgPos) // argfirst, argsecond...
|
paramKey = genParamKey(funcArgPos) // argfirst, argsecond...
|
||||||
paramType = ast.ParamTypeString // default string
|
m = p.macros.GetMaster() // default (String by-default)
|
||||||
|
trailings = p.macros.GetTrailings()
|
||||||
)
|
)
|
||||||
|
|
||||||
// string, int...
|
// string, int...
|
||||||
goType := typ.In(funcArgPos).Name()
|
goType := typ.In(funcArgPos).Kind()
|
||||||
nextWord := p.lexer.peekNext()
|
nextWord := p.lexer.peekNext()
|
||||||
|
|
||||||
if nextWord == tokenWildcard {
|
if nextWord == tokenWildcard {
|
||||||
p.lexer.skip() // skip the Wildcard word.
|
p.lexer.skip() // skip the Wildcard word.
|
||||||
paramType = ast.ParamTypePath
|
if len(trailings) == 0 {
|
||||||
} else if pType := ast.LookupParamTypeFromStd(goType); pType != ast.ParamTypeUnExpected {
|
return "", 0, errors.New("no trailing path parameter found")
|
||||||
// it's not wildcard, so check base on our available macro types.
|
}
|
||||||
paramType = pType
|
m = trailings[0]
|
||||||
} else {
|
} else {
|
||||||
if typ.NumIn() > funcArgPos {
|
// validMacros := p.macros.LookupForGoType(goType)
|
||||||
// has more input arguments but we are not in the correct
|
|
||||||
// index now, maybe the first argument was an `iris/context.Context`
|
// instead of mapping with a reflect.Kind which has its limitation,
|
||||||
// so retry with the "funcArgPos" incremented.
|
// we map the param types with a go type as a string,
|
||||||
//
|
// so custom structs such as "user" can be mapped to a macro with indent || alias == "user".
|
||||||
// the "funcArgPos" will be updated to the caller as well
|
m = p.macros.Get(strings.ToLower(goType.String()))
|
||||||
// because we return it among the path and the error.
|
|
||||||
return p.parsePathParam(path, w, funcArgPos+1)
|
if m == nil {
|
||||||
|
if typ.NumIn() > funcArgPos {
|
||||||
|
// has more input arguments but we are not in the correct
|
||||||
|
// index now, maybe the first argument was an `iris/context.Context`
|
||||||
|
// so retry with the "funcArgPos" incremented.
|
||||||
|
//
|
||||||
|
// the "funcArgPos" will be updated to the caller as well
|
||||||
|
// because we return it among the path and the error.
|
||||||
|
return p.parsePathParam(path, w, funcArgPos+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", 0, fmt.Errorf("invalid syntax: the standard go type: %s found in controller's function: %s at position: %d does not match any valid macro", goType, p.fn.Name, funcArgPos)
|
||||||
}
|
}
|
||||||
return "", 0, errors.New("invalid syntax for " + p.fn.Name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// /{argfirst:path}, /{argfirst:long}...
|
// /{argfirst:path}, /{argfirst:long}...
|
||||||
path += fmt.Sprintf("/{%s:%s}", paramKey, paramType.String())
|
path += fmt.Sprintf("/{%s:%s}", paramKey, m.Indent())
|
||||||
|
|
||||||
if nextWord == "" && typ.NumIn() > funcArgPos+1 {
|
if nextWord == "" && typ.NumIn() > funcArgPos+1 {
|
||||||
// By is the latest word but func is expected
|
// By is the latest word but func is expected
|
||||||
|
|
86
mvc/param.go
86
mvc/param.go
|
@ -5,7 +5,6 @@ import (
|
||||||
|
|
||||||
"github.com/kataras/iris/context"
|
"github.com/kataras/iris/context"
|
||||||
"github.com/kataras/iris/core/router/macro"
|
"github.com/kataras/iris/core/router/macro"
|
||||||
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getPathParamsForInput(params []macro.TemplateParam, funcIn ...reflect.Type) (values []reflect.Value) {
|
func getPathParamsForInput(params []macro.TemplateParam, funcIn ...reflect.Type) (values []reflect.Value) {
|
||||||
|
@ -13,61 +12,40 @@ func getPathParamsForInput(params []macro.TemplateParam, funcIn ...reflect.Type)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
consumedParams := make(map[int]bool, 0)
|
// consumedParams := make(map[int]bool, 0)
|
||||||
for _, in := range funcIn {
|
// for _, in := range funcIn {
|
||||||
for j, p := range params {
|
// for j, p := range params {
|
||||||
if _, consumed := consumedParams[j]; consumed {
|
// if _, consumed := consumedParams[j]; consumed {
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
paramType := p.Type
|
|
||||||
paramName := p.Name
|
// // fmt.Printf("%s input arg type vs %s param type\n", in.Kind().String(), p.Type.Kind().String())
|
||||||
// fmt.Printf("%s input arg type vs %s param type\n", in.Kind().String(), p.Type.Kind().String())
|
// if m := macros.Lookup(p.Type); m != nil && m.GoType == in.Kind() {
|
||||||
if paramType.Assignable(in.Kind()) {
|
// consumedParams[j] = true
|
||||||
consumedParams[j] = true
|
// // fmt.Printf("param.go: bind path param func for paramName = '%s' and paramType = '%s'\n", paramName, paramType.String())
|
||||||
// fmt.Printf("param.go: bind path param func for paramName = '%s' and paramType = '%s'\n", paramName, paramType.String())
|
// funcDep, ok := context.ParamResolverByKindAndIndex(m.GoType, p.Index)
|
||||||
values = append(values, makeFuncParamGetter(paramType, paramName))
|
// // funcDep, ok := context.ParamResolverByKindAndKey(in.Kind(), paramName)
|
||||||
}
|
// if !ok {
|
||||||
|
// // here we can add a logger about invalid parameter type although it should never happen here
|
||||||
|
// // unless the end-developer modified the macro/macros with a special type but not the context/ParamResolvers.
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// values = append(values, funcDep)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
for i, param := range params {
|
||||||
|
if len(funcIn) <= i {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
funcDep, ok := context.ParamResolverByKindAndIndex(funcIn[i].Kind(), param.Index)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
values = append(values, funcDep)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeFuncParamGetter(paramType ast.ParamType, paramName string) reflect.Value {
|
|
||||||
var fn interface{}
|
|
||||||
|
|
||||||
switch paramType {
|
|
||||||
case ast.ParamTypeNumber:
|
|
||||||
fn = func(ctx context.Context) int {
|
|
||||||
v, _ := ctx.Params().GetInt(paramName)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
case ast.ParamTypeInt64:
|
|
||||||
fn = func(ctx context.Context) int64 {
|
|
||||||
v, _ := ctx.Params().GetInt64(paramName)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
case ast.ParamTypeUint8:
|
|
||||||
fn = func(ctx context.Context) uint8 {
|
|
||||||
v, _ := ctx.Params().GetUint8(paramName)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
case ast.ParamTypeUint64:
|
|
||||||
fn = func(ctx context.Context) uint64 {
|
|
||||||
v, _ := ctx.Params().GetUint64(paramName)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
case ast.ParamTypeBoolean:
|
|
||||||
fn = func(ctx context.Context) bool {
|
|
||||||
v, _ := ctx.Params().GetBool(paramName)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// string, path...
|
|
||||||
fn = func(ctx context.Context) string {
|
|
||||||
return ctx.Params().Get(paramName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return reflect.ValueOf(fn)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user