frostfs-node/pkg/util/attributes/parser.go

70 lines
1.6 KiB
Go

package attributes
import (
"fmt"
"strings"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
)
const keyValueSeparator = ":"
// ReadNodeAttributes parses node attributes from list of string in "Key:Value" format
// and writes them into netmap.NodeInfo instance. Supports escaped symbols
// "\:", "\/" and "\\".
func ReadNodeAttributes(dst *netmap.NodeInfo, attrs []string) error {
cache := make(map[string]struct{}, len(attrs))
for i := range attrs {
line := replaceEscaping(attrs[i], false) // replaced escaped symbols with non-printable symbols
k, v, found := strings.Cut(line, keyValueSeparator)
if !found {
return fmt.Errorf("wrong format for node attribute: '%s'", attrs[i])
}
_, ok := cache[k]
if ok {
return fmt.Errorf("duplicated keys %s", k)
}
cache[k] = struct{}{}
// replace non-printable symbols with escaped symbols without escape character
k = replaceEscaping(k, true)
v = replaceEscaping(v, true)
if k == "" {
return fmt.Errorf("empty key in node attribute: '%s'", attrs[i])
} else if v == "" {
return fmt.Errorf("empty value in node attribute: '%s'", attrs[i])
}
dst.SetAttribute(k, v)
}
return nil
}
func replaceEscaping(target string, rollback bool) (s string) {
const escChar = `\`
var (
oldKVSep = escChar + keyValueSeparator
oldEsc = escChar + escChar
newKVSep = string(uint8(2))
newEsc = string(uint8(3))
)
if rollback {
oldKVSep, oldEsc = newKVSep, newEsc
newKVSep = keyValueSeparator
newEsc = escChar
}
s = strings.ReplaceAll(target, oldEsc, newEsc)
s = strings.ReplaceAll(s, oldKVSep, newKVSep)
return
}