iris/core/router/route.go
2017-06-11 23:07:50 +03:00

137 lines
3.9 KiB
Go

// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package router
import (
"fmt"
"strings"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/router/macro"
)
// Route contains the information about a registered Route.
// If any of the following fields are changed then the
// caller should Refresh the router.
type Route struct {
Name string // "userRoute"
Method string // "GET"
Subdomain string // "admin."
tmpl *macro.Template // Tmpl().Src: "/api/user/{id:int}"
Path string // "/api/user/:id"
Handlers context.Handlers
// FormattedPath all dynamic named parameters (if any) replaced with %v,
// used by Application to validate param values of a Route based on its name.
FormattedPath string
}
// NewRoute returns a new route based on its method,
// subdomain, the path (unparsed or original),
// handlers and the macro container which all routes should share.
// 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) {
tmpl, err := macro.Parse(unparsedPath, macros)
if err != nil {
return nil, err
}
path, handlers, err := compileRoutePathAndHandlers(handlers, tmpl)
if err != nil {
return nil, err
}
path = cleanPath(path) // maybe unnecessary here but who cares in this moment
defaultName := method + subdomain + path
formattedPath := formatPath(path)
route := &Route{
Name: defaultName,
Method: method,
Subdomain: subdomain,
tmpl: tmpl,
Path: path,
Handlers: handlers,
FormattedPath: formattedPath,
}
return route, nil
}
// Tmpl returns the path template, i
// it contains the parsed template
// for the route's path.
// May contain zero named parameters.
//
// Developer can get his registered path
// via Tmpl().Src, Route.Path is the path
// converted to match the underline router's specs.
func (r Route) Tmpl() macro.Template {
return *r.tmpl
}
// IsOnline returns true if the route is marked as "online" (state).
func (r Route) IsOnline() bool {
return r.Method != MethodNone
}
// formats the parsed to the underline path syntax.
// path = "/api/users/:id"
// return "/api/users/%v"
//
// path = "/files/*file"
// return /files/%v
//
// path = "/:username/messages/:messageid"
// return "/%v/messages/%v"
// we don't care about performance here, it's prelisten.
func formatPath(path string) string {
if strings.Contains(path, ParamStart) || strings.Contains(path, WildcardParamStart) {
var (
startRune = ParamStart[0]
wildcardStartRune = WildcardParamStart[0]
)
var formattedParts []string
parts := strings.Split(path, "/")
for _, part := range parts {
if len(part) == 0 {
continue
}
if part[0] == startRune || part[0] == wildcardStartRune {
// is param or wildcard param
part = "%v"
}
formattedParts = append(formattedParts, part)
}
return "/" + strings.Join(formattedParts, "/")
}
// the whole path is static just return it
return path
}
// ResolvePath returns the formatted path's %v replaced with the args.
func (r Route) ResolvePath(args ...string) string {
rpath, formattedPath := r.Path, r.FormattedPath
if rpath == formattedPath {
// static, no need to pass args
return rpath
}
// check if we have /*, if yes then join all arguments to one as path and pass that as parameter
if rpath[len(rpath)-1] == WildcardParamStart[0] {
parameter := strings.Join(args, "/")
return fmt.Sprintf(formattedPath, parameter)
}
// else return the formattedPath with its args,
// the order matters.
for _, s := range args {
formattedPath = strings.Replace(formattedPath, "%v", s, 1)
}
return formattedPath
}