ircmsg: add ParseTags function to allow parsing of tag-like strings arbitrarily
This commit is contained in:
parent
ea7e22b650
commit
a5eafb7ec8
@ -11,6 +11,8 @@ import (
|
||||
var (
|
||||
// ErrorLineIsEmpty indicates that the given IRC line was empty.
|
||||
ErrorLineIsEmpty = errors.New("Line is empty")
|
||||
// ErrorTagsContainsBadChar indicates that the passed tag string contains a space or newline.
|
||||
ErrorTagsContainsBadChar = errors.New("Tag string contains bad character (such as a space or newline)")
|
||||
)
|
||||
|
||||
// IrcMessage represents an IRC message, as defined by the RFCs and as
|
||||
@ -75,25 +77,10 @@ func parseLine(line string, maxlenTags, maxlenRest int, useMaxLen bool) (IrcMess
|
||||
return ircmsg, ErrorLineIsEmpty
|
||||
}
|
||||
|
||||
// truncate if desired
|
||||
if useMaxLen && len(tags) > maxlenTags {
|
||||
tags = tags[:maxlenTags]
|
||||
}
|
||||
|
||||
for _, fulltag := range strings.Split(tags, ";") {
|
||||
var name string
|
||||
var val TagValue
|
||||
if strings.Contains(fulltag, "=") {
|
||||
val.HasValue = true
|
||||
splittag := strings.SplitN(fulltag, "=", 2)
|
||||
name = splittag[0]
|
||||
val.Value = UnescapeTagValue(splittag[1])
|
||||
} else {
|
||||
name = fulltag
|
||||
val.HasValue = false
|
||||
}
|
||||
|
||||
ircmsg.Tags[name] = val
|
||||
var err error
|
||||
ircmsg.Tags, err = parseTags(tags, maxlenTags, useMaxLen)
|
||||
if err != nil {
|
||||
return ircmsg, err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,3 +103,46 @@ func MakeTags(values ...interface{}) *map[string]TagValue {
|
||||
|
||||
return &tags
|
||||
}
|
||||
|
||||
// ParseTags takes a tag string such as "network=freenode;buffer=#chan;joined=1;topic=some\stopic" and outputs a TagValue map.
|
||||
func ParseTags(tags string) (map[string]TagValue, error) {
|
||||
return parseTags(tags, 0, false)
|
||||
}
|
||||
|
||||
// parseTags does the actual tags parsing for the above user-facing function.
|
||||
func parseTags(tags string, maxlenTags int, useMaxLen bool) (map[string]TagValue, error) {
|
||||
tagMap := make(map[string]TagValue)
|
||||
|
||||
// confirm no bad strings exist
|
||||
if strings.ContainsAny(tags, " \r\n") {
|
||||
return tagMap, ErrorTagsContainsBadChar
|
||||
}
|
||||
|
||||
// truncate if desired
|
||||
if useMaxLen && len(tags) > maxlenTags {
|
||||
tags = tags[:maxlenTags]
|
||||
}
|
||||
|
||||
for _, fulltag := range strings.Split(tags, ";") {
|
||||
// skip empty tag string values
|
||||
if len(fulltag) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
var name string
|
||||
var val TagValue
|
||||
if strings.Contains(fulltag, "=") {
|
||||
val.HasValue = true
|
||||
splittag := strings.SplitN(fulltag, "=", 2)
|
||||
name = splittag[0]
|
||||
val.Value = UnescapeTagValue(splittag[1])
|
||||
} else {
|
||||
name = fulltag
|
||||
val.HasValue = false
|
||||
}
|
||||
|
||||
tagMap[name] = val
|
||||
}
|
||||
|
||||
return tagMap, nil
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package ircmsg
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testcase struct {
|
||||
escaped string
|
||||
@ -56,3 +59,75 @@ func TestUnescape(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tag string tests
|
||||
type testtags struct {
|
||||
raw string
|
||||
tags map[string]TagValue
|
||||
}
|
||||
type testtagswithlen struct {
|
||||
raw string
|
||||
length int
|
||||
tags map[string]TagValue
|
||||
}
|
||||
|
||||
var tagdecodelentests = []testtagswithlen{
|
||||
{"time=12732;re", 512, *MakeTags("time", "12732", "re", nil)},
|
||||
{"time=12732;re", 12, *MakeTags("time", "12732", "r", nil)},
|
||||
{"", 512, *MakeTags()},
|
||||
}
|
||||
var tagdecodetests = []testtags{
|
||||
{"", *MakeTags()},
|
||||
{"time=12732;re", *MakeTags("time", "12732", "re", nil)},
|
||||
}
|
||||
var tagdecodetesterrors = []string{
|
||||
"\r\n",
|
||||
" \r\n",
|
||||
"tags=tesa\r\n",
|
||||
"tags=tested \r\n",
|
||||
}
|
||||
|
||||
func TestDecodeTags(t *testing.T) {
|
||||
for _, pair := range tagdecodelentests {
|
||||
tags, err := parseTags(pair.raw, pair.length, true)
|
||||
if err != nil {
|
||||
t.Error(
|
||||
"For", pair.raw,
|
||||
"Failed to parse tags:", err,
|
||||
)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tags, pair.tags) {
|
||||
t.Error(
|
||||
"For", pair.raw,
|
||||
"expected", pair.tags,
|
||||
"got", tags,
|
||||
)
|
||||
}
|
||||
}
|
||||
for _, pair := range tagdecodetests {
|
||||
tags, err := ParseTags(pair.raw)
|
||||
if err != nil {
|
||||
t.Error(
|
||||
"For", pair.raw,
|
||||
"Failed to parse line:", err,
|
||||
)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tags, pair.tags) {
|
||||
t.Error(
|
||||
"For", pair.raw,
|
||||
"expected", pair.tags,
|
||||
"got", tags,
|
||||
)
|
||||
}
|
||||
}
|
||||
for _, line := range tagdecodetesterrors {
|
||||
_, err := ParseTags(line)
|
||||
if err == nil {
|
||||
t.Error(
|
||||
"Expected to fail parsing", line,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user