1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-20 19:37:03 +00: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:
Gerasimos (Makis) Maropoulos
2018-09-26 11:37:11 +03:00
parent 52a07df0f4
commit dc3c38b189
26 changed files with 1070 additions and 1036 deletions

View File

@@ -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
}
}