1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-09 04:51: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:
Gerasimos (Makis) Maropoulos
2018-08-23 06:30:12 +03:00
parent 01b5f6089d
commit b019a281eb
28 changed files with 478 additions and 242 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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