mirror of
https://github.com/kataras/iris.git
synced 2026-01-09 13:05:56 +00:00
New ':int64' and ':uint64' route path parameters - and - support the new uint64 for MVC (int64 was already supported there) - and - add ctx.Params().GetUint64 (GetInt64 was already there) - and - make the ':int or :number' to accept negative numbers with no digit limit (at low level) and rename the 'app.Macros().Int.RegisterFunc' to 'Number.RegisterFunc' because number can be any type of number not only standard go type limited - and - add alias for ':boolean' -> ':bool'. Finally, Update the examples but not the version yet, I have to provide a good README table to explain the end-developers how they can benefit by those changes and why the breaking change (which is to accept negative numbers via ':int') is for their own good and how they can make their own macro functions so they do not depend on the Iris builtn macro funcs only. More to come tomorrow, stay tuned
Former-commit-id: 3601abfc89478185afec3594375080778214283e
This commit is contained in:
@@ -270,10 +270,10 @@ func (api *APIBuilder) Handle(method string, relativePath string, handlers ...co
|
||||
// otherwise use `Party` which can handle many paths with different handlers and middlewares.
|
||||
//
|
||||
// Usage:
|
||||
// app.HandleMany("GET", "/user /user/{id:int} /user/me", genericUserHandler)
|
||||
// app.HandleMany("GET", "/user /user/{id:uint64} /user/me", genericUserHandler)
|
||||
// At the other side, with `Handle` we've had to write:
|
||||
// app.Handle("GET", "/user", userHandler)
|
||||
// app.Handle("GET", "/user/{id:int}", userByIDHandler)
|
||||
// app.Handle("GET", "/user/{id:uint64}", userByIDHandler)
|
||||
// app.Handle("GET", "/user/me", userMeHandler)
|
||||
//
|
||||
// This method is used behind the scenes at the `Controller` function
|
||||
|
||||
@@ -56,7 +56,7 @@ func genPaths(routesLength, minCharLength, maxCharLength int) []string {
|
||||
b.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength))
|
||||
b.WriteString("/{name:string}/") // sugar.
|
||||
b.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength))
|
||||
b.WriteString("/{age:int}/end")
|
||||
b.WriteString("/{age:number}/end")
|
||||
paths[i] = b.String()
|
||||
|
||||
b.Reset()
|
||||
|
||||
@@ -35,8 +35,9 @@ func registerBuiltinsMacroFuncs(out *macro.Map) {
|
||||
//
|
||||
// these can be overridden by the user, later on.
|
||||
registerStringMacroFuncs(out.String)
|
||||
registerIntMacroFuncs(out.Int)
|
||||
registerIntMacroFuncs(out.Long)
|
||||
registerNumberMacroFuncs(out.Number)
|
||||
registerInt64MacroFuncs(out.Int64)
|
||||
registerUint64MacroFuncs(out.Uint64)
|
||||
registerAlphabeticalMacroFuncs(out.Alphabetical)
|
||||
registerFileMacroFuncs(out.File)
|
||||
registerPathMacroFuncs(out.Path)
|
||||
@@ -87,9 +88,9 @@ func registerStringMacroFuncs(out *macro.Macro) {
|
||||
})
|
||||
}
|
||||
|
||||
// Int
|
||||
// only numbers (0-9)
|
||||
func registerIntMacroFuncs(out *macro.Macro) {
|
||||
// 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 {
|
||||
@@ -131,6 +132,94 @@ func registerIntMacroFuncs(out *macro.Macro) {
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
@@ -16,20 +16,27 @@ const (
|
||||
// ParamTypeString is the string type.
|
||||
// If parameter type is missing then it defaults to String type.
|
||||
// Allows anything
|
||||
// Declaration: /mypath/{myparam:string} or /mypath{myparam}
|
||||
// Declaration: /mypath/{myparam:string} or {myparam}
|
||||
ParamTypeString
|
||||
// ParamTypeInt is the integer, a number type.
|
||||
// Allows only positive numbers (0-9)
|
||||
// Declaration: /mypath/{myparam:int}
|
||||
ParamTypeInt
|
||||
// ParamTypeLong is the integer, a number type.
|
||||
// Allows only positive numbers (0-9)
|
||||
// Declaration: /mypath/{myparam:long}
|
||||
ParamTypeLong
|
||||
|
||||
// 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
|
||||
|
||||
// ParamTypeInt64 is a number type.
|
||||
// Allows only -9223372036854775808 to 9223372036854775807.
|
||||
// Declaration: /mypath/{myparam:int64} or {myparam:long}
|
||||
ParamTypeInt64
|
||||
// ParamTypeUint64 a number type.
|
||||
// Allows only 0 to 18446744073709551615.
|
||||
// Declaration: /mypath/{myparam:uint64}
|
||||
ParamTypeUint64
|
||||
|
||||
// ParamTypeBoolean 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:boolean}
|
||||
// Declaration: /mypath/{myparam:bool} or {myparam:boolean}
|
||||
ParamTypeBoolean
|
||||
// ParamTypeAlphabetical is the alphabetical/letter type type.
|
||||
// Allows letters only (upper or lowercase)
|
||||
@@ -79,10 +86,12 @@ func (pt ParamType) Kind() reflect.Kind {
|
||||
fallthrough
|
||||
case ParamTypeString:
|
||||
return reflect.String
|
||||
case ParamTypeInt:
|
||||
case ParamTypeNumber:
|
||||
return reflect.Int
|
||||
case ParamTypeLong:
|
||||
case ParamTypeInt64:
|
||||
return reflect.Int64
|
||||
case ParamTypeUint64:
|
||||
return reflect.Uint64
|
||||
case ParamTypeBoolean:
|
||||
return reflect.Bool
|
||||
}
|
||||
@@ -99,6 +108,8 @@ func ValidKind(k reflect.Kind) bool {
|
||||
fallthrough
|
||||
case reflect.Int64:
|
||||
fallthrough
|
||||
case reflect.Uint64:
|
||||
fallthrough
|
||||
case reflect.Bool:
|
||||
return true
|
||||
default:
|
||||
@@ -113,10 +124,17 @@ func (pt ParamType) Assignable(k reflect.Kind) bool {
|
||||
}
|
||||
|
||||
var paramTypes = map[string]ParamType{
|
||||
"string": ParamTypeString,
|
||||
"int": ParamTypeInt,
|
||||
"long": ParamTypeLong,
|
||||
"boolean": ParamTypeBoolean,
|
||||
"string": ParamTypeString,
|
||||
|
||||
"number": ParamTypeNumber,
|
||||
"int": ParamTypeNumber, // same as number.
|
||||
"long": ParamTypeInt64,
|
||||
"int64": ParamTypeInt64, // same as long.
|
||||
"uint64": ParamTypeUint64,
|
||||
|
||||
"boolean": ParamTypeBoolean,
|
||||
"bool": ParamTypeBoolean, // same as boolean.
|
||||
|
||||
"alphabetical": ParamTypeAlphabetical,
|
||||
"file": ParamTypeFile,
|
||||
"path": ParamTypePath,
|
||||
@@ -131,8 +149,10 @@ var paramTypes = map[string]ParamType{
|
||||
// representation of a parameter type.
|
||||
// Available:
|
||||
// "string"
|
||||
// "int"
|
||||
// "long"
|
||||
// "number" or "int"
|
||||
// "long" or "int64"
|
||||
// "uint64"
|
||||
// "boolean" or "bool"
|
||||
// "alphabetical"
|
||||
// "file"
|
||||
// "path"
|
||||
@@ -149,17 +169,20 @@ func LookupParamType(ident string) ParamType {
|
||||
// make sure that caller resolves these types before this call.
|
||||
//
|
||||
// string matches to string
|
||||
// int matches to int
|
||||
// int64 matches to long
|
||||
// bool matches to boolean
|
||||
// int matches to int/number
|
||||
// int64 matches to int64/long
|
||||
// uint64 matches to uint64
|
||||
// bool matches to bool/boolean
|
||||
func LookupParamTypeFromStd(goType string) ParamType {
|
||||
switch goType {
|
||||
case "string":
|
||||
return ParamTypeString
|
||||
case "int":
|
||||
return ParamTypeInt
|
||||
return ParamTypeNumber
|
||||
case "int64":
|
||||
return ParamTypeLong
|
||||
return ParamTypeInt64
|
||||
case "uint64":
|
||||
return ParamTypeUint64
|
||||
case "bool":
|
||||
return ParamTypeBoolean
|
||||
default:
|
||||
|
||||
@@ -179,7 +179,7 @@ func (l *Lexer) skipWhitespace() {
|
||||
|
||||
func (l *Lexer) readIdentifier() string {
|
||||
pos := l.pos
|
||||
for isLetter(l.ch) {
|
||||
for isLetter(l.ch) || isDigit(l.ch) {
|
||||
l.readChar()
|
||||
}
|
||||
return l.input[pos:l.pos]
|
||||
|
||||
@@ -7,27 +7,27 @@ import (
|
||||
)
|
||||
|
||||
func TestNextToken(t *testing.T) {
|
||||
input := `{id:int min(1) max(5) else 404}`
|
||||
input := `{id:number min(1) max(5) else 404}`
|
||||
|
||||
tests := []struct {
|
||||
expectedType token.Type
|
||||
expectedLiteral string
|
||||
}{
|
||||
{token.LBRACE, "{"}, // 0
|
||||
{token.IDENT, "id"}, // 1
|
||||
{token.COLON, ":"}, // 2
|
||||
{token.IDENT, "int"}, // 3
|
||||
{token.IDENT, "min"}, // 4
|
||||
{token.LPAREN, "("}, // 5
|
||||
{token.INT, "1"}, // 6
|
||||
{token.RPAREN, ")"}, // 7
|
||||
{token.IDENT, "max"}, // 8
|
||||
{token.LPAREN, "("}, // 9
|
||||
{token.INT, "5"}, // 10
|
||||
{token.RPAREN, ")"}, // 11
|
||||
{token.ELSE, "else"}, // 12
|
||||
{token.INT, "404"}, // 13
|
||||
{token.RBRACE, "}"}, // 14
|
||||
{token.LBRACE, "{"}, // 0
|
||||
{token.IDENT, "id"}, // 1
|
||||
{token.COLON, ":"}, // 2
|
||||
{token.IDENT, "number"}, // 3
|
||||
{token.IDENT, "min"}, // 4
|
||||
{token.LPAREN, "("}, // 5
|
||||
{token.INT, "1"}, // 6
|
||||
{token.RPAREN, ")"}, // 7
|
||||
{token.IDENT, "max"}, // 8
|
||||
{token.LPAREN, "("}, // 9
|
||||
{token.INT, "5"}, // 10
|
||||
{token.RPAREN, ")"}, // 11
|
||||
{token.ELSE, "else"}, // 12
|
||||
{token.INT, "404"}, // 13
|
||||
{token.RBRACE, "}"}, // 14
|
||||
}
|
||||
|
||||
l := New(input)
|
||||
|
||||
@@ -120,11 +120,11 @@ func (p *ParamParser) Parse() (*ast.ParamStatement, error) {
|
||||
|
||||
switch t.Type {
|
||||
case token.LBRACE:
|
||||
// name, alphabetical and _, param names are not allowed to contain any number.
|
||||
// can accept only letter or number only.
|
||||
nextTok := l.NextToken()
|
||||
stmt.Name = nextTok.Literal
|
||||
case token.COLON:
|
||||
// type
|
||||
// type can accept both letters and numbers but not symbols ofc.
|
||||
nextTok := l.NextToken()
|
||||
paramType := ast.LookupParamType(nextTok.Literal)
|
||||
if paramType == ast.ParamTypeUnExpected {
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestParseParamError(t *testing.T) {
|
||||
//
|
||||
|
||||
// success
|
||||
input2 := "{id:int range(1,5) else 404}"
|
||||
input2 := "{id:uint64 range(1,5) else 404}"
|
||||
p.Reset(input2)
|
||||
_, err = p.Parse()
|
||||
|
||||
@@ -47,9 +47,9 @@ func TestParseParam(t *testing.T) {
|
||||
}{
|
||||
{true,
|
||||
ast.ParamStatement{
|
||||
Src: "{id:int min(1) max(5) else 404}",
|
||||
Src: "{id:number min(1) max(5) else 404}",
|
||||
Name: "id",
|
||||
Type: ast.ParamTypeInt,
|
||||
Type: ast.ParamTypeNumber,
|
||||
Funcs: []ast.ParamFunc{
|
||||
{
|
||||
Name: "min",
|
||||
@@ -63,9 +63,9 @@ func TestParseParam(t *testing.T) {
|
||||
|
||||
{true,
|
||||
ast.ParamStatement{
|
||||
Src: "{id:int range(1,5)}",
|
||||
Src: "{id:number range(1,5)}",
|
||||
Name: "id",
|
||||
Type: ast.ParamTypeInt,
|
||||
Type: ast.ParamTypeNumber,
|
||||
Funcs: []ast.ParamFunc{
|
||||
{
|
||||
Name: "range",
|
||||
@@ -106,18 +106,18 @@ func TestParseParam(t *testing.T) {
|
||||
Type: ast.ParamTypeUnExpected,
|
||||
ErrorCode: 404,
|
||||
}}, // 5
|
||||
{false, // false because it will give an error of unexpeced token type with value 2
|
||||
{true,
|
||||
ast.ParamStatement{
|
||||
Src: "{myparam2}",
|
||||
Name: "myparam", // expected "myparam" because we don't allow integers to the parameter names.
|
||||
Name: "myparam2", // we now allow integers to the parameter names.
|
||||
Type: ast.ParamTypeString,
|
||||
ErrorCode: 404,
|
||||
}}, // 6
|
||||
{true,
|
||||
ast.ParamStatement{
|
||||
Src: "{id:int even()}", // test param funcs without any arguments (LPAREN peek for RPAREN)
|
||||
Src: "{id:number even()}", // test param funcs without any arguments (LPAREN peek for RPAREN)
|
||||
Name: "id",
|
||||
Type: ast.ParamTypeInt,
|
||||
Type: ast.ParamTypeNumber,
|
||||
Funcs: []ast.ParamFunc{
|
||||
{
|
||||
Name: "even"},
|
||||
@@ -126,18 +126,32 @@ func TestParseParam(t *testing.T) {
|
||||
}}, // 7
|
||||
{true,
|
||||
ast.ParamStatement{
|
||||
Src: "{id:long else 404}",
|
||||
Src: "{id:int64 else 404}",
|
||||
Name: "id",
|
||||
Type: ast.ParamTypeLong,
|
||||
Type: ast.ParamTypeInt64,
|
||||
ErrorCode: 404,
|
||||
}}, // 8
|
||||
{true,
|
||||
ast.ParamStatement{
|
||||
Src: "{has:boolean else 404}",
|
||||
Src: "{id:long else 404}", // backwards-compatible test.
|
||||
Name: "id",
|
||||
Type: ast.ParamTypeInt64,
|
||||
ErrorCode: 404,
|
||||
}}, // 9
|
||||
{true,
|
||||
ast.ParamStatement{
|
||||
Src: "{has:bool else 404}",
|
||||
Name: "has",
|
||||
Type: ast.ParamTypeBoolean,
|
||||
ErrorCode: 404,
|
||||
}}, // 9
|
||||
}}, // 10
|
||||
{true,
|
||||
ast.ParamStatement{
|
||||
Src: "{has:boolean else 404}", // backwards-compatible test.
|
||||
Name: "has",
|
||||
Type: ast.ParamTypeBoolean,
|
||||
ErrorCode: 404,
|
||||
}}, // 11
|
||||
|
||||
}
|
||||
|
||||
@@ -167,11 +181,11 @@ func TestParse(t *testing.T) {
|
||||
valid bool
|
||||
expectedStatements []ast.ParamStatement
|
||||
}{
|
||||
{"/api/users/{id:int min(1) max(5) else 404}", true,
|
||||
{"/api/users/{id:number min(1) max(5) else 404}", true,
|
||||
[]ast.ParamStatement{{
|
||||
Src: "{id:int min(1) max(5) else 404}",
|
||||
Src: "{id:number min(1) max(5) else 404}",
|
||||
Name: "id",
|
||||
Type: ast.ParamTypeInt,
|
||||
Type: ast.ParamTypeNumber,
|
||||
Funcs: []ast.ParamFunc{
|
||||
{
|
||||
Name: "min",
|
||||
@@ -183,11 +197,11 @@ func TestParse(t *testing.T) {
|
||||
ErrorCode: 404,
|
||||
},
|
||||
}}, // 0
|
||||
{"/admin/{id:int range(1,5)}", true,
|
||||
{"/admin/{id:uint64 range(1,5)}", true,
|
||||
[]ast.ParamStatement{{
|
||||
Src: "{id:int range(1,5)}",
|
||||
Src: "{id:uint64 range(1,5)}", // test alternative (backwards-compatibility) "int"
|
||||
Name: "id",
|
||||
Type: ast.ParamTypeInt,
|
||||
Type: ast.ParamTypeUint64,
|
||||
Funcs: []ast.ParamFunc{
|
||||
{
|
||||
Name: "range",
|
||||
@@ -233,10 +247,10 @@ func TestParse(t *testing.T) {
|
||||
ErrorCode: 404,
|
||||
},
|
||||
}}, // 5
|
||||
{"/p2/{myparam2}", false, // false because it will give an error of unexpeced token type with value 2
|
||||
{"/p2/{myparam2}", true,
|
||||
[]ast.ParamStatement{{
|
||||
Src: "{myparam2}",
|
||||
Name: "myparam", // expected "myparam" because we don't allow integers to the parameter names.
|
||||
Name: "myparam2", // we now allow integers to the parameter names.
|
||||
Type: ast.ParamTypeString,
|
||||
ErrorCode: 404,
|
||||
},
|
||||
|
||||
@@ -13,8 +13,8 @@ type Token struct {
|
||||
|
||||
// /about/{fullname:alphabetical}
|
||||
// /profile/{anySpecialName:string}
|
||||
// {id:int range(1,5) else 404}
|
||||
// /admin/{id:int eq(1) else 402}
|
||||
// {id:uint64 range(1,5) else 404}
|
||||
// /admin/{id:number eq(1) else 402}
|
||||
// /file/{filepath:file else 405}
|
||||
const (
|
||||
EOF = iota // 0
|
||||
|
||||
@@ -214,14 +214,17 @@ type Map struct {
|
||||
// string type
|
||||
// anything
|
||||
String *Macro
|
||||
// uint type
|
||||
// only positive numbers (+0-9)
|
||||
// it could be uint/uint32 but we keep int for simplicity
|
||||
Int *Macro
|
||||
// long an int64 type
|
||||
// only positive numbers (+0-9)
|
||||
// it could be uint64 but we keep int64 for simplicity
|
||||
Long *Macro
|
||||
|
||||
// int type
|
||||
// both positive and negative numbers, any number of digits.
|
||||
Number *Macro
|
||||
// int64 as int64 type
|
||||
// -9223372036854775808 to 9223372036854775807.
|
||||
Int64 *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".
|
||||
@@ -247,11 +250,26 @@ type Map struct {
|
||||
//
|
||||
// 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 }),
|
||||
Int: newMacro(MustNewEvaluatorFromRegexp("^[0-9]+$")),
|
||||
Long: newMacro(MustNewEvaluatorFromRegexp("^[0-9]+$")),
|
||||
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]$")),
|
||||
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.
|
||||
@@ -270,14 +288,16 @@ func NewMap() *Map {
|
||||
|
||||
// Lookup returns the specific Macro from the map
|
||||
// based on the parameter type.
|
||||
// i.e if ast.ParamTypeInt then it will return the m.Int.
|
||||
// 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.ParamTypeInt:
|
||||
return m.Int
|
||||
case ast.ParamTypeLong:
|
||||
return m.Long
|
||||
case ast.ParamTypeNumber:
|
||||
return m.Number
|
||||
case ast.ParamTypeInt64:
|
||||
return m.Int64
|
||||
case ast.ParamTypeUint64:
|
||||
return m.Uint64
|
||||
case ast.ParamTypeBoolean:
|
||||
return m.Boolean
|
||||
case ast.ParamTypeAlphabetical:
|
||||
|
||||
@@ -64,9 +64,9 @@ func TestGoodParamFuncName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testEvaluatorRaw(macroEvaluator *Macro, input string, pass bool, i int, t *testing.T) {
|
||||
func testEvaluatorRaw(t *testing.T, macroEvaluator *Macro, input string, pass bool, i int) {
|
||||
if got := macroEvaluator.Evaluator(input); pass != got {
|
||||
t.Fatalf("tests[%d] - expecting %v but got %v", i, pass, got)
|
||||
t.Fatalf("%s - tests[%d] - expecting %v but got %v", t.Name(), i, pass, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,26 +86,86 @@ func TestStringEvaluatorRaw(t *testing.T) {
|
||||
} // 0
|
||||
|
||||
for i, tt := range tests {
|
||||
testEvaluatorRaw(f.String, tt.input, tt.pass, i, t)
|
||||
testEvaluatorRaw(t, f.String, tt.input, tt.pass, i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntEvaluatorRaw(t *testing.T) {
|
||||
func TestNumberEvaluatorRaw(t *testing.T) {
|
||||
f := NewMap()
|
||||
|
||||
tests := []struct {
|
||||
pass bool
|
||||
input string
|
||||
}{
|
||||
{false, "astring"}, // 0
|
||||
{false, "astringwith_numb3rS_and_symbol$"}, // 1
|
||||
{true, "32321"}, // 2
|
||||
{false, "main.css"}, // 3
|
||||
{false, "/assets/main.css"}, // 4
|
||||
{false, "astring"}, // 0
|
||||
{false, "astringwith_numb3rS_and_symbol$"}, // 1
|
||||
{true, "32321"}, // 2
|
||||
{true, "18446744073709551615"}, // 3
|
||||
{true, "-18446744073709551615"}, // 4
|
||||
{true, "-18446744073709553213213213213213121615"}, // 5
|
||||
{false, "42 18446744073709551615"}, // 6
|
||||
{false, "--42"}, // 7
|
||||
{false, "+42"}, // 9
|
||||
{false, "main.css"}, // 9
|
||||
{false, "/assets/main.css"}, // 10
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
testEvaluatorRaw(f.Int, tt.input, tt.pass, i, t)
|
||||
testEvaluatorRaw(t, f.Number, tt.input, tt.pass, i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt64EvaluatorRaw(t *testing.T) {
|
||||
f := NewMap()
|
||||
|
||||
tests := []struct {
|
||||
pass bool
|
||||
input string
|
||||
}{
|
||||
{false, "astring"}, // 0
|
||||
{false, "astringwith_numb3rS_and_symbol$"}, // 1
|
||||
{false, "18446744073709551615"}, // 2
|
||||
{false, "92233720368547758079223372036854775807"}, // 3
|
||||
{false, "9223372036854775808 9223372036854775808"}, // 4
|
||||
{false, "main.css"}, // 5
|
||||
{false, "/assets/main.css"}, // 6
|
||||
{true, "9223372036854775807"}, // 7
|
||||
{true, "-9223372036854775808"}, // 8
|
||||
{true, "-0"}, // 9
|
||||
{true, "1"}, // 10
|
||||
{true, "-042"}, // 11
|
||||
{true, "142"}, // 12
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
testEvaluatorRaw(t, f.Int64, tt.input, tt.pass, i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint64EvaluatorRaw(t *testing.T) {
|
||||
f := NewMap()
|
||||
|
||||
tests := []struct {
|
||||
pass bool
|
||||
input string
|
||||
}{
|
||||
{false, "astring"}, // 0
|
||||
{false, "astringwith_numb3rS_and_symbol$"}, // 1
|
||||
{false, "-9223372036854775808"}, // 2
|
||||
{false, "main.css"}, // 3
|
||||
{false, "/assets/main.css"}, // 4
|
||||
{false, "92233720368547758079223372036854775807"}, // 5
|
||||
{false, "9223372036854775808 9223372036854775808"}, // 6
|
||||
{false, "-1"}, // 7
|
||||
{false, "-0"}, // 8
|
||||
{false, "+1"}, // 9
|
||||
{true, "18446744073709551615"}, // 10
|
||||
{true, "9223372036854775807"}, // 11
|
||||
{true, "0"}, // 12
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
testEvaluatorRaw(t, f.Uint64, tt.input, tt.pass, i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +184,7 @@ func TestAlphabeticalEvaluatorRaw(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
testEvaluatorRaw(f.Alphabetical, tt.input, tt.pass, i, t)
|
||||
testEvaluatorRaw(t, f.Alphabetical, tt.input, tt.pass, i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +203,7 @@ func TestFileEvaluatorRaw(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
testEvaluatorRaw(f.File, tt.input, tt.pass, i, t)
|
||||
testEvaluatorRaw(t, f.File, tt.input, tt.pass, i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +223,7 @@ func TestPathEvaluatorRaw(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, tt := range pathTests {
|
||||
testEvaluatorRaw(f.Path, tt.input, tt.pass, i, t)
|
||||
testEvaluatorRaw(t, f.Path, tt.input, tt.pass, i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,5 +242,5 @@ func TestPathEvaluatorRaw(t *testing.T) {
|
||||
|
||||
// // p.Params = append(p.)
|
||||
|
||||
// testEvaluatorRaw(m.String, p.Src, false, 0, t)
|
||||
// testEvaluatorRaw(t, m.String, p.Src, false, 0)
|
||||
// }
|
||||
|
||||
@@ -110,10 +110,10 @@ type Party interface {
|
||||
// otherwise use `Party` which can handle many paths with different handlers and middlewares.
|
||||
//
|
||||
// Usage:
|
||||
// app.HandleMany(iris.MethodGet, "/user /user/{id:int} /user/me", userHandler)
|
||||
// app.HandleMany(iris.MethodGet, "/user /user/{id:uint64} /user/me", userHandler)
|
||||
// At the other side, with `Handle` we've had to write:
|
||||
// app.Handle(iris.MethodGet, "/user", userHandler)
|
||||
// app.Handle(iris.MethodGet, "/user/{id:int}", userHandler)
|
||||
// app.Handle(iris.MethodGet, "/user/{id:uint64}", userHandler)
|
||||
// app.Handle(iris.MethodGet, "/user/me", userHandler)
|
||||
//
|
||||
// This method is used behind the scenes at the `Controller` function
|
||||
|
||||
@@ -27,8 +27,8 @@ func TestCleanPath(t *testing.T) {
|
||||
"/total/{year:string regexp(\\d{4})}/more/{s:string regexp(\\d{7})}"},
|
||||
{"/single_no_params",
|
||||
"/single_no_params"},
|
||||
{"/single/{id:int}",
|
||||
"/single/{id:int}"},
|
||||
{"/single/{id:uint64}",
|
||||
"/single/{id:uint64}"},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
@@ -45,14 +45,16 @@ func TestSplitPath(t *testing.T) {
|
||||
}{
|
||||
{"/v2/stores/{id:string format(uuid)} /v3",
|
||||
[]string{"/v2/stores/{id:string format(uuid)}", "/v3"}},
|
||||
{"/user/{id:int} /admin/{id:int}",
|
||||
[]string{"/user/{id:int}", "/admin/{id:int}"}},
|
||||
{"/user/{id:uint64} /admin/{id:uint64}",
|
||||
[]string{"/user/{id:uint64}", "/admin/{id:uint64}"}},
|
||||
{"/users/{id:int} /admins/{id:int64}",
|
||||
[]string{"/users/{id:int}", "/admins/{id:int64}"}},
|
||||
{"/user /admin",
|
||||
[]string{"/user", "/admin"}},
|
||||
{"/single_no_params",
|
||||
[]string{"/single_no_params"}},
|
||||
{"/single/{id:int}",
|
||||
[]string{"/single/{id:int}"}},
|
||||
{"/single/{id:number}",
|
||||
[]string{"/single/{id:number}"}},
|
||||
}
|
||||
|
||||
equalSlice := func(s1 []string, s2 []string) bool {
|
||||
|
||||
@@ -16,7 +16,7 @@ type Route struct {
|
||||
Method string `json:"method"` // "GET"
|
||||
methodBckp string // if Method changed to something else (which is possible at runtime as well, via RefreshRouter) then this field will be filled with the old one.
|
||||
Subdomain string `json:"subdomain"` // "admin."
|
||||
tmpl *macro.Template // Tmpl().Src: "/api/user/{id:int}"
|
||||
tmpl *macro.Template // Tmpl().Src: "/api/user/{id:uint64}"
|
||||
// temp storage, they're appended to the Handlers on build.
|
||||
// Execution happens before Handlers, can be empty.
|
||||
beginHandlers context.Handlers
|
||||
@@ -198,7 +198,7 @@ func formatPath(path string) string {
|
||||
|
||||
// StaticPath returns the static part of the original, registered route path.
|
||||
// if /user/{id} it will return /user
|
||||
// if /user/{id}/friend/{friendid:int} it will return /user too
|
||||
// if /user/{id}/friend/{friendid:uint64} it will return /user too
|
||||
// if /assets/{filepath:path} it will return /assets.
|
||||
func (r Route) StaticPath() string {
|
||||
src := r.tmpl.Src
|
||||
|
||||
Reference in New Issue
Block a user