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.
|
||||
// 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) {
|
||||
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
|
||||
|
||||
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 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 `[...,...]`.
|
||||
|
||||
```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 {
|
||||
for _, validName := range validNames {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// 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) {
|
||||
ctx.handlers = nil // will be filled by router.Serve/HTTP
|
||||
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.currentHandlerIndex = 0
|
||||
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 {
|
||||
return def, errFindParse.Format("int", e.Key)
|
||||
}
|
||||
if vint, ok := v.(int); ok {
|
||||
return vint, nil
|
||||
} else if vstring, sok := v.(string); sok && vstring != "" {
|
||||
vint, err := strconv.Atoi(vstring)
|
||||
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
val, err := strconv.Atoi(vv)
|
||||
if err != nil {
|
||||
return def, err
|
||||
}
|
||||
|
||||
return vint, nil
|
||||
return val, nil
|
||||
case int:
|
||||
return vv, nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if vint64, ok := v.(int64); ok {
|
||||
return vint64, nil
|
||||
}
|
||||
|
||||
if vint, ok := v.(int); ok {
|
||||
return int64(vint), nil
|
||||
}
|
||||
|
||||
if vstring, sok := v.(string); sok {
|
||||
return strconv.ParseInt(vstring, 10, 64)
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
return strconv.ParseInt(vv, 10, 64)
|
||||
case int64:
|
||||
return vv, nil
|
||||
case int:
|
||||
return int64(vv), nil
|
||||
}
|
||||
|
||||
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.
|
||||
func (e Entry) Float64Default(def float64) (float64, error) {
|
||||
v := e.ValueRaw
|
||||
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("float64", e.Key)
|
||||
}
|
||||
|
||||
if vfloat32, ok := v.(float32); ok {
|
||||
return float64(vfloat32), nil
|
||||
}
|
||||
|
||||
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)
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
val, err := strconv.ParseFloat(vv, 64)
|
||||
if err != nil {
|
||||
return def, err
|
||||
}
|
||||
|
||||
return vfloat64, nil
|
||||
return val, 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)
|
||||
|
@ -168,30 +160,24 @@ func (e Entry) Float64Default(def float64) (float64, error) {
|
|||
// If not found returns "def" and a non-nil error.
|
||||
func (e Entry) Float32Default(key string, def float32) (float32, error) {
|
||||
v := e.ValueRaw
|
||||
|
||||
if v == nil {
|
||||
return def, errFindParse.Format("float32", e.Key)
|
||||
}
|
||||
|
||||
if vfloat32, ok := v.(float32); ok {
|
||||
return vfloat32, nil
|
||||
}
|
||||
|
||||
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)
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
val, err := strconv.ParseFloat(vv, 32)
|
||||
if err != nil {
|
||||
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)
|
||||
|
@ -205,26 +191,23 @@ func (e Entry) Uint8Default(def uint8) (uint8, error) {
|
|||
return def, errFindParse.Format("uint8", e.Key)
|
||||
}
|
||||
|
||||
if vuint8, ok := v.(uint8); ok {
|
||||
return vuint8, nil
|
||||
}
|
||||
|
||||
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)
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
val, err := strconv.ParseUint(vv, 10, 8)
|
||||
if err != nil {
|
||||
return def, err
|
||||
}
|
||||
if vuint64 > 255 {
|
||||
if val > 255 {
|
||||
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)
|
||||
|
@ -238,20 +221,15 @@ func (e Entry) Uint64Default(def uint64) (uint64, error) {
|
|||
return def, errFindParse.Format("uint64", e.Key)
|
||||
}
|
||||
|
||||
if vuint64, ok := v.(uint64); ok {
|
||||
return vuint64, nil
|
||||
}
|
||||
|
||||
if vint64, ok := v.(int64); ok {
|
||||
return uint64(vint64), nil
|
||||
}
|
||||
|
||||
if vint, ok := v.(int); ok {
|
||||
return uint64(vint), nil
|
||||
}
|
||||
|
||||
if vstring, sok := v.(string); sok {
|
||||
return strconv.ParseUint(vstring, 10, 64)
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
return strconv.ParseUint(vv, 10, 64)
|
||||
case uint64:
|
||||
return vv, nil
|
||||
case int64:
|
||||
return uint64(vv), nil
|
||||
case int:
|
||||
return uint64(vv), nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if vBoolean, ok := v.(bool); ok {
|
||||
return vBoolean, nil
|
||||
}
|
||||
|
||||
if vString, ok := v.(string); ok {
|
||||
b, err := strconv.ParseBool(vString)
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
val, err := strconv.ParseBool(vv)
|
||||
if err != nil {
|
||||
return def, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
if vInt, ok := v.(int); ok {
|
||||
if vInt == 1 {
|
||||
return val, nil
|
||||
case bool:
|
||||
return vv, nil
|
||||
case int:
|
||||
if vv == 1 {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
|
@ -394,28 +369,39 @@ func (r *Store) SetImmutable(key string, value interface{}) (Entry, bool) {
|
|||
return r.Save(key, value, true)
|
||||
}
|
||||
|
||||
var emptyEntry Entry
|
||||
|
||||
// GetEntry returns a pointer to the "Entry" found with the given "key"
|
||||
// if nothing found then it returns nil, so be careful with that,
|
||||
// it's not supposed to be used by end-developers.
|
||||
func (r *Store) GetEntry(key string) *Entry {
|
||||
// if nothing found then it returns an empty Entry and false.
|
||||
func (r *Store) GetEntry(key string) (Entry, bool) {
|
||||
args := *r
|
||||
n := len(args)
|
||||
for i := 0; i < n; i++ {
|
||||
kv := &args[i]
|
||||
if kv.Key == key {
|
||||
return kv
|
||||
if kv := args[i]; kv.Key == key {
|
||||
return kv, true
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
// If not found returns "def".
|
||||
// This function checks for immutability as well, the rest don't.
|
||||
func (r *Store) GetDefault(key string, def interface{}) interface{} {
|
||||
v := r.GetEntry(key)
|
||||
if v == nil || v.ValueRaw == nil {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok || v.ValueRaw == nil {
|
||||
return def
|
||||
}
|
||||
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.
|
||||
// If not found returns "def".
|
||||
func (r *Store) GetStringDefault(key string, def string) string {
|
||||
v := r.GetEntry(key)
|
||||
if v == nil {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
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.
|
||||
// If not found returns -1 and a non-nil error.
|
||||
func (r *Store) GetInt(key string) (int, error) {
|
||||
v := r.GetEntry(key)
|
||||
if v == nil {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return 0, errFindParse.Format("int", key)
|
||||
}
|
||||
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.
|
||||
// If not found returns 0 and a non-nil error.
|
||||
func (r *Store) GetUint8(key string) (uint8, error) {
|
||||
v := r.GetEntry(key)
|
||||
if v == nil {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return 0, errFindParse.Format("uint8", key)
|
||||
}
|
||||
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.
|
||||
// If not found returns 0 and a non-nil error.
|
||||
func (r *Store) GetUint64(key string) (uint64, error) {
|
||||
v := r.GetEntry(key)
|
||||
if v == nil {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return 0, errFindParse.Format("uint64", key)
|
||||
}
|
||||
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.
|
||||
// If not found returns -1 and a non-nil error.
|
||||
func (r *Store) GetInt64(key string) (int64, error) {
|
||||
v := r.GetEntry(key)
|
||||
if v == nil {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return -1, errFindParse.Format("int64", key)
|
||||
}
|
||||
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.
|
||||
// If not found returns -1 and a non nil error.
|
||||
func (r *Store) GetFloat64(key string) (float64, error) {
|
||||
v := r.GetEntry(key)
|
||||
if v == nil {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return -1, errFindParse.Format("float64", key)
|
||||
}
|
||||
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.
|
||||
func (r *Store) GetBool(key string) (bool, error) {
|
||||
v := r.GetEntry(key)
|
||||
if v == nil {
|
||||
v, ok := r.GetEntry(key)
|
||||
if !ok {
|
||||
return false, errFindParse.Format("bool", key)
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ func (r *repository) getAll() []*Route {
|
|||
// and child routers.
|
||||
type APIBuilder struct {
|
||||
// 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)
|
||||
errorCodeHandlers *ErrorCodeHandlers
|
||||
// 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.
|
||||
func NewAPIBuilder() *APIBuilder {
|
||||
api := &APIBuilder{
|
||||
macros: defaultMacros(),
|
||||
macros: macro.Defaults,
|
||||
errorCodeHandlers: defaultErrorCodeHandlers(),
|
||||
reporter: errors.NewReporter(),
|
||||
relativePath: "/",
|
||||
|
@ -246,7 +246,7 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
|
|||
)
|
||||
|
||||
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:
|
||||
api.reporter.Add("%v -> %s:%s:%s", err, method, subdomain, path)
|
||||
return nil // fail on first error.
|
||||
|
@ -411,11 +411,11 @@ func (api *APIBuilder) WildcardSubdomain(middleware ...context.Handler) Party {
|
|||
return api.Subdomain(SubdomainWildcardIndicator, middleware...)
|
||||
}
|
||||
|
||||
// Macros returns the macro map which is responsible
|
||||
// to register custom macro functions for all routes.
|
||||
// Macros returns the macro collection that is responsible
|
||||
// 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
|
||||
func (api *APIBuilder) Macros() *macro.Map {
|
||||
func (api *APIBuilder) Macros() *macro.Macros {
|
||||
return api.macros
|
||||
}
|
||||
|
||||
|
|
|
@ -1,295 +1,15 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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/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
|
||||
// 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,
|
||||
// so no any further check needed
|
||||
for i, p := range tmpl.Params {
|
||||
if p.Type == ast.ParamTypePath {
|
||||
if ast.IsTrailing(p.Type) {
|
||||
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)
|
||||
} else {
|
||||
|
@ -338,7 +58,7 @@ func convertTmplToNodePath(tmpl *macro.Template) (string, error) {
|
|||
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 {
|
||||
|
||||
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)
|
||||
// 2. if we don't have any named params then we don't need a handler too.
|
||||
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 {
|
||||
// println("we need handler for: " + tmpl.Src)
|
||||
needMacroHandler = true
|
||||
|
|
|
@ -1,43 +1,68 @@
|
|||
package ast
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
type (
|
||||
// ParamType holds the necessary information about a parameter type for the parser to lookup for.
|
||||
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.
|
||||
type ParamType struct {
|
||||
Indent string // the name of the parameter type.
|
||||
Aliases []string // any aliases, can be empty.
|
||||
|
||||
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`.
|
||||
// IsMaster returns true if the "pt" param type is a master one.
|
||||
func IsMaster(pt ParamType) bool {
|
||||
p, ok := pt.(MasterParamType)
|
||||
return ok && p.Master()
|
||||
}
|
||||
|
||||
// ParamTypeUnExpected is the unexpected parameter type.
|
||||
var ParamTypeUnExpected = ParamType{invalid: true}
|
||||
|
||||
func (pt ParamType) String() string {
|
||||
return pt.Indent
|
||||
// IsTrailing returns true if the "pt" param type is a marked as trailing,
|
||||
// which should accept more than one path segment when in the end.
|
||||
func IsTrailing(pt ParamType) bool {
|
||||
p, ok := pt.(TrailingParamType)
|
||||
return ok && p.Trailing()
|
||||
}
|
||||
|
||||
// Assignable returns true if the "k" standard type
|
||||
// is assignabled to this ParamType.
|
||||
func (pt ParamType) Assignable(k reflect.Kind) bool {
|
||||
return pt.GoType == k
|
||||
// HasAlias returns any alias of the "pt" param type.
|
||||
// If alias is empty or not found then it returns false as its second output argument.
|
||||
func HasAlias(pt ParamType) (string, bool) {
|
||||
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.
|
||||
// If no `Default` specified:
|
||||
// GetMasterParamType accepts a list of ParamType and returns its master.
|
||||
// If no `Master` specified:
|
||||
// and len(paramTypes) > 0 then it will return the first one,
|
||||
// otherwise it returns a "string" parameter type.
|
||||
func GetDefaultParamType(paramTypes ...ParamType) ParamType {
|
||||
// otherwise it returns nil.
|
||||
func GetMasterParamType(paramTypes ...ParamType) ParamType {
|
||||
for _, pt := range paramTypes {
|
||||
if pt.Default == true {
|
||||
if IsMaster(pt) {
|
||||
return pt
|
||||
}
|
||||
}
|
||||
|
@ -46,24 +71,12 @@ func GetDefaultParamType(paramTypes ...ParamType) ParamType {
|
|||
return paramTypes[0]
|
||||
}
|
||||
|
||||
return ParamType{Indent: "string", GoType: reflect.String, Default: true}
|
||||
}
|
||||
|
||||
// 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
|
||||
return nil
|
||||
}
|
||||
|
||||
// LookupParamType accepts the string
|
||||
// representation of a parameter type.
|
||||
// Available:
|
||||
// Example:
|
||||
// "string"
|
||||
// "number" or "int"
|
||||
// "long" or "int64"
|
||||
|
@ -73,41 +86,20 @@ func ValidKind(k reflect.Kind, paramTypes ...ParamType) bool {
|
|||
// "alphabetical"
|
||||
// "file"
|
||||
// "path"
|
||||
func LookupParamType(indent string, paramTypes ...ParamType) (ParamType, bool) {
|
||||
func LookupParamType(indentOrAlias string, paramTypes ...ParamType) (ParamType, bool) {
|
||||
for _, pt := range paramTypes {
|
||||
if pt.Indent == indent {
|
||||
if pt.Indent() == indentOrAlias {
|
||||
return pt, true
|
||||
}
|
||||
|
||||
for _, alias := range pt.Aliases {
|
||||
if alias == indent {
|
||||
if alias, has := HasAlias(pt); has {
|
||||
if alias == indentOrAlias {
|
||||
return pt, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ParamTypeUnExpected, 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
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// ParamStatement is a struct
|
||||
|
|
|
@ -2,7 +2,6 @@ package parser
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -11,67 +10,12 @@ import (
|
|||
"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"
|
||||
// and returns its param statements
|
||||
// and an error on failure.
|
||||
func Parse(fullpath string, paramTypes ...ast.ParamType) ([]*ast.ParamStatement, error) {
|
||||
// or an error if failed.
|
||||
func Parse(fullpath string, paramTypes []ast.ParamType) ([]*ast.ParamStatement, error) {
|
||||
if len(paramTypes) == 0 {
|
||||
paramTypes = DefaultParamTypes
|
||||
return nil, fmt.Errorf("empty parameter types")
|
||||
}
|
||||
|
||||
pathParts := strings.SplitN(fullpath, "/", -1)
|
||||
|
@ -94,7 +38,7 @@ func Parse(fullpath string, paramTypes ...ast.ParamType) ([]*ast.ParamStatement,
|
|||
return nil, err
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
@ -166,7 +110,7 @@ func (p *ParamParser) Parse(paramTypes []ast.ParamType) (*ast.ParamStatement, er
|
|||
|
||||
stmt := &ast.ParamStatement{
|
||||
ErrorCode: DefaultParamErrorCode,
|
||||
Type: ast.GetDefaultParamType(paramTypes...),
|
||||
Type: ast.GetMasterParamType(paramTypes...),
|
||||
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.
|
||||
nextTok := l.NextToken()
|
||||
paramType, found := ast.LookupParamType(nextTok.Literal, paramTypes...)
|
||||
|
||||
if !found {
|
||||
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"
|
||||
)
|
||||
|
||||
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) {
|
||||
// fail
|
||||
illegalChar := '$'
|
||||
|
@ -16,7 +54,7 @@ func TestParseParamError(t *testing.T) {
|
|||
input := "{id" + string(illegalChar) + "int range(1,5) else 404}"
|
||||
p := NewParamParser(input)
|
||||
|
||||
_, err := p.Parse(DefaultParamTypes)
|
||||
_, err := p.Parse(testParamTypes)
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("expecting not empty error on input '%s'", input)
|
||||
|
@ -32,7 +70,7 @@ func TestParseParamError(t *testing.T) {
|
|||
// success
|
||||
input2 := "{id:uint64 range(1,5) else 404}"
|
||||
p.Reset(input2)
|
||||
_, err = p.Parse(DefaultParamTypes)
|
||||
_, err = p.Parse(testParamTypes)
|
||||
|
||||
if err != nil {
|
||||
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.
|
||||
func mustLookupParamType(indent string) ast.ParamType {
|
||||
pt, found := ast.LookupParamType(indent, DefaultParamTypes...)
|
||||
pt, found := ast.LookupParamType(indent, testParamTypes...)
|
||||
if !found {
|
||||
panic("param type '" + indent + "' is not part of the provided param types")
|
||||
}
|
||||
|
@ -113,14 +151,14 @@ func TestParseParam(t *testing.T) {
|
|||
ast.ParamStatement{
|
||||
Src: "{myparam_:thisianunexpected}",
|
||||
Name: "myparam_",
|
||||
Type: ast.ParamTypeUnExpected,
|
||||
Type: nil,
|
||||
ErrorCode: 404,
|
||||
}}, // 5
|
||||
{true,
|
||||
ast.ParamStatement{
|
||||
Src: "{myparam2}",
|
||||
Name: "myparam2", // we now allow integers to the parameter names.
|
||||
Type: ast.GetDefaultParamType(DefaultParamTypes...),
|
||||
Type: ast.GetMasterParamType(testParamTypes...),
|
||||
ErrorCode: 404,
|
||||
}}, // 6
|
||||
{true,
|
||||
|
@ -152,7 +190,7 @@ func TestParseParam(t *testing.T) {
|
|||
ast.ParamStatement{
|
||||
Src: "{id:long else 404}",
|
||||
Name: "id",
|
||||
Type: mustLookupParamType("long"), // backwards-compatible test of LookupParamType.
|
||||
Type: mustLookupParamType("int64"), // backwards-compatible test of LookupParamType.
|
||||
ErrorCode: 404,
|
||||
}}, // 10
|
||||
{true,
|
||||
|
@ -175,7 +213,7 @@ func TestParseParam(t *testing.T) {
|
|||
p := new(ParamParser)
|
||||
for i, tt := range tests {
|
||||
p.Reset(tt.expectedStatement.Src)
|
||||
resultStmt, err := p.Parse(DefaultParamTypes)
|
||||
resultStmt, err := p.Parse(testParamTypes)
|
||||
|
||||
if tt.valid && err != nil {
|
||||
t.Fatalf("tests[%d] - error %s", i, err.Error())
|
||||
|
@ -216,7 +254,7 @@ func TestParse(t *testing.T) {
|
|||
}}, // 0
|
||||
{"/admin/{id:uint64 range(1,5)}", true,
|
||||
[]ast.ParamStatement{{
|
||||
Src: "{id:uint64 range(1,5)}", // test alternative (backwards-compatibility) "int"
|
||||
Src: "{id:uint64 range(1,5)}",
|
||||
Name: "id",
|
||||
Type: paramTypeUint64,
|
||||
Funcs: []ast.ParamFunc{
|
||||
|
@ -260,7 +298,7 @@ func TestParse(t *testing.T) {
|
|||
[]ast.ParamStatement{{
|
||||
Src: "{myparam_:thisianunexpected}",
|
||||
Name: "myparam_",
|
||||
Type: ast.ParamTypeUnExpected,
|
||||
Type: nil,
|
||||
ErrorCode: 404,
|
||||
},
|
||||
}}, // 5
|
||||
|
@ -282,7 +320,7 @@ func TestParse(t *testing.T) {
|
|||
}}, // 7
|
||||
}
|
||||
for i, tt := range tests {
|
||||
statements, err := Parse(tt.path)
|
||||
statements, err := Parse(tt.path, testParamTypes)
|
||||
|
||||
if tt.valid && err != nil {
|
||||
t.Fatalf("tests[%d] - error %s", i, err.Error())
|
||||
|
|
|
@ -7,8 +7,6 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
||||
)
|
||||
|
||||
// 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.
|
||||
var (
|
||||
v interface{}
|
||||
err error
|
||||
val interface{}
|
||||
|
||||
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.
|
||||
switch field.Kind() {
|
||||
case reflect.Int:
|
||||
v, err = strconv.Atoi(arg)
|
||||
v, err := strconv.Atoi(arg)
|
||||
panicIfErr(err)
|
||||
val = v
|
||||
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:
|
||||
v, err = strconv.ParseInt(arg, 10, 16)
|
||||
v, err := strconv.ParseInt(arg, 10, 16)
|
||||
panicIfErr(err)
|
||||
val = int16(v)
|
||||
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:
|
||||
v, err = strconv.ParseInt(arg, 10, 64)
|
||||
v, err := strconv.ParseInt(arg, 10, 64)
|
||||
panicIfErr(err)
|
||||
val = v
|
||||
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:
|
||||
v, err = strconv.ParseUint(arg, 10, 16)
|
||||
v, err := strconv.ParseUint(arg, 10, 16)
|
||||
panicIfErr(err)
|
||||
val = uint16(v)
|
||||
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:
|
||||
v, err = strconv.ParseUint(arg, 10, 64)
|
||||
v, err := strconv.ParseUint(arg, 10, 64)
|
||||
panicIfErr(err)
|
||||
val = v
|
||||
case reflect.Float32:
|
||||
v, err = strconv.ParseFloat(arg, 32)
|
||||
v, err := strconv.ParseFloat(arg, 32)
|
||||
panicIfErr(err)
|
||||
val = float32(v)
|
||||
case reflect.Float64:
|
||||
v, err = strconv.ParseFloat(arg, 64)
|
||||
v, err := strconv.ParseFloat(arg, 64)
|
||||
panicIfErr(err)
|
||||
val = v
|
||||
case reflect.Bool:
|
||||
v, err = strconv.ParseBool(arg)
|
||||
v, err := strconv.ParseBool(arg)
|
||||
panicIfErr(err)
|
||||
val = v
|
||||
case reflect.Slice:
|
||||
if len(arg) > 1 {
|
||||
if arg[0] == '[' && arg[len(arg)-1] == ']' {
|
||||
// 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:
|
||||
v = arg
|
||||
val = arg
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("on field index: %d: %v", i, err))
|
||||
}
|
||||
|
||||
argValue := reflect.ValueOf(v)
|
||||
argValue := reflect.ValueOf(val)
|
||||
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))
|
||||
}
|
||||
|
@ -190,6 +213,11 @@ type (
|
|||
// and it can register param functions
|
||||
// to that macro which maps to a parameter type.
|
||||
Macro struct {
|
||||
indent string
|
||||
alias string
|
||||
master bool
|
||||
trailing bool
|
||||
|
||||
Evaluator EvaluatorFunc
|
||||
funcs []ParamFunc
|
||||
}
|
||||
|
@ -212,19 +240,51 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
func newMacro(evaluator EvaluatorFunc) *Macro {
|
||||
return &Macro{Evaluator: evaluator}
|
||||
// NewMacro creates and returns a Macro that can be used as a registry for
|
||||
// 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
|
||||
// to that macro.
|
||||
// Accepts the func name ("range")
|
||||
// and the function body, which should return an EvaluatorFunc
|
||||
// a bool (it will be converted to EvaluatorFunc later on),
|
||||
// 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)
|
||||
m.registerFunc(funcName, fullFn)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Macro) registerFunc(funcName string, fullFn ParamEvaluatorBuilder) {
|
||||
|
@ -256,113 +316,3 @@ func (m *Macro) getFunc(funcName string) ParamEvaluatorBuilder {
|
|||
}
|
||||
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) {
|
||||
f := NewMap()
|
||||
|
||||
tests := []struct {
|
||||
pass bool
|
||||
input string
|
||||
|
@ -86,13 +84,11 @@ func TestStringEvaluatorRaw(t *testing.T) {
|
|||
} // 0
|
||||
|
||||
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) {
|
||||
f := NewMap()
|
||||
|
||||
tests := []struct {
|
||||
pass bool
|
||||
input string
|
||||
|
@ -111,13 +107,11 @@ func TestNumberEvaluatorRaw(t *testing.T) {
|
|||
}
|
||||
|
||||
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) {
|
||||
f := NewMap()
|
||||
|
||||
tests := []struct {
|
||||
pass bool
|
||||
input string
|
||||
|
@ -138,13 +132,11 @@ func TestInt64EvaluatorRaw(t *testing.T) {
|
|||
}
|
||||
|
||||
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) {
|
||||
f := NewMap()
|
||||
|
||||
tests := []struct {
|
||||
pass bool
|
||||
input string
|
||||
|
@ -169,13 +161,11 @@ func TestUint8EvaluatorRaw(t *testing.T) {
|
|||
}
|
||||
|
||||
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) {
|
||||
f := NewMap()
|
||||
|
||||
tests := []struct {
|
||||
pass bool
|
||||
input string
|
||||
|
@ -196,13 +186,11 @@ func TestUint64EvaluatorRaw(t *testing.T) {
|
|||
}
|
||||
|
||||
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) {
|
||||
f := NewMap()
|
||||
|
||||
tests := []struct {
|
||||
pass bool
|
||||
input string
|
||||
|
@ -215,13 +203,11 @@ func TestAlphabeticalEvaluatorRaw(t *testing.T) {
|
|||
}
|
||||
|
||||
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) {
|
||||
f := NewMap()
|
||||
|
||||
tests := []struct {
|
||||
pass bool
|
||||
input string
|
||||
|
@ -234,13 +220,11 @@ func TestFileEvaluatorRaw(t *testing.T) {
|
|||
}
|
||||
|
||||
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) {
|
||||
f := NewMap()
|
||||
|
||||
pathTests := []struct {
|
||||
pass bool
|
||||
input string
|
||||
|
@ -254,28 +238,10 @@ func TestPathEvaluatorRaw(t *testing.T) {
|
|||
}
|
||||
|
||||
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) {
|
||||
fn := func(min uint64, slice []string) func(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
|
||||
Type ast.ParamType `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Index int `json:"index"`
|
||||
ErrCode int `json:"errCode"`
|
||||
TypeEvaluator EvaluatorFunc `json:"-"`
|
||||
Funcs []EvaluatorFunc `json:"-"`
|
||||
|
@ -34,15 +35,20 @@ type TemplateParam struct {
|
|||
// and returns a new 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.
|
||||
func Parse(src string, macros *Map) (*Template, error) {
|
||||
params, err := parser.Parse(src)
|
||||
func Parse(src string, macros Macros) (*Template, error) {
|
||||
types := make([]ast.ParamType, len(macros))
|
||||
for i, m := range macros {
|
||||
types[i] = m
|
||||
}
|
||||
|
||||
params, err := parser.Parse(src, types)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := new(Template)
|
||||
t.Src = src
|
||||
|
||||
for _, p := range params {
|
||||
for idx, p := range params {
|
||||
funcMap := macros.Lookup(p.Type)
|
||||
typEval := funcMap.Evaluator
|
||||
|
||||
|
@ -50,17 +56,23 @@ func Parse(src string, macros *Map) (*Template, error) {
|
|||
Src: p.Src,
|
||||
Type: p.Type,
|
||||
Name: p.Name,
|
||||
Index: idx,
|
||||
ErrCode: p.ErrorCode,
|
||||
TypeEvaluator: typEval,
|
||||
}
|
||||
|
||||
for _, paramfn := range p.Funcs {
|
||||
tmplFn := funcMap.getFunc(paramfn.Name)
|
||||
if tmplFn == nil { // if not find on this type, check for String's which is for global funcs too
|
||||
tmplFn = macros.String.getFunc(paramfn.Name)
|
||||
if tmplFn == nil { // if not found then just skip this param
|
||||
if tmplFn == nil { // if not find on this type, check for Master's which is for global funcs too.
|
||||
if m := macros.GetMaster(); m != nil {
|
||||
tmplFn = m.getFunc(paramfn.Name)
|
||||
}
|
||||
|
||||
if tmplFn == nil { // if not found then just skip this param.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
evalFn := tmplFn(paramfn.Args)
|
||||
if evalFn == nil {
|
||||
continue
|
||||
|
|
|
@ -18,11 +18,11 @@ type Party interface {
|
|||
GetRelPath() string
|
||||
// GetReporter returns the reporter for adding errors
|
||||
GetReporter() *errors.Reporter
|
||||
// Macros returns the macro map which is responsible
|
||||
// to register custom macro functions for all routes.
|
||||
// Macros returns the macro collection that is responsible
|
||||
// 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
|
||||
Macros() *macro.Map
|
||||
Macros() *macro.Macros
|
||||
|
||||
// Party groups routes which may have the same prefix and share same handlers,
|
||||
// returns that new rich subrouter.
|
||||
|
|
|
@ -39,7 +39,7 @@ type Route struct {
|
|||
// It parses the path based on the "macros",
|
||||
// handlers are being changed to validate the macros at serve time, if needed.
|
||||
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)
|
||||
if err != nil {
|
||||
|
|
11
hero/di.go
11
hero/di.go
|
@ -8,6 +8,17 @@ import (
|
|||
|
||||
func init() {
|
||||
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) {
|
||||
return nil, false
|
||||
}
|
||||
|
|
|
@ -132,9 +132,8 @@ func (s *FuncInjector) addValue(inputIndex int, value reflect.Value) bool {
|
|||
}
|
||||
|
||||
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",
|
||||
// i, b.Type.String(), value.String(), val.Pointer())
|
||||
// i, b.Type.String(), inTyp.String(), inTyp.Pointer())
|
||||
s.inputs = append(s.inputs, &targetFuncInput{
|
||||
InputIndex: inputIndex,
|
||||
Object: &b,
|
||||
|
@ -194,8 +193,8 @@ func (s *FuncInjector) Inject(in *[]reflect.Value, ctx ...reflect.Value) {
|
|||
args := *in
|
||||
for _, input := range s.inputs {
|
||||
input.Object.Assign(ctx, func(v reflect.Value) {
|
||||
// fmt.Printf("assign input index: %d for value: %v\n",
|
||||
// input.InputIndex, v.String())
|
||||
// fmt.Printf("assign input index: %d for value: %v of type: %s\n",
|
||||
// input.InputIndex, v.String(), v.Type().Name())
|
||||
args[input.InputIndex] = v
|
||||
})
|
||||
|
||||
|
|
|
@ -101,6 +101,11 @@ func MakeReturnValue(fn reflect.Value, goodFunc TypeChecker) (func([]reflect.Val
|
|||
if !v.IsValid() {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
return false
|
||||
}
|
||||
|
||||
zero := reflect.Zero(v.Type())
|
||||
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 not a pointer, Indirect returns v.
|
||||
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".
|
||||
|
@ -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())
|
||||
return got.Implements(expected)
|
||||
}
|
||||
|
||||
// if got.String() == "interface {}" {
|
||||
// return true
|
||||
// }
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -161,7 +170,6 @@ func lookupFields(elemTyp reflect.Type, skipUnexported bool, parentIndex []int)
|
|||
|
||||
for i, n := 0, elemTyp.NumField(); i < n; i++ {
|
||||
f := elemTyp.Field(i)
|
||||
|
||||
if IndirectType(f.Type).Kind() == reflect.Struct &&
|
||||
!structFieldIgnored(f) {
|
||||
fields = append(fields, lookupFields(f.Type, skipUnexported, append(parentIndex, i))...)
|
||||
|
|
|
@ -5,19 +5,31 @@ import (
|
|||
"reflect"
|
||||
"runtime"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/core/memstore"
|
||||
"github.com/kataras/iris/hero/di"
|
||||
|
||||
"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.
|
||||
func IsContext(inTyp reflect.Type) bool {
|
||||
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).
|
||||
func isContextHandler(handler interface{}) (context.Handler, bool) {
|
||||
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
|
||||
// or their types are not match, we will take look at the
|
||||
// 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,
|
||||
// but in mvc. So we have to do it here.
|
||||
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) {
|
||||
currentParamIndex := p.next
|
||||
v, ok := resolveParam(currentParamIndex, typ)
|
||||
v, ok := context.ParamResolverByKindAndIndex(typ.Kind(), currentParamIndex)
|
||||
|
||||
p.next = p.next + 1
|
||||
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) {
|
||||
httpMethod, httpPath, err := parseMethod(m, c.isReservedMethod)
|
||||
httpMethod, httpPath, err := parseMethod(*c.router.Macros(), m, c.isReservedMethod)
|
||||
if err != nil {
|
||||
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))
|
||||
|
@ -283,7 +283,7 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware .
|
|||
}
|
||||
|
||||
// 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 {
|
||||
c.addErr(fmt.Errorf("MVC: fail to parse the path for '%s.%s': %v", c.fullName, funcName, err))
|
||||
return nil
|
||||
|
@ -338,6 +338,7 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
|
|||
}
|
||||
|
||||
// fmt.Printf("for %s | values: %s\n", funcName, funcDependencies)
|
||||
|
||||
funcInjector := di.Func(m.Func, funcDependencies...)
|
||||
// fmt.Printf("actual injector's inputs length: %d\n", funcInjector.Length)
|
||||
if funcInjector.Has {
|
||||
|
@ -396,6 +397,11 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref
|
|||
in := make([]reflect.Value, n, n)
|
||||
in[0] = ctrl
|
||||
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))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ type testControllerHandle struct {
|
|||
func (c *testControllerHandle) BeforeActivation(b BeforeActivation) {
|
||||
b.Handle("GET", "/histatic", "HiStatic")
|
||||
b.Handle("GET", "/hiservice", "HiService")
|
||||
b.Handle("GET", "/hiservice/{ps:string}", "HiServiceBy")
|
||||
b.Handle("GET", "/hiparam/{ps:string}", "HiParamBy")
|
||||
b.Handle("GET", "/hiparamempyinput/{ps:string}", "HiParamEmptyInputBy")
|
||||
}
|
||||
|
@ -84,6 +85,10 @@ func (c *testControllerHandle) HiService() string {
|
|||
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 {
|
||||
return v
|
||||
}
|
||||
|
@ -116,7 +121,8 @@ func TestControllerHandle(t *testing.T) {
|
|||
// and can be used in a user-defined, dynamic "mvc handler".
|
||||
e.GET("/hiservice").Expect().Status(httptest.StatusOK).
|
||||
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
|
||||
// correct design, I should split the path and params with the rest of implementation
|
||||
// in order a simple template.Src can be given.
|
||||
|
|
|
@ -4,11 +4,12 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/kataras/iris/core/router"
|
||||
"github.com/kataras/iris/core/router/macro/interpreter/ast"
|
||||
"github.com/kataras/iris/core/router/macro"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -95,40 +96,17 @@ func (l *methodLexer) peekPrev() (w string) {
|
|||
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 {
|
||||
return "arg" + posWords[argIdx] // argfirst, argsecond...
|
||||
return "param" + strconv.Itoa(argIdx) // param0, param1, param2...
|
||||
}
|
||||
|
||||
type methodParser struct {
|
||||
lexer *methodLexer
|
||||
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) {
|
||||
return "", "", errSkip
|
||||
}
|
||||
|
@ -136,6 +114,7 @@ func parseMethod(fn reflect.Method, skipper func(string) bool) (method, path str
|
|||
p := &methodParser{
|
||||
fn: fn,
|
||||
lexer: newMethodLexer(fn.Name),
|
||||
macros: macros,
|
||||
}
|
||||
return p.parse()
|
||||
}
|
||||
|
@ -211,20 +190,29 @@ func (p *methodParser) parsePathParam(path string, w string, funcArgPos int) (st
|
|||
|
||||
var (
|
||||
paramKey = genParamKey(funcArgPos) // argfirst, argsecond...
|
||||
paramType = ast.ParamTypeString // default string
|
||||
m = p.macros.GetMaster() // default (String by-default)
|
||||
trailings = p.macros.GetTrailings()
|
||||
)
|
||||
|
||||
// string, int...
|
||||
goType := typ.In(funcArgPos).Name()
|
||||
goType := typ.In(funcArgPos).Kind()
|
||||
nextWord := p.lexer.peekNext()
|
||||
|
||||
if nextWord == tokenWildcard {
|
||||
p.lexer.skip() // skip the Wildcard word.
|
||||
paramType = ast.ParamTypePath
|
||||
} else if pType := ast.LookupParamTypeFromStd(goType); pType != ast.ParamTypeUnExpected {
|
||||
// it's not wildcard, so check base on our available macro types.
|
||||
paramType = pType
|
||||
if len(trailings) == 0 {
|
||||
return "", 0, errors.New("no trailing path parameter found")
|
||||
}
|
||||
m = trailings[0]
|
||||
} else {
|
||||
// validMacros := p.macros.LookupForGoType(goType)
|
||||
|
||||
// instead of mapping with a reflect.Kind which has its limitation,
|
||||
// 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".
|
||||
m = p.macros.Get(strings.ToLower(goType.String()))
|
||||
|
||||
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`
|
||||
|
@ -234,11 +222,13 @@ func (p *methodParser) parsePathParam(path string, w string, funcArgPos int) (st
|
|||
// because we return it among the path and the error.
|
||||
return p.parsePathParam(path, w, funcArgPos+1)
|
||||
}
|
||||
return "", 0, errors.New("invalid syntax for " + p.fn.Name)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// /{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 {
|
||||
// By is the latest word but func is expected
|
||||
|
|
84
mvc/param.go
84
mvc/param.go
|
@ -5,7 +5,6 @@ import (
|
|||
|
||||
"github.com/kataras/iris/context"
|
||||
"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) {
|
||||
|
@ -13,61 +12,40 @@ func getPathParamsForInput(params []macro.TemplateParam, funcIn ...reflect.Type)
|
|||
return
|
||||
}
|
||||
|
||||
consumedParams := make(map[int]bool, 0)
|
||||
for _, in := range funcIn {
|
||||
for j, p := range params {
|
||||
if _, consumed := consumedParams[j]; consumed {
|
||||
// consumedParams := make(map[int]bool, 0)
|
||||
// for _, in := range funcIn {
|
||||
// for j, p := range params {
|
||||
// if _, consumed := consumedParams[j]; consumed {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// // 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() {
|
||||
// consumedParams[j] = true
|
||||
// // 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)
|
||||
// // 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
|
||||
}
|
||||
paramType := p.Type
|
||||
paramName := p.Name
|
||||
// fmt.Printf("%s input arg type vs %s param type\n", in.Kind().String(), p.Type.Kind().String())
|
||||
if paramType.Assignable(in.Kind()) {
|
||||
consumedParams[j] = true
|
||||
// fmt.Printf("param.go: bind path param func for paramName = '%s' and paramType = '%s'\n", paramName, paramType.String())
|
||||
values = append(values, makeFuncParamGetter(paramType, paramName))
|
||||
}
|
||||
}
|
||||
|
||||
values = append(values, funcDep)
|
||||
}
|
||||
|
||||
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