mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-06-23 14:04:09 +08:00
196 lines
4.5 KiB
Go
196 lines
4.5 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// 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
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// 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.
|
|
|
|
package appsrv
|
|
|
|
import (
|
|
"fmt"
|
|
"path"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"yunion.io/x/log"
|
|
)
|
|
|
|
type RadixNode struct {
|
|
data interface{}
|
|
stringNodes map[string]*RadixNode
|
|
regexpNodes []*RegexpNode
|
|
segNames map[int]string
|
|
}
|
|
|
|
type RegexpNode struct {
|
|
node *RadixNode
|
|
regStr string
|
|
}
|
|
|
|
func NewRadix() *RadixNode {
|
|
return &RadixNode{
|
|
data: nil,
|
|
stringNodes: make(map[string]*RadixNode, 0),
|
|
regexpNodes: make([]*RegexpNode, 0),
|
|
segNames: nil,
|
|
}
|
|
}
|
|
|
|
func NewRegexpNode(node *RadixNode, regStr string) *RegexpNode {
|
|
return &RegexpNode{
|
|
node: node,
|
|
regStr: regStr,
|
|
}
|
|
}
|
|
|
|
func isRegstrInRegexpNodes(regNodes []*RegexpNode, regStr string) (*RadixNode, bool) {
|
|
for i := 0; i < len(regNodes); i++ {
|
|
if regNodes[i].regStr == regStr {
|
|
return regNodes[i].node, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func isRegexSegment(seg string) bool {
|
|
if len(seg) > 2 && seg[0] == '<' && seg[len(seg)-1] == '>' {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (r *RadixNode) Add(segments []string, data interface{}) error {
|
|
err := r.add(segments, data, 1, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("Add Node error: %s %s", err, strings.Join(segments, "/"))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *RadixNode) add(segments []string, data interface{}, depth int, segNames map[int]string) error {
|
|
if len(segments) == 0 {
|
|
if r.data != nil {
|
|
return fmt.Errorf("Duplicate data for node")
|
|
} else {
|
|
r.data = data
|
|
r.segNames = segNames
|
|
return nil
|
|
}
|
|
} else {
|
|
var nextNode *RadixNode
|
|
if isRegexSegment(segments[0]) {
|
|
var (
|
|
regStr string
|
|
segName string
|
|
segStr = segments[0][1 : len(segments[0])-1]
|
|
splitIndex = strings.IndexByte(segStr, ':')
|
|
)
|
|
|
|
if splitIndex < 0 {
|
|
regStr = ".*" // match anything
|
|
segName = "<" + segStr + ">"
|
|
} else {
|
|
regStr = segStr[splitIndex+1:]
|
|
segName = "<" + segStr[0:splitIndex] + ">"
|
|
}
|
|
|
|
if segNames == nil {
|
|
segNames = make(map[int]string, 0)
|
|
}
|
|
segNames[depth-1] = segName
|
|
|
|
if node, ok := isRegstrInRegexpNodes(r.regexpNodes, regStr); ok {
|
|
nextNode = node
|
|
} else {
|
|
nextNode = NewRadix()
|
|
regNode := NewRegexpNode(nextNode, regStr)
|
|
r.regexpNodes = append(r.regexpNodes, regNode)
|
|
}
|
|
} else {
|
|
if node, ok := r.stringNodes[segments[0]]; ok {
|
|
nextNode = node
|
|
} else {
|
|
nextNode = NewRadix()
|
|
r.stringNodes[segments[0]] = nextNode
|
|
}
|
|
}
|
|
return nextNode.add(segments[1:], data, depth+1, segNames)
|
|
}
|
|
}
|
|
|
|
func (r *RadixNode) Match(segments []string, params map[string]string) interface{} {
|
|
node, _ := r.match(segments)
|
|
if node == nil {
|
|
return nil
|
|
}
|
|
for index, segName := range node.segNames {
|
|
params[segName] = segments[index]
|
|
}
|
|
return node.data
|
|
}
|
|
|
|
func (r *RadixNode) match(segments []string) (*RadixNode, bool) {
|
|
if len(segments) == 0 {
|
|
return r, true
|
|
} else if len(r.stringNodes) == 0 && len(r.regexpNodes) == 0 {
|
|
return r, false
|
|
}
|
|
|
|
if node, ok := r.stringNodes[segments[0]]; ok {
|
|
if rnode, _ := node.match(segments[1:]); rnode != nil && rnode.data != nil {
|
|
return rnode, true
|
|
}
|
|
}
|
|
|
|
var nodeTmp *RadixNode
|
|
for _, regNode := range r.regexpNodes {
|
|
if regexp.MustCompile(regNode.regStr).MatchString(segments[0]) {
|
|
if rnode, fullMatch := regNode.node.match(segments[1:]); rnode != nil && rnode.data != nil {
|
|
if fullMatch {
|
|
return rnode, fullMatch
|
|
} else {
|
|
if nodeTmp != nil {
|
|
log.Errorf("segments %v match mutil node", segments)
|
|
continue
|
|
}
|
|
nodeTmp = rnode
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if nodeTmp != nil {
|
|
return nodeTmp, false
|
|
} else {
|
|
return r, false
|
|
}
|
|
}
|
|
|
|
func (r *RadixNode) Walk(f func(spath string, data interface{})) {
|
|
r.walk("/", f)
|
|
}
|
|
|
|
func (r *RadixNode) walk(fullPath string, f func(spath string, data interface{})) {
|
|
if r.data != nil {
|
|
f(fullPath, r.data)
|
|
}
|
|
|
|
for key, node := range r.stringNodes {
|
|
curPath := path.Join(fullPath, key)
|
|
node.walk(curPath, f)
|
|
}
|
|
for _, regNode := range r.regexpNodes {
|
|
curPath := path.Join(fullPath, regNode.regStr)
|
|
regNode.node.walk(curPath, f)
|
|
}
|
|
}
|