diff --git a/_examples/routing/README.md b/_examples/routing/README.md index 14c3f904..40074682 100644 --- a/_examples/routing/README.md +++ b/_examples/routing/README.md @@ -166,6 +166,12 @@ both positive and negative numbers, any number of digits (ctx.Params().GetInt wi int64 type -9223372036854775808 to 9223372036854775807 ++------------------------+ +| {param:uint8} | ++------------------------+ +uint8 type +0 to 255 + +------------------------+ | {param:uint64} | +------------------------+ diff --git a/_examples/routing/dynamic-path/main.go b/_examples/routing/dynamic-path/main.go index 42ce2f0a..462634c7 100644 --- a/_examples/routing/dynamic-path/main.go +++ b/_examples/routing/dynamic-path/main.go @@ -48,6 +48,13 @@ func main() { // -9223372036854775808 to 9223372036854775807 // // +------------------------+ + // | {param:uint8} | + // +------------------------+ + // uint8 type + // 0 to 255 + // + // + // +------------------------+ // | {param:uint64} | // +------------------------+ // uint64 type @@ -146,6 +153,12 @@ func main() { ctx.Writef("Hello id: %d looking for friend id: ", id, friendid) }) // this will throw e 504 error code instead of 404 if all route's macros not passed. + // :uint8 0 to 255. + app.Get("/ages/{age:uint8 else 400}", func(ctx iris.Context) { + age, _ := ctx.Params().GetUint8("age") + ctx.Writef("age selected: %d", age) + }) + // Another example using a custom regexp and any custom logic. latLonExpr := "^-?[0-9]{1,3}(?:\\.[0-9]{1,10})?$" latLonRegex, err := regexp.Compile(latLonExpr) diff --git a/context/context.go b/context/context.go index 46397a10..b558fe9a 100644 --- a/context/context.go +++ b/context/context.go @@ -162,7 +162,14 @@ func (r RequestParams) GetFloat64(key string) (float64, error) { return r.store.GetFloat64(key) } -// GetUint64 returns the path paramete's value as uint64, based on its 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) { diff --git a/core/memstore/memstore.go b/core/memstore/memstore.go index 5b6bbdf2..48986514 100644 --- a/core/memstore/memstore.go +++ b/core/memstore/memstore.go @@ -197,6 +197,39 @@ func (e Entry) Float32Default(key string, def float32) (float32, error) { return def, errFindParse.Format("float32", e.Key) } +// Uint8Default returns the entry's value as uint8. +// If not found returns "def" and a non-nil error. +func (e Entry) Uint8Default(def uint8) (uint8, error) { + v := e.ValueRaw + if v == nil { + 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) + if err != nil { + return def, err + } + if vuint64 > 255 { + return def, errFindParse.Format("uint8", e.Key) + } + return uint8(vuint64), nil + } + + return def, errFindParse.Format("uint8", e.Key) +} + // Uint64Default returns the entry's value as uint64. // If not found returns "def" and a non-nil error. func (e Entry) Uint64Default(def uint64) (uint64, error) { @@ -449,6 +482,26 @@ func (r *Store) GetIntDefault(key string, def int) int { return def } +// 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 { + return 0, errFindParse.Format("uint8", key) + } + return v.Uint8Default(0) +} + +// GetUint8Default returns the entry's value as uint8, based on its key. +// If not found returns "def". +func (r *Store) GetUint8Default(key string, def uint8) uint8 { + if v, err := r.GetUint8(key); err == nil { + return v + } + + return def +} + // 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) { diff --git a/core/router/macro.go b/core/router/macro.go index aa2d8e8a..e46a07c7 100644 --- a/core/router/macro.go +++ b/core/router/macro.go @@ -37,6 +37,7 @@ func registerBuiltinsMacroFuncs(out *macro.Map) { registerStringMacroFuncs(out.String) registerNumberMacroFuncs(out.Number) registerInt64MacroFuncs(out.Int64) + registerUint8MacroFuncs(out.Uint8) registerUint64MacroFuncs(out.Uint64) registerAlphabeticalMacroFuncs(out.Alphabetical) registerFileMacroFuncs(out.File) @@ -176,6 +177,51 @@ func registerInt64MacroFuncs(out *macro.Macro) { }) } +// 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) { diff --git a/core/router/macro/interpreter/ast/ast.go b/core/router/macro/interpreter/ast/ast.go index 4a4103a4..42995690 100644 --- a/core/router/macro/interpreter/ast/ast.go +++ b/core/router/macro/interpreter/ast/ast.go @@ -26,6 +26,10 @@ const ( // Allows only -9223372036854775808 to 9223372036854775807. // Declaration: /mypath/{myparam:int64} or {myparam:long} ParamTypeInt64 + // ParamTypeUint8 a number type. + // Allows only 0 to 255. + // Declaration: /mypath/{myparam:uint8} + ParamTypeUint8 // ParamTypeUint64 a number type. // Allows only 0 to 18446744073709551615. // Declaration: /mypath/{myparam:uint64} @@ -88,6 +92,8 @@ func (pt ParamType) Kind() reflect.Kind { return reflect.Int case ParamTypeInt64: return reflect.Int64 + case ParamTypeUint8: + return reflect.Uint8 case ParamTypeUint64: return reflect.Uint64 case ParamTypeBoolean: @@ -106,6 +112,8 @@ func ValidKind(k reflect.Kind) bool { fallthrough case reflect.Int64: fallthrough + case reflect.Uint8: + fallthrough case reflect.Uint64: fallthrough case reflect.Bool: @@ -128,6 +136,7 @@ var paramTypes = map[string]ParamType{ "int": ParamTypeNumber, // same as number. "long": ParamTypeInt64, "int64": ParamTypeInt64, // same as long. + "uint8": ParamTypeUint8, "uint64": ParamTypeUint64, "boolean": ParamTypeBoolean, @@ -149,6 +158,7 @@ var paramTypes = map[string]ParamType{ // "string" // "number" or "int" // "long" or "int64" +// "uint8" // "uint64" // "boolean" or "bool" // "alphabetical" @@ -179,6 +189,8 @@ func LookupParamTypeFromStd(goType string) ParamType { return ParamTypeNumber case "int64": return ParamTypeInt64 + case "uint8": + return ParamTypeUint8 case "uint64": return ParamTypeUint64 case "bool": diff --git a/core/router/macro/macro.go b/core/router/macro/macro.go index 23a45d14..7ea4630f 100644 --- a/core/router/macro/macro.go +++ b/core/router/macro/macro.go @@ -271,6 +271,9 @@ type Map struct { // 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 @@ -313,6 +316,7 @@ func NewMap() *Map { // 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 @@ -346,6 +350,8 @@ func (m *Map) Lookup(typ ast.ParamType) *Macro { return m.Number case ast.ParamTypeInt64: return m.Int64 + case ast.ParamTypeUint8: + return m.Uint8 case ast.ParamTypeUint64: return m.Uint64 case ast.ParamTypeBoolean: diff --git a/core/router/macro/macro_test.go b/core/router/macro/macro_test.go index beab57f1..83092939 100644 --- a/core/router/macro/macro_test.go +++ b/core/router/macro/macro_test.go @@ -142,6 +142,37 @@ func TestInt64EvaluatorRaw(t *testing.T) { } } +func TestUint8EvaluatorRaw(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 + {false, "18446744073709551615"}, // 10 + {false, "9223372036854775807"}, // 11 + {false, "021"}, // 12 - no leading zeroes are allowed. + {false, "300"}, // 13 + {true, "0"}, // 14 + {true, "255"}, // 15 + {true, "21"}, // 16 + } + + for i, tt := range tests { + testEvaluatorRaw(t, f.Uint8, tt.input, tt.pass, i) + } +} + func TestUint64EvaluatorRaw(t *testing.T) { f := NewMap() diff --git a/doc.go b/doc.go index 3137bead..dc5f93b1 100644 --- a/doc.go +++ b/doc.go @@ -723,6 +723,12 @@ Standard macro types for parameters: int64 type -9223372036854775808 to 9223372036854775807 + +------------------------+ + | {param:uint8} | + +------------------------+ + uint8 type + 0 to 255 + +------------------------+ | {param:uint64} | +------------------------+ diff --git a/hero/param.go b/hero/param.go index 278e507a..4ea62fc2 100644 --- a/hero/param.go +++ b/hero/param.go @@ -44,6 +44,13 @@ func resolveParam(currentParamIndex int, typ reflect.Type) (reflect.Value, bool) 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: diff --git a/mvc/controller_test.go b/mvc/controller_test.go index 70de2a72..525c2103 100644 --- a/mvc/controller_test.go +++ b/mvc/controller_test.go @@ -366,7 +366,8 @@ func (c *testControllerRelPathFromFunc) EndRequest(ctx context.Context) { func (c *testControllerRelPathFromFunc) Get() {} func (c *testControllerRelPathFromFunc) GetBy(uint64) {} -func (c *testControllerRelPathFromFunc) GetRatioBy(int64) {} +func (c *testControllerRelPathFromFunc) GetUint8RatioBy(uint8) {} +func (c *testControllerRelPathFromFunc) GetUint64RatioBy(int64) {} func (c *testControllerRelPathFromFunc) GetAnythingByWildcard(string) {} func (c *testControllerRelPathFromFunc) GetLogin() {} @@ -391,8 +392,11 @@ func TestControllerRelPathFromFunc(t *testing.T) { e.GET("/18446744073709551615").Expect().Status(iris.StatusOK). Body().Equal("GET:/18446744073709551615") - e.GET("/ratio/-42").Expect().Status(iris.StatusOK). - Body().Equal("GET:/ratio/-42") + e.GET("/uint8/ratio/255").Expect().Status(iris.StatusOK). + Body().Equal("GET:/uint8/ratio/255") + e.GET("/uint8/ratio/256").Expect().Status(iris.StatusNotFound) + e.GET("/uint64/ratio/-42").Expect().Status(iris.StatusOK). + Body().Equal("GET:/uint64/ratio/-42") e.GET("/something/true").Expect().Status(iris.StatusOK). Body().Equal("GET:/something/true") e.GET("/something/false").Expect().Status(iris.StatusOK). diff --git a/mvc/param.go b/mvc/param.go index e261ac05..c2f22990 100644 --- a/mvc/param.go +++ b/mvc/param.go @@ -47,6 +47,11 @@ func makeFuncParamGetter(paramType ast.ParamType, paramName string) reflect.Valu 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)