1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-10 20:40:55 +00:00
Beego/tree.go

586 lines
15 KiB
Go
Raw Normal View History

2014-08-18 08:41:43 +00:00
// Copyright 2014 beego Author. All Rights Reserved.
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// http://www.apache.org/licenses/LICENSE-2.0
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2014-06-04 15:07:57 +00:00
package beego
import (
"path"
"regexp"
"strings"
2015-12-11 05:51:01 +00:00
"github.com/astaxie/beego/context"
2014-06-04 15:07:57 +00:00
"github.com/astaxie/beego/utils"
)
2015-12-16 15:11:03 +00:00
var (
allowSuffixExt = []string{".json", ".xml", ".html"}
)
2015-09-08 15:49:24 +00:00
// Tree has three elements: FixRouter/wildcard/leaves
// fixRouter sotres Fixed Router
// wildcard stores params
// leaves store the endpoint information
2014-06-04 15:07:57 +00:00
type Tree struct {
2015-12-16 15:11:03 +00:00
//prefix set for static router
prefix string
2014-06-04 15:07:57 +00:00
//search fix route first
2015-12-16 15:11:03 +00:00
fixrouters []*Tree
2014-06-04 15:07:57 +00:00
//if set, failure to match fixrouters search then search wildcard
wildcard *Tree
//if set, failure to match wildcard search
leaves []*leafInfo
2014-06-04 15:07:57 +00:00
}
2015-09-08 15:49:24 +00:00
// NewTree return a new Tree
2014-06-04 15:07:57 +00:00
func NewTree() *Tree {
2015-12-16 15:11:03 +00:00
return &Tree{}
2014-06-04 15:07:57 +00:00
}
2015-09-08 15:49:24 +00:00
// AddTree will add tree to the exist Tree
2014-06-08 12:24:01 +00:00
// prefix should has no params
func (t *Tree) AddTree(prefix string, tree *Tree) {
2014-06-12 12:50:29 +00:00
t.addtree(splitPath(prefix), tree, nil, "")
2014-06-08 12:24:01 +00:00
}
2014-06-12 12:50:29 +00:00
func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg string) {
2014-06-08 12:24:01 +00:00
if len(segments) == 0 {
panic("prefix should has path")
}
2014-06-12 12:50:29 +00:00
seg := segments[0]
iswild, params, regexpStr := splitSegment(seg)
2015-12-16 15:11:03 +00:00
// if it's ? meaning can igone this, so add one more rule for it
if len(params) > 0 && params[0] == ":" {
params = params[1:]
if len(segments[1:]) > 0 {
t.addtree(segments[1:], tree, append(wildcards, params...), reg)
} else {
filterTreeWithPrefix(tree, wildcards, reg)
}
}
//Rule: /login/*/access match /login/2009/11/access
//if already has *, and when loop the access, should as a regexpStr
if !iswild && utils.InSlice(":splat", wildcards) {
iswild = true
regexpStr = seg
}
//Rule: /user/:id/*
if seg == "*" && len(wildcards) > 0 && reg == "" {
regexpStr = "(.+)"
}
2014-08-11 16:02:27 +00:00
if len(segments) == 1 {
2014-06-12 12:50:29 +00:00
if iswild {
if regexpStr != "" {
2014-08-11 16:02:27 +00:00
if reg == "" {
rr := ""
for _, w := range wildcards {
if w == ":splat" {
rr = rr + "(.+)/"
} else {
rr = rr + "([^/]+)/"
}
}
regexpStr = rr + regexpStr
} else {
regexpStr = "/" + regexpStr
}
2015-04-05 15:11:50 +00:00
} else if reg != "" {
if seg == "*.*" {
regexpStr = "([^.]+).(.+)"
} else {
for _, w := range params {
if w == "." || w == ":" {
continue
}
regexpStr = "([^/]+)/" + regexpStr
2014-06-12 12:50:29 +00:00
}
}
}
2015-04-05 15:11:50 +00:00
reg = strings.Trim(reg+"/"+regexpStr, "/")
2014-08-11 16:02:27 +00:00
filterTreeWithPrefix(tree, append(wildcards, params...), reg)
2014-06-12 12:50:29 +00:00
t.wildcard = tree
} else {
2015-04-05 15:11:50 +00:00
reg = strings.Trim(reg+"/"+regexpStr, "/")
2014-08-11 16:02:27 +00:00
filterTreeWithPrefix(tree, append(wildcards, params...), reg)
2015-12-16 15:11:03 +00:00
tree.prefix = seg
t.fixrouters = append(t.fixrouters, tree)
2014-06-12 12:50:29 +00:00
}
2014-06-08 12:24:01 +00:00
return
}
2015-12-16 15:11:03 +00:00
2014-06-12 12:50:29 +00:00
if iswild {
if t.wildcard == nil {
t.wildcard = NewTree()
}
if regexpStr != "" {
2014-08-11 16:02:27 +00:00
if reg == "" {
rr := ""
for _, w := range wildcards {
if w == ":splat" {
rr = rr + "(.+)/"
} else {
rr = rr + "([^/]+)/"
}
}
2015-04-05 15:11:50 +00:00
regexpStr = rr + regexpStr
2014-08-11 16:02:27 +00:00
} else {
2015-04-05 15:11:50 +00:00
regexpStr = "/" + regexpStr
2014-08-11 16:02:27 +00:00
}
2015-04-05 15:11:50 +00:00
} else if reg != "" {
if seg == "*.*" {
regexpStr = "([^.]+).(.+)"
2015-12-16 15:11:03 +00:00
params = params[1:]
2015-04-05 15:11:50 +00:00
} else {
2016-03-11 05:00:58 +00:00
for range params {
regexpStr = "([^/]+)/" + regexpStr
2014-08-11 16:15:39 +00:00
}
2014-06-12 12:50:29 +00:00
}
2015-12-16 15:11:03 +00:00
} else {
if seg == "*.*" {
params = params[1:]
}
2014-06-12 12:50:29 +00:00
}
2015-04-05 15:11:50 +00:00
reg = strings.TrimRight(strings.TrimRight(reg, "/")+"/"+regexpStr, "/")
2014-08-11 16:02:27 +00:00
t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg)
2014-06-12 12:50:29 +00:00
} else {
subTree := NewTree()
2015-12-16 15:11:03 +00:00
subTree.prefix = seg
t.fixrouters = append(t.fixrouters, subTree)
2014-08-11 16:02:27 +00:00
subTree.addtree(segments[1:], tree, append(wildcards, params...), reg)
2014-06-12 12:50:29 +00:00
}
}
func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) {
for _, v := range t.fixrouters {
filterTreeWithPrefix(v, wildcards, reg)
}
if t.wildcard != nil {
filterTreeWithPrefix(t.wildcard, wildcards, reg)
}
for _, l := range t.leaves {
2014-06-12 12:50:29 +00:00
if reg != "" {
if l.regexps != nil {
l.wildcards = append(wildcards, l.wildcards...)
2015-04-05 15:11:50 +00:00
l.regexps = regexp.MustCompile("^" + reg + "/" + strings.Trim(l.regexps.String(), "^$") + "$")
} else {
for _, v := range l.wildcards {
if v == ":splat" {
reg = reg + "/(.+)"
} else {
reg = reg + "/([^/]+)"
}
}
l.regexps = regexp.MustCompile("^" + reg + "$")
l.wildcards = append(wildcards, l.wildcards...)
}
2014-06-12 12:50:29 +00:00
} else {
l.wildcards = append(wildcards, l.wildcards...)
if l.regexps != nil {
2014-06-12 12:50:29 +00:00
for _, w := range wildcards {
2014-08-11 16:15:39 +00:00
if w == ":splat" {
reg = "(.+)/" + reg
} else {
reg = "([^/]+)/" + reg
}
2014-06-12 12:50:29 +00:00
}
l.regexps = regexp.MustCompile("^" + reg + strings.Trim(l.regexps.String(), "^$") + "$")
2014-06-12 12:50:29 +00:00
}
}
}
2014-06-08 12:24:01 +00:00
}
2015-09-08 15:49:24 +00:00
// AddRouter call addseg function
2014-06-04 15:07:57 +00:00
func (t *Tree) AddRouter(pattern string, runObject interface{}) {
t.addseg(splitPath(pattern), runObject, nil, "")
}
// "/"
// "admin" ->
func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, reg string) {
if len(segments) == 0 {
if reg != "" {
2015-12-16 15:11:03 +00:00
t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards, regexps: regexp.MustCompile("^" + reg + "$")})
2014-06-04 15:07:57 +00:00
} else {
t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards})
2014-06-04 15:07:57 +00:00
}
} else {
seg := segments[0]
iswild, params, regexpStr := splitSegment(seg)
2015-12-16 15:11:03 +00:00
// if it's ? meaning can igone this, so add one more rule for it
if len(params) > 0 && params[0] == ":" {
t.addseg(segments[1:], route, wildcards, reg)
params = params[1:]
}
//Rule: /login/*/access match /login/2009/11/access
//if already has *, and when loop the access, should as a regexpStr
2014-06-23 07:28:53 +00:00
if !iswild && utils.InSlice(":splat", wildcards) {
iswild = true
regexpStr = seg
}
2015-12-16 15:11:03 +00:00
//Rule: /user/:id/*
2014-06-30 15:49:11 +00:00
if seg == "*" && len(wildcards) > 0 && reg == "" {
regexpStr = "(.+)"
}
2014-06-04 15:07:57 +00:00
if iswild {
if t.wildcard == nil {
t.wildcard = NewTree()
}
2014-06-12 12:50:29 +00:00
if regexpStr != "" {
if reg == "" {
2014-06-23 07:28:53 +00:00
rr := ""
2014-06-12 12:50:29 +00:00
for _, w := range wildcards {
2014-06-23 07:28:53 +00:00
if w == ":splat" {
rr = rr + "(.+)/"
} else {
rr = rr + "([^/]+)/"
}
2014-06-12 12:50:29 +00:00
}
2014-06-23 07:28:53 +00:00
regexpStr = rr + regexpStr
2014-06-12 12:50:29 +00:00
} else {
regexpStr = "/" + regexpStr
}
2014-06-18 15:32:47 +00:00
} else if reg != "" {
if seg == "*.*" {
regexpStr = "/([^.]+).(.+)"
2015-12-16 15:11:03 +00:00
params = params[1:]
} else {
2016-03-11 05:00:58 +00:00
for range params {
regexpStr = "/([^/]+)" + regexpStr
2014-06-18 15:32:47 +00:00
}
}
2015-12-16 15:11:03 +00:00
} else {
if seg == "*.*" {
params = params[1:]
}
2014-06-12 12:50:29 +00:00
}
2014-06-04 15:07:57 +00:00
t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr)
} else {
2015-12-16 15:11:03 +00:00
var subTree *Tree
2016-02-12 06:45:45 +00:00
for _, sub := range t.fixrouters {
if sub.prefix == seg {
subTree = sub
2015-12-16 15:11:03 +00:00
break
}
}
2016-02-12 06:45:45 +00:00
if subTree == nil {
2014-06-04 15:07:57 +00:00
subTree = NewTree()
2015-12-16 15:11:03 +00:00
subTree.prefix = seg
t.fixrouters = append(t.fixrouters, subTree)
2014-06-04 15:07:57 +00:00
}
subTree.addseg(segments[1:], route, wildcards, reg)
}
}
}
2015-09-08 15:49:24 +00:00
// Match router to runObject & params
2015-12-11 05:51:01 +00:00
func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{}) {
2014-06-04 15:07:57 +00:00
if len(pattern) == 0 || pattern[0] != '/' {
2015-12-11 05:51:01 +00:00
return nil
2014-06-04 15:07:57 +00:00
}
2015-12-16 15:43:32 +00:00
w := make([]string, 0, 20)
2017-03-10 01:28:25 +00:00
return t.match(pattern[1:], pattern, w, ctx)
2014-06-04 15:07:57 +00:00
}
2017-03-10 01:28:25 +00:00
func (t *Tree) match(treePattern string, pattern string, wildcardValues []string, ctx *context.Context) (runObject interface{}) {
2015-12-16 15:11:03 +00:00
if len(pattern) > 0 {
i := 0
for ; i < len(pattern) && pattern[i] == '/'; i++ {
}
pattern = pattern[i:]
}
2014-06-04 15:07:57 +00:00
// Handle leaf nodes:
2015-12-16 15:11:03 +00:00
if len(pattern) == 0 {
for _, l := range t.leaves {
2017-03-10 01:28:25 +00:00
if ok := l.match(treePattern, wildcardValues, ctx); ok {
2015-12-11 05:51:01 +00:00
return l.runObject
2014-06-04 15:07:57 +00:00
}
}
if t.wildcard != nil {
for _, l := range t.wildcard.leaves {
2017-03-10 01:28:25 +00:00
if ok := l.match(treePattern, wildcardValues, ctx); ok {
2015-12-11 05:51:01 +00:00
return l.runObject
}
2014-06-08 12:24:01 +00:00
}
}
2015-12-11 05:51:01 +00:00
return nil
2014-06-04 15:07:57 +00:00
}
2015-12-16 15:11:03 +00:00
var seg string
i, l := 0, len(pattern)
for ; i < l && pattern[i] != '/'; i++ {
}
if i == 0 {
seg = pattern
pattern = ""
} else {
seg = pattern[:i]
pattern = pattern[i:]
}
for _, subTree := range t.fixrouters {
if subTree.prefix == seg {
2017-03-10 01:28:25 +00:00
if len(pattern) != 0 && pattern[0] == '/' {
treePattern = pattern[1:]
} else {
treePattern = pattern
}
runObject = subTree.match(treePattern, pattern, wildcardValues, ctx)
2015-12-16 15:11:03 +00:00
if runObject != nil {
break
}
}
}
2015-12-17 16:14:28 +00:00
if runObject == nil && len(t.fixrouters) > 0 {
2015-12-16 15:11:03 +00:00
// Filter the .json .xml .html extension
for _, str := range allowSuffixExt {
if strings.HasSuffix(seg, str) {
for _, subTree := range t.fixrouters {
if subTree.prefix == seg[:len(seg)-len(str)] {
2017-03-10 01:28:25 +00:00
runObject = subTree.match(treePattern, pattern, wildcardValues, ctx)
2015-12-16 15:11:03 +00:00
if runObject != nil {
ctx.Input.SetParam(":ext", str[1:])
}
}
2014-06-08 12:24:01 +00:00
}
}
}
2014-06-04 15:07:57 +00:00
}
if runObject == nil && t.wildcard != nil {
2017-03-10 01:28:25 +00:00
runObject = t.wildcard.match(treePattern, pattern, append(wildcardValues, seg), ctx)
2014-06-04 15:07:57 +00:00
}
2015-12-16 15:11:03 +00:00
2015-12-17 16:14:28 +00:00
if runObject == nil && len(t.leaves) > 0 {
2015-12-16 15:11:03 +00:00
wildcardValues = append(wildcardValues, seg)
2015-12-17 13:31:44 +00:00
start, i := 0, 0
for ; i < len(pattern); i++ {
if pattern[i] == '/' {
if i != 0 && start < len(pattern) {
wildcardValues = append(wildcardValues, pattern[start:i])
}
start = i + 1
continue
}
}
if start > 0 {
wildcardValues = append(wildcardValues, pattern[start:i])
}
for _, l := range t.leaves {
2017-03-10 01:28:25 +00:00
if ok := l.match(treePattern, wildcardValues, ctx); ok {
2015-12-11 05:51:01 +00:00
return l.runObject
2014-06-04 15:07:57 +00:00
}
}
}
2015-12-11 05:51:01 +00:00
return runObject
2014-06-04 15:07:57 +00:00
}
type leafInfo struct {
// names of wildcards that lead to this leaf. eg, ["id" "name"] for the wildcard ":id" and ":name"
wildcards []string
// if the leaf is regexp
regexps *regexp.Regexp
runObject interface{}
}
2017-03-10 01:28:25 +00:00
func (leaf *leafInfo) match(treePattern string, wildcardValues []string, ctx *context.Context) (ok bool) {
2015-12-16 15:11:03 +00:00
//fmt.Println("Leaf:", wildcardValues, leaf.wildcards, leaf.regexps)
2014-06-04 15:07:57 +00:00
if leaf.regexps == nil {
2016-03-10 13:59:50 +00:00
if len(wildcardValues) == 0 && len(leaf.wildcards) == 0 { // static path
2015-12-11 05:51:01 +00:00
return true
2014-06-04 15:07:57 +00:00
}
// match *
if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" {
2017-03-10 01:28:25 +00:00
ctx.Input.SetParam(":splat", treePattern)
2015-12-11 05:51:01 +00:00
return true
2014-06-04 15:07:57 +00:00
}
2015-12-16 15:11:03 +00:00
// match *.* or :id
if len(leaf.wildcards) >= 2 && leaf.wildcards[len(leaf.wildcards)-2] == ":path" && leaf.wildcards[len(leaf.wildcards)-1] == ":ext" {
if len(leaf.wildcards) == 2 {
2014-06-08 12:24:01 +00:00
lastone := wildcardValues[len(wildcardValues)-1]
strs := strings.SplitN(lastone, ".", 2)
if len(strs) == 2 {
2015-12-16 15:11:03 +00:00
ctx.Input.SetParam(":ext", strs[1])
2014-06-08 12:24:01 +00:00
}
2015-12-16 15:11:03 +00:00
ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[:len(wildcardValues)-1]...), strs[0]))
2015-12-11 05:51:01 +00:00
return true
2015-12-16 15:11:03 +00:00
} else if len(wildcardValues) < 2 {
2015-12-11 05:51:01 +00:00
return false
2014-11-02 13:01:51 +00:00
}
2015-12-16 15:11:03 +00:00
var index int
for index = 0; index < len(leaf.wildcards)-2; index++ {
ctx.Input.SetParam(leaf.wildcards[index], wildcardValues[index])
}
lastone := wildcardValues[len(wildcardValues)-1]
strs := strings.SplitN(lastone, ".", 2)
if len(strs) == 2 {
ctx.Input.SetParam(":ext", strs[1])
}
if index > (len(wildcardValues) - 1) {
ctx.Input.SetParam(":path", "")
} else {
ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[index:len(wildcardValues)-1]...), strs[0]))
}
2015-12-16 15:11:03 +00:00
return true
2014-06-04 15:07:57 +00:00
}
2015-12-16 15:11:03 +00:00
// match :id
if len(leaf.wildcards) != len(wildcardValues) {
2015-12-11 05:51:01 +00:00
return false
2014-06-04 15:07:57 +00:00
}
2015-12-16 15:11:03 +00:00
for j, v := range leaf.wildcards {
ctx.Input.SetParam(v, wildcardValues[j])
}
2015-12-11 05:51:01 +00:00
return true
2014-06-04 15:07:57 +00:00
}
2014-06-12 12:50:29 +00:00
if !leaf.regexps.MatchString(path.Join(wildcardValues...)) {
2015-12-11 05:51:01 +00:00
return false
2014-06-04 15:07:57 +00:00
}
2014-06-12 12:50:29 +00:00
matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...))
2014-06-04 15:07:57 +00:00
for i, match := range matches[1:] {
2016-01-23 11:13:19 +00:00
if i < len(leaf.wildcards) {
ctx.Input.SetParam(leaf.wildcards[i], match)
}
2014-06-04 15:07:57 +00:00
}
2015-12-11 05:51:01 +00:00
return true
2014-06-04 15:07:57 +00:00
}
// "/" -> []
// "/admin" -> ["admin"]
// "/admin/" -> ["admin"]
// "/admin/users" -> ["admin", "users"]
func splitPath(key string) []string {
key = strings.Trim(key, "/ ")
2014-12-19 07:33:51 +00:00
if key == "" {
return []string{}
}
return strings.Split(key, "/")
2014-06-04 15:07:57 +00:00
}
// "admin" -> false, nil, ""
// ":id" -> true, [:id], ""
2014-06-08 12:24:01 +00:00
// "?:id" -> true, [: :id], "" : meaning can empty
2014-06-04 15:07:57 +00:00
// ":id:int" -> true, [:id], ([0-9]+)
// ":name:string" -> true, [:name], ([\w]+)
// ":id([0-9]+)" -> true, [:id], ([0-9]+)
// ":id([0-9]+)_:name" -> true, [:id :name], ([0-9]+)_(.+)
// "cms_:id_:page.html" -> true, [:id_ :page], cms_(.+)(.+).html
2016-01-07 12:55:28 +00:00
// "cms_:id(.+)_:page.html" -> true, [:id :page], cms_(.+)_(.+).html
2014-06-04 15:07:57 +00:00
// "*" -> true, [:splat], ""
// "*.*" -> true,[. :path :ext], "" . meaning separator
func splitSegment(key string) (bool, []string, string) {
if strings.HasPrefix(key, "*") {
if key == "*.*" {
return true, []string{".", ":path", ":ext"}, ""
}
2015-09-08 15:49:24 +00:00
return true, []string{":splat"}, ""
2014-06-04 15:07:57 +00:00
}
if strings.ContainsAny(key, ":") {
var paramsNum int
var out []rune
var start bool
var startexp bool
var param []rune
var expt []rune
var skipnum int
params := []string{}
reg := regexp.MustCompile(`[a-zA-Z0-9_]+`)
2014-06-04 15:07:57 +00:00
for i, v := range key {
if skipnum > 0 {
2015-09-08 15:49:24 +00:00
skipnum--
2014-06-04 15:07:57 +00:00
continue
}
if start {
//:id:int and :name:string
if v == ':' {
if len(key) >= i+4 {
if key[i+1:i+4] == "int" {
out = append(out, []rune("([0-9]+)")...)
params = append(params, ":"+string(param))
start = false
startexp = false
skipnum = 3
param = make([]rune, 0)
2015-09-08 15:49:24 +00:00
paramsNum++
2014-06-04 15:07:57 +00:00
continue
}
}
if len(key) >= i+7 {
if key[i+1:i+7] == "string" {
out = append(out, []rune(`([\w]+)`)...)
params = append(params, ":"+string(param))
2015-09-08 15:49:24 +00:00
paramsNum++
2014-06-04 15:07:57 +00:00
start = false
startexp = false
skipnum = 6
param = make([]rune, 0)
continue
}
}
}
// params only support a-zA-Z0-9
if reg.MatchString(string(v)) {
param = append(param, v)
continue
}
if v != '(' {
out = append(out, []rune(`(.+)`)...)
params = append(params, ":"+string(param))
param = make([]rune, 0)
2015-09-08 15:49:24 +00:00
paramsNum++
2014-06-04 15:07:57 +00:00
start = false
startexp = false
}
}
if startexp {
if v != ')' {
expt = append(expt, v)
continue
}
}
2016-01-23 11:13:19 +00:00
// Escape Sequence '\'
if i > 0 && key[i-1] == '\\' {
out = append(out, v)
} else if v == ':' {
2014-06-04 15:07:57 +00:00
param = make([]rune, 0)
start = true
} else if v == '(' {
startexp = true
start = false
2016-01-23 11:13:19 +00:00
if len(param) > 0 {
params = append(params, ":"+string(param))
param = make([]rune, 0)
}
2015-09-08 15:49:24 +00:00
paramsNum++
2014-06-04 15:07:57 +00:00
expt = make([]rune, 0)
expt = append(expt, '(')
} else if v == ')' {
startexp = false
expt = append(expt, ')')
out = append(out, expt...)
param = make([]rune, 0)
} else if v == '?' {
params = append(params, ":")
} else {
out = append(out, v)
}
}
if len(param) > 0 {
if paramsNum > 0 {
out = append(out, []rune(`(.+)`)...)
}
params = append(params, ":"+string(param))
}
return true, params, string(out)
}
2015-09-08 15:49:24 +00:00
return false, nil, ""
2014-06-04 15:07:57 +00:00
}