248 lines
7.1 KiB
Go
248 lines
7.1 KiB
Go
package routing
|
|
|
|
import (
|
|
"errors"
|
|
"path"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type (
|
|
|
|
// UrlResolver is a URL resolver utility that stores the handler information registered by UrlFor.
|
|
UrlResolver interface {
|
|
// Get is a resolver function that takes a name and parameters and returns a URL. If the URL is not found, it panics.
|
|
Get(urlName string, params ...string) string
|
|
// Reverse is a resolver function that takes a name and parameters and returns a URL.
|
|
Reverse(urlName string, params ...string) (string, error)
|
|
// ReverseWithParams is a resolver function that takes a name and parameters and returns a URL.
|
|
ReverseWithParams(urlName string, params []string) (string, error)
|
|
// MustReverse is a resolver function that takes a name and parameters and returns a URL. If the URL is not found, it panics.
|
|
MustReverse(urlName string, params ...string) string
|
|
// MustReverseWithParams is a resolver function that takes a name and parameters and returns a URL. If the URL is not found, it panics.
|
|
MustReverseWithParams(urlName string, params []string) string
|
|
}
|
|
|
|
// UrlFor is a reverse-routing utility that stores the handler information.
|
|
UrlFor interface {
|
|
UrlResolver
|
|
// Add registers name, parameter, and URL for UrlResolver.
|
|
// If a duplicate name exists, an error is returned instead of registering.
|
|
Add(urlName, urlAddr string, params ...string) (string, error)
|
|
// MustAdd registers name, parameter, and URL for UrlResolver.
|
|
// If a duplicate name exists, it panics.
|
|
MustAdd(urlName, urlAddr string, params ...string) string
|
|
// AddGr registers name, parameter, and URL for UrlResolver with nested group infos.
|
|
// If a duplicate name exists, an error is returned instead of registering.
|
|
AddGr(urlName, urlAddr string, groupNames, groupAddrs []string, params ...string) (string, error)
|
|
// MustAddGr registers name, parameter, and URL for UrlResolver with nested group infos.
|
|
// If a duplicate name exists, it panics.
|
|
MustAddGr(urlName, urlAddr string, groupNames, groupAddrs []string, params ...string) string
|
|
// Clear clears all registered reverse-routing infos.
|
|
Clear()
|
|
// String returns summarized info of registered reverse-routing infos.
|
|
String() string
|
|
// ToResolver returns a UrlResolver.
|
|
ToResolver() UrlResolver
|
|
}
|
|
routerFragment struct {
|
|
url string
|
|
params []string
|
|
}
|
|
|
|
reverseRouter map[string]routerFragment
|
|
|
|
reverseRouteResolver func(urlName string, params []string) (string, error)
|
|
)
|
|
|
|
// NewUrlFor returns a UrlFor.
|
|
func NewUrlFor() UrlFor {
|
|
router := make(reverseRouter)
|
|
return &router
|
|
}
|
|
|
|
func (rr reverseRouteResolver) Get(urlName string, params ...string) string {
|
|
res, err := rr(urlName, params)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (rr reverseRouteResolver) Reverse(urlName string, params ...string) (string, error) {
|
|
return rr(urlName, params)
|
|
}
|
|
|
|
func (rr reverseRouteResolver) ReverseWithParams(urlName string, params []string) (string, error) {
|
|
return rr(urlName, params)
|
|
}
|
|
|
|
func (rr reverseRouteResolver) MustReverse(urlName string, params ...string) string {
|
|
res, err := rr(urlName, params)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (rr reverseRouteResolver) MustReverseWithParams(urlName string, params []string) string {
|
|
res, err := rr(urlName, params)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (us *reverseRouter) ToResolver() UrlResolver {
|
|
return reverseRouteResolver(us.ReverseWithParams)
|
|
}
|
|
|
|
func (us *reverseRouter) MustReverse(urlName string, params ...string) string {
|
|
res, err := us.ReverseWithParams(urlName, params)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (us *reverseRouter) MustReverseWithParams(urlName string, params []string) string {
|
|
res, err := us.ReverseWithParams(urlName, params)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return res
|
|
}
|
|
|
|
func (us *reverseRouter) MustAdd(urlName, urlAddr string, params ...string) string {
|
|
addr, err := us.addInternal(urlName, urlAddr, nil, nil, params)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return addr
|
|
}
|
|
|
|
func (us *reverseRouter) Add(urlName, urlAddr string, params ...string) (string, error) {
|
|
return us.addInternal(urlName, urlAddr, nil, nil, params)
|
|
}
|
|
|
|
func (us *reverseRouter) MustAddGr(urlName, urlAddr string, groupNames, groupAddrs []string, params ...string) string {
|
|
addr, err := us.addInternal(urlName, urlAddr, groupNames, groupAddrs, params)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return addr
|
|
}
|
|
|
|
func (us *reverseRouter) AddGr(urlName, urlAddr string, groupNames, groupAddrs []string, params ...string) (string, error) {
|
|
return us.addInternal(urlName, urlAddr, groupNames, groupAddrs, params)
|
|
}
|
|
|
|
func (us *reverseRouter) Reverse(urlName string, params ...string) (string, error) {
|
|
return us.ReverseWithParams(urlName, params)
|
|
}
|
|
|
|
func (us reverseRouter) addInternal(urlName, urlAddr string, groupNames, groupAddrs, params []string) (string, error) {
|
|
if _, ok := us[urlName]; ok {
|
|
return "", errors.New("Url already exists. Try to use .Get() method.")
|
|
}
|
|
routeName := strings.Join(append(groupNames, urlName), ".")
|
|
addr := path.Join(append(groupAddrs, urlAddr)...)
|
|
|
|
tmpUrl := routerFragment{addr, params}
|
|
us[routeName] = tmpUrl
|
|
return addr, nil
|
|
}
|
|
|
|
func (us reverseRouter) Clear() {
|
|
for k := range us {
|
|
delete(us, k)
|
|
}
|
|
}
|
|
|
|
func (us *reverseRouter) Get(urlName string, params ...string) string {
|
|
url, err := us.ReverseWithParams(urlName, params)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return url
|
|
}
|
|
|
|
func (us reverseRouter) ReverseWithParams(urlName string, params []string) (string, error) {
|
|
if len(params) != len(us[urlName].params) {
|
|
return "", errors.New("Bad Url Reverse: mismatch params for URL: " + urlName)
|
|
}
|
|
res := us[urlName].url
|
|
for i, val := range params {
|
|
res = strings.Replace(res, us[urlName].params[i], val, 1)
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (us reverseRouter) String() (ret string) {
|
|
var (
|
|
numOfRoutes = len(us)
|
|
builder strings.Builder
|
|
needSort bool
|
|
)
|
|
defer func() {
|
|
ret = builder.String()
|
|
}()
|
|
|
|
builder.WriteString(strconv.FormatInt(int64(numOfRoutes), 10))
|
|
builder.WriteByte(' ')
|
|
switch numOfRoutes {
|
|
case 0:
|
|
builder.WriteString("route")
|
|
return
|
|
case 1:
|
|
builder.WriteString("route:\n")
|
|
default:
|
|
builder.WriteString("routes:\n")
|
|
needSort = true
|
|
}
|
|
|
|
fragmentStringer := func(builder *strings.Builder, idx int, key string, value routerFragment) {
|
|
builder.WriteByte('\t')
|
|
builder.WriteString(key)
|
|
builder.WriteByte('(')
|
|
builder.WriteString(value.url)
|
|
builder.WriteString(") [")
|
|
numOfParams := len(value.params)
|
|
for pathIdx := 0; pathIdx < numOfParams; pathIdx++ {
|
|
builder.WriteString(value.params[pathIdx])
|
|
if pathIdx < numOfParams-1 {
|
|
builder.WriteByte(',')
|
|
}
|
|
}
|
|
builder.WriteByte(']')
|
|
if idx < numOfRoutes-1 {
|
|
builder.WriteByte('\n')
|
|
}
|
|
}
|
|
if needSort {
|
|
routeKeys := make([]string, 0, numOfRoutes)
|
|
for key := range us {
|
|
routeKeys = append(routeKeys, key)
|
|
}
|
|
|
|
sort.SliceStable(routeKeys, func(i, j int) bool {
|
|
return len(us[routeKeys[i]].url) < len(us[routeKeys[j]].url)
|
|
})
|
|
for i, key := range routeKeys {
|
|
fragmentStringer(&builder, i, key, us[key])
|
|
}
|
|
} else {
|
|
i := 0
|
|
for key, value := range us {
|
|
fragmentStringer(&builder, i, key, value)
|
|
i++
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (us reverseRouter) getParamName(urlName string, num int) string {
|
|
return us[urlName].params[num]
|
|
}
|