uplink/app/streams/metadata.go

107 lines
1.9 KiB
Go

package streams
import (
"fmt"
"sort"
"strings"
"unicode"
)
func quote(text string) string {
text = strings.Replace(text, "\\", "\\\\", -1)
text = strings.Replace(text, "'", "\\'", -1)
text = "'" + text + "'"
return text
}
func unquote(text string) string {
if strings.HasPrefix(text, "'") && strings.HasSuffix(text, "'") {
text = text[1 : len(text)-1]
text = strings.Replace(text, "\\'", "'", -1)
text = strings.Replace(text, "\\\\", "\\", -1)
}
return text
}
type Metadata map[string]string
func (meta Metadata) sortedKeys() []string {
keys := make([]string, len(meta))
i := 0
for key, _ := range meta {
keys[i] = key
i++
}
sort.Strings(keys)
return keys
}
func decodeMetadataItem(text string, metadata *Metadata) (err error) {
parts := strings.SplitN(text, "=", 2)
if len(parts) < 2 {
err = fmt.Errorf("expected key=value but only got key (in %q)", text)
return
}
parts[1] = unquote(parts[1])
(*metadata)[parts[0]] = parts[1]
return
}
func DecodeMetadata(source string) (meta Metadata, err error) {
// name='value'; name='value';name='value';
meta = make(Metadata)
escape := false
quoted := false
fieldsFunc := func(c rune) (retval bool) {
retval = false
if escape {
escape = false
} else {
switch {
case unicode.IsSpace(c) && !quoted:
retval = true
return
case c == '\'':
quoted = !quoted
case c == '\\' && quoted:
escape = true
case c == ';' && !quoted:
retval = true
return
}
}
return
}
for _, field := range strings.FieldsFunc(source, fieldsFunc) {
if len(field) == 0 {
continue
}
if err = decodeMetadataItem(field, &meta); err != nil {
return
}
}
return
}
func (meta Metadata) String() string {
mstr := ""
if meta != nil {
for _, key := range meta.sortedKeys() {
value := meta[key]
mstr += fmt.Sprintf("%s=%s;", key, quote(value))
}
}
return mstr
}
func (meta Metadata) Bytes() (buf []byte) {
return []byte(meta.String())
}