135 lines
3.7 KiB
Go
135 lines
3.7 KiB
Go
package templating
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// node is an item in a sorted k-ary tree of filter parts. Each child is sorted by its part value.
|
|
// The special value of "*", is always sorted last.
|
|
type node struct {
|
|
separator string
|
|
value string
|
|
children nodes
|
|
template *Template
|
|
}
|
|
|
|
// insert inserts the given string template into the tree. The filter string is separated
|
|
// on the template separator and each part is used as the path in the tree.
|
|
func (n *node) insert(filter string, template *Template) {
|
|
n.separator = template.separator
|
|
n.recursiveInsert(strings.Split(filter, n.separator), template)
|
|
}
|
|
|
|
// recursiveInsert does the actual recursive insertion
|
|
func (n *node) recursiveInsert(values []string, template *Template) {
|
|
// Add the end, set the template
|
|
if len(values) == 0 {
|
|
n.template = template
|
|
return
|
|
}
|
|
|
|
// See if the the current element already exists in the tree. If so, insert the
|
|
// into that sub-tree
|
|
for _, v := range n.children {
|
|
if v.value == values[0] {
|
|
v.recursiveInsert(values[1:], template)
|
|
return
|
|
}
|
|
}
|
|
|
|
// New element, add it to the tree and sort the children
|
|
newNode := &node{value: values[0]}
|
|
n.children = append(n.children, newNode)
|
|
sort.Sort(&n.children)
|
|
|
|
// Now insert the rest of the tree into the new element
|
|
newNode.recursiveInsert(values[1:], template)
|
|
}
|
|
|
|
// search searches for a template matching the input string
|
|
func (n *node) search(line string) *Template {
|
|
separator := n.separator
|
|
return n.recursiveSearch(strings.Split(line, separator))
|
|
}
|
|
|
|
// recursiveSearch performs the actual recursive search
|
|
func (n *node) recursiveSearch(lineParts []string) *Template {
|
|
// nothing to search
|
|
if len(lineParts) == 0 || len(n.children) == 0 {
|
|
return n.template
|
|
}
|
|
|
|
var (
|
|
hasWildcard bool
|
|
length = len(n.children)
|
|
)
|
|
|
|
// exclude last child from search if it is a wildcard. sort.Search expects
|
|
// a lexicographically sorted set of children and we have artificially sorted
|
|
// wildcards to the end of the child set
|
|
// wildcards will be searched separately if no exact match is found
|
|
if hasWildcard = n.children[length-1].value == "*"; hasWildcard {
|
|
length--
|
|
}
|
|
|
|
i := sort.Search(length, func(i int) bool {
|
|
return n.children[i].value >= lineParts[0]
|
|
})
|
|
|
|
// given an exact match is found within children set
|
|
if i < length && n.children[i].value == lineParts[0] {
|
|
// descend into the matching node
|
|
if tmpl := n.children[i].recursiveSearch(lineParts[1:]); tmpl != nil {
|
|
// given a template is found return it
|
|
return tmpl
|
|
}
|
|
}
|
|
|
|
// given no template is found and the last child is a wildcard
|
|
if hasWildcard {
|
|
// also search the wildcard child node
|
|
return n.children[length].recursiveSearch(lineParts[1:])
|
|
}
|
|
|
|
// fallback to returning template at this node
|
|
return n.template
|
|
}
|
|
|
|
// nodes is simply an array of nodes implementing the sorting interface.
|
|
type nodes []*node
|
|
|
|
// Less returns a boolean indicating whether the filter at position j
|
|
// is less than the filter at position k. Filters are order by string
|
|
// comparison of each component parts. A wildcard value "*" is never
|
|
// less than a non-wildcard value.
|
|
//
|
|
// For example, the filters:
|
|
// "*.*"
|
|
// "servers.*"
|
|
// "servers.localhost"
|
|
// "*.localhost"
|
|
//
|
|
// Would be sorted as:
|
|
// "servers.localhost"
|
|
// "servers.*"
|
|
// "*.localhost"
|
|
// "*.*"
|
|
func (n *nodes) Less(j, k int) bool {
|
|
if (*n)[j].value == "*" && (*n)[k].value != "*" {
|
|
return false
|
|
}
|
|
|
|
if (*n)[j].value != "*" && (*n)[k].value == "*" {
|
|
return true
|
|
}
|
|
|
|
return (*n)[j].value < (*n)[k].value
|
|
}
|
|
|
|
// Swap swaps two elements of the array
|
|
func (n *nodes) Swap(i, j int) { (*n)[i], (*n)[j] = (*n)[j], (*n)[i] }
|
|
|
|
// Len returns the length of the array
|
|
func (n *nodes) Len() int { return len(*n) }
|