From fb85ae15d5e6410f40f7285476b738f614d27bff Mon Sep 17 00:00:00 2001 From: kataras Date: Mon, 12 Jun 2017 04:47:16 +0300 Subject: [PATCH] Linting :a: Former-commit-id: 367bb53ed6656002c60c40e3ad30bda578de21c6 --- core/router/api_builder.go | 6 +- core/router/macro.go | 6 +- core/router/macro/interpreter/ast/ast.go | 103 +++++++++--------- .../router/macro/interpreter/parser/parser.go | 25 ++++- .../macro/interpreter/parser/parser_test.go | 3 +- core/router/macro/macro.go | 58 +++++++++- core/router/macro/template.go | 14 ++- core/router/route.go | 2 +- 8 files changed, 148 insertions(+), 69 deletions(-) diff --git a/core/router/api_builder.go b/core/router/api_builder.go index c5377e3c..f1da07ad 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -73,7 +73,7 @@ type RoutesProvider interface { // api builder // and child routers. type APIBuilder struct { // the api builder global macros registry - macros *macro.MacroMap + macros *macro.Map // the api builder global handlers per status code registry (used for custom http errors) errorCodeHandlers *ErrorCodeHandlers // the api builder global routes repository @@ -181,7 +181,9 @@ func (rb *APIBuilder) Party(relativePath string, handlers ...context.Handler) Pa // Macros returns the macro map which is responsible // to register custom macro functions for all routes. -func (rb *APIBuilder) Macros() *macro.MacroMap { +// +// Learn more at: https://github.com/kataras/iris/tree/master/_examples/beginner/routing/dynamic-path +func (rb *APIBuilder) Macros() *macro.Map { return rb.macros } diff --git a/core/router/macro.go b/core/router/macro.go index 3a088651..e4de53b4 100644 --- a/core/router/macro.go +++ b/core/router/macro.go @@ -17,8 +17,8 @@ import ( // defaultMacros returns a new macro map which // contains the default router's named param types functions. -func defaultMacros() *macro.MacroMap { - macros := macro.NewMacroMap() +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: @@ -31,7 +31,7 @@ func defaultMacros() *macro.MacroMap { return macros } -func registerBuiltinsMacroFuncs(out *macro.MacroMap) { +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 diff --git a/core/router/macro/interpreter/ast/ast.go b/core/router/macro/interpreter/ast/ast.go index b1fee043..0d36aeec 100644 --- a/core/router/macro/interpreter/ast/ast.go +++ b/core/router/macro/interpreter/ast/ast.go @@ -9,19 +9,39 @@ import ( "strconv" ) +// ParamType is a specific uint8 type +// which holds the parameter types' type. type ParamType uint8 const ( + // ParamTypeUnExpected is an unexpected parameter type. ParamTypeUnExpected ParamType = iota - // /myparam1 + // 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} ParamTypeString - // /42 + // ParamTypeInt is the integer, a number type. + // Allows only numbers (0-9) + // Declaration: /mypath/{myparam:int} ParamTypeInt - // /myparam + // ParamTypeAlphabetical is the alphabetical/letter type type. + // Allows letters only (upper or lowercase) + // Declaration: /mypath/{myparam:alphabetical} ParamTypeAlphabetical - // /main.css + // 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 - // /myparam1/myparam2 + // ParamTypePath is the multi path (or wildcard) type. + // Allows anything, should be the last part + // Declaration: /mypath/{myparam:path} ParamTypePath ) @@ -38,6 +58,14 @@ var paramTypes = map[string]ParamType{ } +// LookupParamType accepts the string +// representation of a parameter type. +// Available: +// "string" +// "int" +// "alphabetical" +// "file" +// "path" func LookupParamType(ident string) ParamType { if typ, ok := paramTypes[ident]; ok { return typ @@ -45,6 +73,14 @@ func LookupParamType(ident string) ParamType { return ParamTypeUnExpected } +// ParamStatement is a struct +// which holds all the necessary information about a macro parameter. +// It holds its type (string, int, alphabetical, file, path), +// its source ({param:type}), +// its name ("param"), +// its attached functions by the user (min, max...) +// and the http error code if that parameter +// failed to be evaluated. type ParamStatement struct { Src string // the original unparsed source, i.e: {id:int range(1,5) else 404} Name string // id @@ -53,35 +89,11 @@ type ParamStatement struct { ErrorCode int // 404 } +// ParamFuncArg represents a single parameter function's argument type ParamFuncArg interface{} -func ParamFuncArgInt64(a ParamFuncArg) (int64, bool) { - if v, ok := a.(int64); ok { - return v, false - } - return -1, false -} - -func ParamFuncArgToInt64(a ParamFuncArg) (int64, error) { - switch a.(type) { - case int64: - return a.(int64), nil - case string: - return strconv.ParseInt(a.(string), 10, 64) - case int: - return int64(a.(int)), nil - default: - return -1, fmt.Errorf("unexpected function argument type: %q", a) - } -} - -func ParamFuncArgInt(a ParamFuncArg) (int, bool) { - if v, ok := a.(int); ok { - return v, false - } - return -1, false -} - +// ParamFuncArgToInt converts and returns +// any type of "a", to an integer. func ParamFuncArgToInt(a ParamFuncArg) (int, error) { switch a.(type) { case int: @@ -95,26 +107,13 @@ func ParamFuncArgToInt(a ParamFuncArg) (int, error) { } } -func ParamFuncArgString(a ParamFuncArg) (string, bool) { - if v, ok := a.(string); ok { - return v, false - } - return "", false -} - -func ParamFuncArgToString(a ParamFuncArg) (string, error) { - switch a.(type) { - case string: - return a.(string), nil - case int: - return strconv.Itoa(a.(int)), nil - case int64: - return strconv.FormatInt(a.(int64), 10), nil - default: - return "", fmt.Errorf("unexpected function argument type: %q", a) - } -} - +// ParamFunc holds the name of a parameter's function +// and its arguments (values) +// A param func is declared with: +// {param:int range(1,5)}, +// the range is the +// param function name +// the 1 and 5 are the two param function arguments // range(1,5) type ParamFunc struct { Name string // range diff --git a/core/router/macro/interpreter/parser/parser.go b/core/router/macro/interpreter/parser/parser.go index 132c7597..7a785c18 100644 --- a/core/router/macro/interpreter/parser/parser.go +++ b/core/router/macro/interpreter/parser/parser.go @@ -14,6 +14,9 @@ import ( "github.com/kataras/iris/core/router/macro/interpreter/token" ) +// Parse takes a route "fullpath" +// and returns its param statements +// and an error on failure. func Parse(fullpath string) ([]*ast.ParamStatement, error) { pathParts := strings.SplitN(fullpath, "/", -1) p := new(ParamParser) @@ -45,20 +48,27 @@ func Parse(fullpath string) ([]*ast.ParamStatement, error) { return statements, nil } +// ParamParser is the parser +// which is being used by the Parse function +// to parse path segments one by one +// and return their parsed parameter statements (param name, param type its functions and the inline route's functions). type ParamParser struct { src string errors []string } +// NewParamParser receives a "src" of a single parameter +// and returns a new ParamParser, ready to Parse. func NewParamParser(src string) *ParamParser { p := new(ParamParser) p.Reset(src) return p } +// Reset resets this ParamParser, +// reset the errors and set the source to the input "src". func (p *ParamParser) Reset(src string) { p.src = src - p.errors = []string{} } @@ -66,8 +76,15 @@ func (p *ParamParser) appendErr(format string, a ...interface{}) { p.errors = append(p.errors, fmt.Sprintf(format, a...)) } -const DefaultParamErrorCode = 404 -const DefaultParamType = ast.ParamTypeString +const ( + // DefaultParamErrorCode is the default http error code, 404 not found, + // per-parameter. An error code can be setted via + // the "else" keyword inside a route's path. + DefaultParamErrorCode = 404 + // DefaultParamType when parameter type is missing use this param type, defaults to string + // and it should be remains unless earth split in two. + DefaultParamType = ast.ParamTypeString +) func parseParamFuncArg(t token.Token) (a ast.ParamFuncArg, err error) { if t.Type == token.INT { @@ -83,6 +100,8 @@ func (p ParamParser) Error() error { return nil } +// Parse parses the p.src and returns its param statement +// and an error on failure. func (p *ParamParser) Parse() (*ast.ParamStatement, error) { l := lexer.New(p.src) diff --git a/core/router/macro/interpreter/parser/parser_test.go b/core/router/macro/interpreter/parser/parser_test.go index f1b9383a..c30d714d 100644 --- a/core/router/macro/interpreter/parser/parser_test.go +++ b/core/router/macro/interpreter/parser/parser_test.go @@ -129,7 +129,8 @@ func TestParseParam(t *testing.T) { }}, // 7 } - var p *ParamParser = new(ParamParser) + + p := new(ParamParser) for i, tt := range tests { p.Reset(tt.expectedStatement.Src) resultStmt, err := p.Parse() diff --git a/core/router/macro/macro.go b/core/router/macro/macro.go index 9012f809..4b57681c 100644 --- a/core/router/macro/macro.go +++ b/core/router/macro/macro.go @@ -13,9 +13,16 @@ import ( "github.com/kataras/iris/core/router/macro/interpreter/ast" ) -// final evaluator signature for both param types and param funcs +// EvaluatorFunc is the signature for both param types and param funcs. +// It should accepts the param's value as string +// and return true if validated otherwise false. type EvaluatorFunc func(paramValue string) bool +// NewEvaluatorFromRegexp accepts a regexp "expr" expression +// and returns an EvaluatorFunc based on that regexp. +// the regexp is compiled before return. +// +// Returns a not-nil error on regexp compile failure. func NewEvaluatorFromRegexp(expr string) (EvaluatorFunc, error) { if expr == "" { return nil, fmt.Errorf("empty regex expression") @@ -34,6 +41,8 @@ func NewEvaluatorFromRegexp(expr string) (EvaluatorFunc, error) { return r.MatchString, nil } +// MustNewEvaluatorFromRegexp same as NewEvaluatorFromRegexp +// but it panics on the "expr" parse failure. func MustNewEvaluatorFromRegexp(expr string) EvaluatorFunc { r, err := NewEvaluatorFromRegexp(expr) if err != nil { @@ -122,13 +131,34 @@ func convertBuilderFunc(fn interface{}) ParamEvaluatorBuilder { } type ( + // Macro represents the parsed macro, + // which holds + // the evaluator (param type's evaluator + param functions evaluators) + // and its param functions. + // + // Any type contains its own macro + // instance, so an String type + // contains its type evaluator + // which is the "Evaluator" field + // and it can register param functions + // to that macro which maps to a parameter type. Macro struct { Evaluator EvaluatorFunc funcs []ParamFunc } + // ParamEvaluatorBuilder is a func + // which accepts a param function's arguments (values) + // and returns an EvaluatorFunc, its job + // is to make the macros to be registered + // by user at the most generic possible way. ParamEvaluatorBuilder func([]ast.ParamFuncArg) EvaluatorFunc + // ParamFunc represents the parsed + // parameter function, it holds + // the parameter's name + // and the function which will build + // the evaluator func. ParamFunc struct { Name string Func ParamEvaluatorBuilder @@ -139,7 +169,12 @@ func newMacro(evaluator EvaluatorFunc) *Macro { return &Macro{Evaluator: evaluator} } -// at boot time, per param +// 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{}) { fullFn := convertBuilderFunc(fn) m.registerFunc(funcName, fullFn) @@ -175,7 +210,10 @@ func (m *Macro) getFunc(funcName string) ParamEvaluatorBuilder { return nil } -type MacroMap struct { +// 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, Alphabetical, File, Path). +type Map struct { // string type // anything String *Macro @@ -198,8 +236,12 @@ type MacroMap struct { Path *Macro } -func NewMacroMap() *MacroMap { - return &MacroMap{ +// NewMap returns a new macro Map with default +// type evaluators. +// +// Learn more at: https://github.com/kataras/iris/tree/master/_examples/beginner/routing/dynamic-path +func NewMap() *Map { + return &Map{ // it allows everything, so no need for a regexp here. String: newMacro(func(string) bool { return true }), Int: newMacro(MustNewEvaluatorFromRegexp("^[0-9]+$")), @@ -213,7 +255,11 @@ func NewMacroMap() *MacroMap { } } -func (m *MacroMap) Lookup(typ ast.ParamType) *Macro { +// 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. +// Returns the m.String if not matched. +func (m *Map) Lookup(typ ast.ParamType) *Macro { switch typ { case ast.ParamTypeInt: return m.Int diff --git a/core/router/macro/template.go b/core/router/macro/template.go index 99cb7d93..dbaa1bba 100644 --- a/core/router/macro/template.go +++ b/core/router/macro/template.go @@ -9,12 +9,20 @@ import ( "github.com/kataras/iris/core/router/macro/interpreter/parser" ) +// Template contains a route's path full parsed template. +// +// Fields: +// Src is the raw source of the path, i.e /users/{id:int min(1)} +// Params is the list of the Params that are being used to the +// path, i.e the min as param name and 1 as the param argument. type Template struct { // Src is the original template given by the client Src string Params []TemplateParam } +// TemplateParam is the parsed macro parameter's template +// they are being used to describe the param's syntax result. type TemplateParam struct { Src string // the unparsed param'false source // Type is not useful anywhere here but maybe @@ -26,7 +34,11 @@ type TemplateParam struct { Funcs []EvaluatorFunc } -func Parse(src string, macros *MacroMap) (*Template, error) { +// Parse takes a full route path and a macro map (macro map contains the macro types with their registered param functions) +// 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) if err != nil { return nil, err diff --git a/core/router/route.go b/core/router/route.go index d33430e4..04e566f6 100644 --- a/core/router/route.go +++ b/core/router/route.go @@ -33,7 +33,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 string, - handlers context.Handlers, macros *macro.MacroMap) (*Route, error) { + handlers context.Handlers, macros *macro.Map) (*Route, error) { tmpl, err := macro.Parse(unparsedPath, macros) if err != nil {