3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-12-22 10:42:52 +01:00

refactor cap line splitting

This commit is contained in:
Shivaram Lingamneni 2019-11-09 20:31:39 -05:00
parent b7076f4c9e
commit a75d26a46b
5 changed files with 126 additions and 83 deletions

View File

@ -180,6 +180,7 @@ CAPDEFS = [
] ]
def validate_defs(): def validate_defs():
CAPDEFS.sort(key=lambda d: d.name)
numCaps = len(CAPDEFS) numCaps = len(CAPDEFS)
numNames = len(set(capdef.name for capdef in CAPDEFS)) numNames = len(set(capdef.name for capdef in CAPDEFS))
if numCaps != numNames: if numCaps != numNames:

View File

@ -13,10 +13,6 @@ const (
) )
const ( const (
// Acc is the proposed IRCv3 capability named "draft/acc":
// https://github.com/ircv3/ircv3-specifications/pull/276
Acc Capability = iota
// AccountNotify is the IRCv3 capability named "account-notify": // AccountNotify is the IRCv3 capability named "account-notify":
// https://ircv3.net/specs/extensions/account-notify-3.1.html // https://ircv3.net/specs/extensions/account-notify-3.1.html
AccountNotify Capability = iota AccountNotify Capability = iota
@ -41,6 +37,34 @@ const (
// https://ircv3.net/specs/extensions/chghost-3.2.html // https://ircv3.net/specs/extensions/chghost-3.2.html
ChgHost Capability = iota ChgHost Capability = iota
// Acc is the proposed IRCv3 capability named "draft/acc":
// https://github.com/ircv3/ircv3-specifications/pull/276
Acc Capability = iota
// EventPlayback is the Proposed IRCv3 capability named "draft/event-playback":
// https://github.com/ircv3/ircv3-specifications/pull/362
EventPlayback Capability = iota
// LabeledResponse is the draft IRCv3 capability named "draft/labeled-response-0.2":
// https://ircv3.net/specs/extensions/labeled-response.html
LabeledResponse Capability = iota
// Languages is the proposed IRCv3 capability named "draft/languages":
// https://gist.github.com/DanielOaks/8126122f74b26012a3de37db80e4e0c6
Languages Capability = iota
// Rename is the proposed IRCv3 capability named "draft/rename":
// https://github.com/SaberUK/ircv3-specifications/blob/rename/extensions/rename.md
Rename Capability = iota
// Resume is the proposed IRCv3 capability named "draft/resume-0.5":
// https://github.com/DanielOaks/ircv3-specifications/blob/master+resume/extensions/resume.md
Resume Capability = iota
// SetName is the proposed IRCv3 capability named "draft/setname":
// https://github.com/ircv3/ircv3-specifications/pull/361
SetName Capability = iota
// EchoMessage is the IRCv3 capability named "echo-message": // EchoMessage is the IRCv3 capability named "echo-message":
// https://ircv3.net/specs/extensions/echo-message-3.2.html // https://ircv3.net/specs/extensions/echo-message-3.2.html
EchoMessage Capability = iota EchoMessage Capability = iota
@ -53,18 +77,6 @@ const (
// https://ircv3.net/specs/extensions/invite-notify-3.2.html // https://ircv3.net/specs/extensions/invite-notify-3.2.html
InviteNotify Capability = iota InviteNotify Capability = iota
// LabeledResponse is the draft IRCv3 capability named "draft/labeled-response-0.2":
// https://ircv3.net/specs/extensions/labeled-response.html
LabeledResponse Capability = iota
// Languages is the proposed IRCv3 capability named "draft/languages":
// https://gist.github.com/DanielOaks/8126122f74b26012a3de37db80e4e0c6
Languages Capability = iota
// MaxLine is the Oragono-specific capability named "oragono.io/maxline-2":
// https://oragono.io/maxline-2
MaxLine Capability = iota
// MessageTags is the IRCv3 capability named "message-tags": // MessageTags is the IRCv3 capability named "message-tags":
// https://ircv3.net/specs/extensions/message-tags.html // https://ircv3.net/specs/extensions/message-tags.html
MessageTags Capability = iota MessageTags Capability = iota
@ -73,13 +85,17 @@ const (
// https://ircv3.net/specs/extensions/multi-prefix-3.1.html // https://ircv3.net/specs/extensions/multi-prefix-3.1.html
MultiPrefix Capability = iota MultiPrefix Capability = iota
// Rename is the proposed IRCv3 capability named "draft/rename": // Bouncer is the Oragono-specific capability named "oragono.io/bnc":
// https://github.com/SaberUK/ircv3-specifications/blob/rename/extensions/rename.md // https://oragono.io/bnc
Rename Capability = iota Bouncer Capability = iota
// Resume is the proposed IRCv3 capability named "draft/resume-0.5": // MaxLine is the Oragono-specific capability named "oragono.io/maxline-2":
// https://github.com/DanielOaks/ircv3-specifications/blob/master+resume/extensions/resume.md // https://oragono.io/maxline-2
Resume Capability = iota MaxLine Capability = iota
// Nope is the Oragono vendor capability named "oragono.io/nope":
// https://oragono.io/nope
Nope Capability = iota
// SASL is the IRCv3 capability named "sasl": // SASL is the IRCv3 capability named "sasl":
// https://ircv3.net/specs/extensions/sasl-3.2.html // https://ircv3.net/specs/extensions/sasl-3.2.html
@ -89,10 +105,6 @@ const (
// https://ircv3.net/specs/extensions/server-time-3.2.html // https://ircv3.net/specs/extensions/server-time-3.2.html
ServerTime Capability = iota ServerTime Capability = iota
// SetName is the proposed IRCv3 capability named "draft/setname":
// https://github.com/ircv3/ircv3-specifications/pull/361
SetName Capability = iota
// STS is the IRCv3 capability named "sts": // STS is the IRCv3 capability named "sts":
// https://ircv3.net/specs/extensions/sts.html // https://ircv3.net/specs/extensions/sts.html
STS Capability = iota STS Capability = iota
@ -101,56 +113,44 @@ const (
// https://ircv3.net/specs/extensions/userhost-in-names-3.2.html // https://ircv3.net/specs/extensions/userhost-in-names-3.2.html
UserhostInNames Capability = iota UserhostInNames Capability = iota
// Bouncer is the Oragono-specific capability named "oragono.io/bnc":
// https://oragono.io/bnc
Bouncer Capability = iota
// ZNCSelfMessage is the ZNC vendor capability named "znc.in/self-message":
// https://wiki.znc.in/Query_buffers
ZNCSelfMessage Capability = iota
// EventPlayback is the Proposed IRCv3 capability named "draft/event-playback":
// https://github.com/ircv3/ircv3-specifications/pull/362
EventPlayback Capability = iota
// ZNCPlayback is the ZNC vendor capability named "znc.in/playback": // ZNCPlayback is the ZNC vendor capability named "znc.in/playback":
// https://wiki.znc.in/Playback // https://wiki.znc.in/Playback
ZNCPlayback Capability = iota ZNCPlayback Capability = iota
// Nope is the Oragono vendor capability named "oragono.io/nope": // ZNCSelfMessage is the ZNC vendor capability named "znc.in/self-message":
// https://oragono.io/nope // https://wiki.znc.in/Query_buffers
Nope Capability = iota ZNCSelfMessage Capability = iota
) )
// `capabilityNames[capab]` is the string name of the capability `capab` // `capabilityNames[capab]` is the string name of the capability `capab`
var ( var (
capabilityNames = [numCapabs]string{ capabilityNames = [numCapabs]string{
"draft/acc",
"account-notify", "account-notify",
"account-tag", "account-tag",
"away-notify", "away-notify",
"batch", "batch",
"cap-notify", "cap-notify",
"chghost", "chghost",
"draft/acc",
"draft/event-playback",
"draft/labeled-response-0.2",
"draft/languages",
"draft/rename",
"draft/resume-0.5",
"draft/setname",
"echo-message", "echo-message",
"extended-join", "extended-join",
"invite-notify", "invite-notify",
"draft/labeled-response-0.2",
"draft/languages",
"oragono.io/maxline-2",
"message-tags", "message-tags",
"multi-prefix", "multi-prefix",
"draft/rename", "oragono.io/bnc",
"draft/resume-0.5", "oragono.io/maxline-2",
"oragono.io/nope",
"sasl", "sasl",
"server-time", "server-time",
"draft/setname",
"sts", "sts",
"userhost-in-names", "userhost-in-names",
"oragono.io/bnc",
"znc.in/self-message",
"draft/event-playback",
"znc.in/playback", "znc.in/playback",
"oragono.io/nope", "znc.in/self-message",
} }
) )

View File

@ -4,9 +4,7 @@
package caps package caps
import ( import (
"bytes" "fmt"
"sort"
"github.com/oragono/oragono/irc/utils" "github.com/oragono/oragono/irc/utils"
) )
@ -95,7 +93,8 @@ const maxPayloadLength = 440
// Strings returns all of our enabled capabilities as a slice of strings. // Strings returns all of our enabled capabilities as a slice of strings.
func (s *Set) Strings(version Version, values Values) (result []string) { func (s *Set) Strings(version Version, values Values) (result []string) {
var strs sort.StringSlice var t utils.TokenLineBuilder
t.Initialize(maxPayloadLength, " ")
var capab Capability var capab Capability
asSlice := s[:] asSlice := s[:]
@ -108,37 +107,15 @@ func (s *Set) Strings(version Version, values Values) (result []string) {
if version >= Cap302 { if version >= Cap302 {
val, exists := values[capab] val, exists := values[capab]
if exists { if exists {
capString += "=" + val capString = fmt.Sprintf("%s=%s", capString, val)
} }
} }
strs = append(strs, capString) t.Add(capString)
} }
if len(strs) == 0 { result = t.Lines()
return []string{""} if result == nil {
result = []string{""}
} }
// sort the cap string before we send it out
sort.Sort(strs)
var buf bytes.Buffer
for _, str := range strs {
tokenLen := len(str)
if buf.Len() != 0 {
tokenLen += 1
}
if maxPayloadLength < buf.Len()+tokenLen {
result = append(result, buf.String())
buf.Reset()
}
if buf.Len() != 0 {
buf.WriteByte(' ')
}
buf.WriteString(str)
}
if buf.Len() != 0 {
result = append(result, buf.String())
}
return return
} }

View File

@ -83,3 +83,44 @@ func MakeSplitMessage(original string, origIs512 bool) (result SplitMessage) {
return return
} }
// TokenLineBuilder is a helper for building IRC lines composed of delimited tokens,
// with a maximum line length.
type TokenLineBuilder struct {
lineLen int
delim string
buf bytes.Buffer
result []string
}
func (t *TokenLineBuilder) Initialize(lineLen int, delim string) {
t.lineLen = lineLen
t.delim = delim
}
// Add adds a token to the line, creating a new line if necessary.
func (t *TokenLineBuilder) Add(token string) {
tokenLen := len(token)
if t.buf.Len() != 0 {
tokenLen += len(t.delim)
}
if t.lineLen < t.buf.Len()+tokenLen {
t.result = append(t.result, t.buf.String())
t.buf.Reset()
}
if t.buf.Len() != 0 {
t.buf.WriteString(t.delim)
}
t.buf.WriteString(token)
}
// Lines terminates the line-building and returns all the lines.
func (t *TokenLineBuilder) Lines() (result []string) {
result = t.result
t.result = nil
if t.buf.Len() != 0 {
result = append(result, t.buf.String())
t.buf.Reset()
}
return
}

View File

@ -58,3 +58,27 @@ func BenchmarkWordWrap(b *testing.B) {
WordWrap(monteCristo, 60) WordWrap(monteCristo, 60)
} }
} }
func TestTokenLineBuilder(t *testing.T) {
lineLen := 400
var tl TokenLineBuilder
tl.Initialize(lineLen, " ")
for _, token := range strings.Fields(monteCristo) {
tl.Add(token)
}
lines := tl.Lines()
if len(lines) != 4 {
t.Errorf("expected 4 lines, got %d", len(lines))
}
for _, line := range lines {
if len(line) > lineLen {
t.Errorf("line length %d exceeds maximum of %d", len(line), lineLen)
}
}
joined := strings.Join(lines, " ")
if joined != monteCristo {
t.Errorf("text incorrectly split into lines: %s instead of %s", joined, monteCristo)
}
}