128 lines
3 KiB
Go
128 lines
3 KiB
Go
package kv
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/kvtools/valkeyrie/store"
|
|
"github.com/traefik/paerser/parser"
|
|
)
|
|
|
|
// DecodeToNode converts the labels to a tree of nodes.
|
|
// If any filters are present, labels which do not match the filters are skipped.
|
|
func DecodeToNode(pairs []*store.KVPair, rootName string, filters ...string) (*parser.Node, error) {
|
|
sortedPairs := filterPairs(pairs, filters)
|
|
|
|
exp := regexp.MustCompile(`^\d+$`)
|
|
|
|
var node *parser.Node
|
|
|
|
for i, pair := range sortedPairs {
|
|
if !strings.HasPrefix(pair.Key, rootName+"/") {
|
|
return nil, fmt.Errorf("invalid label root %s", rootName)
|
|
}
|
|
|
|
split := strings.FieldsFunc(pair.Key[len(rootName)+1:], func(c rune) bool { return c == '/' })
|
|
|
|
parts := []string{rootName}
|
|
for _, fragment := range split {
|
|
if exp.MatchString(fragment) {
|
|
parts = append(parts, "["+fragment+"]")
|
|
} else {
|
|
parts = append(parts, fragment)
|
|
}
|
|
}
|
|
|
|
if i == 0 {
|
|
node = &parser.Node{}
|
|
}
|
|
decodeToNode(node, parts, string(pair.Value))
|
|
}
|
|
|
|
return node, nil
|
|
}
|
|
|
|
func decodeToNode(root *parser.Node, path []string, value string) {
|
|
if len(root.Name) == 0 {
|
|
root.Name = path[0]
|
|
}
|
|
|
|
// it's a leaf or not -> children
|
|
if len(path) > 1 {
|
|
if n := containsNode(root.Children, path[1]); n != nil {
|
|
// the child already exists
|
|
decodeToNode(n, path[1:], value)
|
|
} else {
|
|
// new child
|
|
child := &parser.Node{Name: path[1]}
|
|
decodeToNode(child, path[1:], value)
|
|
root.Children = append(root.Children, child)
|
|
}
|
|
} else {
|
|
root.Value = value
|
|
}
|
|
}
|
|
|
|
func containsNode(nodes []*parser.Node, name string) *parser.Node {
|
|
for _, n := range nodes {
|
|
if strings.EqualFold(name, n.Name) {
|
|
return n
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func filterPairs(pairs []*store.KVPair, filters []string) []*store.KVPair {
|
|
exp := regexp.MustCompile(`^(.+)/\d+$`)
|
|
|
|
sort.Slice(pairs, func(i, j int) bool {
|
|
return pairs[i].Key < pairs[j].Key
|
|
})
|
|
|
|
simplePairs := map[string]*store.KVPair{}
|
|
slicePairs := map[string][]string{}
|
|
|
|
for _, pair := range pairs {
|
|
if len(filters) == 0 {
|
|
// Slice of simple type
|
|
if exp.MatchString(pair.Key) {
|
|
sanitizedKey := exp.FindStringSubmatch(pair.Key)[1]
|
|
slicePairs[sanitizedKey] = append(slicePairs[sanitizedKey], string(pair.Value))
|
|
} else {
|
|
simplePairs[pair.Key] = pair
|
|
}
|
|
continue
|
|
}
|
|
|
|
for _, filter := range filters {
|
|
if len(pair.Key) >= len(filter) && strings.EqualFold(pair.Key[:len(filter)], filter) {
|
|
// Slice of simple type
|
|
if exp.MatchString(pair.Key) {
|
|
sanitizedKey := exp.FindStringSubmatch(pair.Key)[1]
|
|
slicePairs[sanitizedKey] = append(slicePairs[sanitizedKey], string(pair.Value))
|
|
} else {
|
|
simplePairs[pair.Key] = pair
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
var sortedPairs []*store.KVPair
|
|
for k, v := range slicePairs {
|
|
delete(simplePairs, k)
|
|
sortedPairs = append(sortedPairs, &store.KVPair{Key: k, Value: []byte(strings.Join(v, ","))})
|
|
}
|
|
|
|
for _, v := range simplePairs {
|
|
sortedPairs = append(sortedPairs, v)
|
|
}
|
|
|
|
sort.Slice(sortedPairs, func(i, j int) bool {
|
|
return sortedPairs[i].Key < sortedPairs[j].Key
|
|
})
|
|
|
|
return sortedPairs
|
|
}
|