mirror of https://github.com/fluffle/goirc
The great state tracker privatisation 2/3: nicks.
This commit is contained in:
parent
bffe946388
commit
4dd8bc72d5
128
state/nick.go
128
state/nick.go
|
@ -4,15 +4,22 @@ import (
|
||||||
"github.com/fluffle/goirc/logging"
|
"github.com/fluffle/goirc/logging"
|
||||||
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A struct representing an IRC nick
|
// A Nick is returned from the state tracker and contains
|
||||||
|
// a copy of the nick state at a particular time.
|
||||||
type Nick struct {
|
type Nick struct {
|
||||||
Nick, Ident, Host, Name string
|
Nick, Ident, Host, Name string
|
||||||
Modes *NickMode
|
Modes *NickMode
|
||||||
lookup map[string]*Channel
|
Channels map[string]*ChanPrivs
|
||||||
chans map[*Channel]*ChanPrivs
|
}
|
||||||
|
|
||||||
|
// Internal bookkeeping struct for nicks.
|
||||||
|
type nick struct {
|
||||||
|
nick, ident, host, name string
|
||||||
|
modes *NickMode
|
||||||
|
lookup map[string]*channel
|
||||||
|
chans map[*channel]*ChanPrivs
|
||||||
}
|
}
|
||||||
|
|
||||||
// A struct representing the modes of an IRC Nick (User Modes)
|
// A struct representing the modes of an IRC Nick (User Modes)
|
||||||
|
@ -43,51 +50,62 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************\
|
/******************************************************************************\
|
||||||
* Nick methods for state management
|
* nick methods for state management
|
||||||
\******************************************************************************/
|
\******************************************************************************/
|
||||||
|
|
||||||
func NewNick(n string) *Nick {
|
func newNick(n string) *nick {
|
||||||
return &Nick{
|
return &nick{
|
||||||
Nick: n,
|
nick: n,
|
||||||
Modes: new(NickMode),
|
modes: new(NickMode),
|
||||||
chans: make(map[*Channel]*ChanPrivs),
|
chans: make(map[*channel]*ChanPrivs),
|
||||||
lookup: make(map[string]*Channel),
|
lookup: make(map[string]*channel),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the Nick is associated with the Channel.
|
// Returns a copy of the internal tracker nick state at this time.
|
||||||
func (nk *Nick) IsOn(ch *Channel) (*ChanPrivs, bool) {
|
// Relies on tracker-level locking for concurrent access.
|
||||||
cp, ok := nk.chans[ch]
|
func (nk *nick) Nick() *Nick {
|
||||||
return cp, ok
|
n := &Nick{
|
||||||
|
Nick: nk.nick,
|
||||||
|
Ident: nk.ident,
|
||||||
|
Host: nk.host,
|
||||||
|
Name: nk.name,
|
||||||
|
Modes: nk.modes.Copy(),
|
||||||
|
Channels: make(map[string]*ChanPrivs),
|
||||||
|
}
|
||||||
|
for c, cp := range nk.chans {
|
||||||
|
n.Channels[c.name] = cp.Copy()
|
||||||
|
}
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nk *Nick) IsOnStr(c string) (*Channel, bool) {
|
func (nk *nick) isOn(ch *channel) (*ChanPrivs, bool) {
|
||||||
ch, ok := nk.lookup[c]
|
cp, ok := nk.chans[ch]
|
||||||
return ch, ok
|
return cp.Copy(), ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Associates a Channel with a Nick.
|
// Associates a Channel with a Nick.
|
||||||
func (nk *Nick) addChannel(ch *Channel, cp *ChanPrivs) {
|
func (nk *nick) addChannel(ch *channel, cp *ChanPrivs) {
|
||||||
if _, ok := nk.chans[ch]; !ok {
|
if _, ok := nk.chans[ch]; !ok {
|
||||||
nk.chans[ch] = cp
|
nk.chans[ch] = cp
|
||||||
nk.lookup[ch.Name] = ch
|
nk.lookup[ch.name] = ch
|
||||||
} else {
|
} else {
|
||||||
logging.Warn("Nick.addChannel(): %s already on %s.", nk.Nick, ch.Name)
|
logging.Warn("Nick.addChannel(): %s already on %s.", nk.nick, ch.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disassociates a Channel from a Nick.
|
// Disassociates a Channel from a Nick.
|
||||||
func (nk *Nick) delChannel(ch *Channel) {
|
func (nk *nick) delChannel(ch *channel) {
|
||||||
if _, ok := nk.chans[ch]; ok {
|
if _, ok := nk.chans[ch]; ok {
|
||||||
delete(nk.chans, ch)
|
delete(nk.chans, ch)
|
||||||
delete(nk.lookup, ch.Name)
|
delete(nk.lookup, ch.name)
|
||||||
} else {
|
} else {
|
||||||
logging.Warn("Nick.delChannel(): %s not on %s.", nk.Nick, ch.Name)
|
logging.Warn("Nick.delChannel(): %s not on %s.", nk.nick, ch.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse mode strings for a Nick.
|
// Parse mode strings for a Nick.
|
||||||
func (nk *Nick) ParseModes(modes string) {
|
func (nk *nick) parseModes(modes string) {
|
||||||
var modeop bool // true => add mode, false => remove mode
|
var modeop bool // true => add mode, false => remove mode
|
||||||
for i := 0; i < len(modes); i++ {
|
for i := 0; i < len(modes); i++ {
|
||||||
switch m := modes[i]; m {
|
switch m := modes[i]; m {
|
||||||
|
@ -96,46 +114,44 @@ func (nk *Nick) ParseModes(modes string) {
|
||||||
case '-':
|
case '-':
|
||||||
modeop = false
|
modeop = false
|
||||||
case 'B':
|
case 'B':
|
||||||
nk.Modes.Bot = modeop
|
nk.modes.Bot = modeop
|
||||||
case 'i':
|
case 'i':
|
||||||
nk.Modes.Invisible = modeop
|
nk.modes.Invisible = modeop
|
||||||
case 'o':
|
case 'o':
|
||||||
nk.Modes.Oper = modeop
|
nk.modes.Oper = modeop
|
||||||
case 'w':
|
case 'w':
|
||||||
nk.Modes.WallOps = modeop
|
nk.modes.WallOps = modeop
|
||||||
case 'x':
|
case 'x':
|
||||||
nk.Modes.HiddenHost = modeop
|
nk.modes.HiddenHost = modeop
|
||||||
case 'z':
|
case 'z':
|
||||||
nk.Modes.SSL = modeop
|
nk.modes.SSL = modeop
|
||||||
default:
|
default:
|
||||||
logging.Info("Nick.ParseModes(): unknown mode char %c", m)
|
logging.Info("Nick.ParseModes(): unknown mode char %c", m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type byName []*Channel
|
// Returns true if the Nick is associated with the Channel.
|
||||||
|
func (nk *Nick) IsOn(ch string) (*ChanPrivs, bool) {
|
||||||
func (b byName) Len() int { return len(b) }
|
cp, ok := nk.Channels[ch]
|
||||||
func (b byName) Less(i, j int) bool { return b[i].Name < b[j].Name }
|
return cp, ok
|
||||||
func (b byName) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
|
||||||
|
|
||||||
// Channels returns a list of *Channel the nick is on, sorted by name.
|
|
||||||
func (nk *Nick) Channels() []*Channel {
|
|
||||||
channels := make([]*Channel, 0, len(nk.lookup))
|
|
||||||
for _, channel := range nk.lookup {
|
|
||||||
channels = append(channels, channel)
|
|
||||||
}
|
|
||||||
sort.Sort(byName(channels))
|
|
||||||
return channels
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChannelsStr returns a list of channel strings the nick is on, sorted by name.
|
// Tests Nick equality.
|
||||||
func (nk *Nick) ChannelsStr() []string {
|
func (nk *Nick) Equals(other *Nick) bool {
|
||||||
var names []string
|
return reflect.DeepEqual(nk, other)
|
||||||
for _, channel := range nk.Channels() {
|
}
|
||||||
names = append(names, channel.Name)
|
|
||||||
}
|
// Duplicates a NickMode struct.
|
||||||
return names
|
func (nm *NickMode) Copy() *NickMode {
|
||||||
|
if nm == nil { return nil }
|
||||||
|
n := *nm
|
||||||
|
return &n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests NickMode equality.
|
||||||
|
func (nm *NickMode) Equals(other *NickMode) bool {
|
||||||
|
return reflect.DeepEqual(nm, other)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a string representing the nick. Looks like:
|
// Returns a string representing the nick. Looks like:
|
||||||
|
@ -152,12 +168,16 @@ func (nk *Nick) String() string {
|
||||||
str += "Real Name: " + nk.Name + "\n\t"
|
str += "Real Name: " + nk.Name + "\n\t"
|
||||||
str += "Modes: " + nk.Modes.String() + "\n\t"
|
str += "Modes: " + nk.Modes.String() + "\n\t"
|
||||||
str += "Channels: \n"
|
str += "Channels: \n"
|
||||||
for ch, cp := range nk.chans {
|
for ch, cp := range nk.Channels {
|
||||||
str += "\t\t" + ch.Name + ": " + cp.String() + "\n"
|
str += "\t\t" + ch + ": " + cp.String() + "\n"
|
||||||
}
|
}
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (nk *nick) String() string {
|
||||||
|
return nk.Nick().String()
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a string representing the nick modes. Looks like:
|
// Returns a string representing the nick modes. Looks like:
|
||||||
// +iwx
|
// +iwx
|
||||||
func (nm *NickMode) String() string {
|
func (nm *NickMode) String() string {
|
||||||
|
|
|
@ -1,23 +1,35 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import "testing"
|
||||||
"testing"
|
|
||||||
)
|
func compareNick(t *testing.T, nk *nick) {
|
||||||
|
n := nk.Nick()
|
||||||
|
if n.Nick != nk.nick || n.Ident != nk.ident || n.Host != nk.host || n.Name != nk.name ||
|
||||||
|
!n.Modes.Equals(nk.modes) || len(n.Channels) != len(nk.chans) {
|
||||||
|
t.Errorf("Nick not duped correctly from internal state.")
|
||||||
|
}
|
||||||
|
for ch, cp := range nk.chans {
|
||||||
|
if other, ok := n.Channels[ch.name]; !ok || !cp.Equals(other) {
|
||||||
|
t.Errorf("Channel not duped correctly from internal state.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewNick(t *testing.T) {
|
func TestNewNick(t *testing.T) {
|
||||||
nk := NewNick("test1")
|
nk := newNick("test1")
|
||||||
|
|
||||||
if nk.Nick != "test1" {
|
if nk.nick != "test1" {
|
||||||
t.Errorf("Nick not created correctly by NewNick()")
|
t.Errorf("Nick not created correctly by NewNick()")
|
||||||
}
|
}
|
||||||
if len(nk.chans) != 0 || len(nk.lookup) != 0 {
|
if len(nk.chans) != 0 || len(nk.lookup) != 0 {
|
||||||
t.Errorf("Nick maps contain data after NewNick()")
|
t.Errorf("Nick maps contain data after NewNick()")
|
||||||
}
|
}
|
||||||
|
compareNick(t, nk)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddChannel(t *testing.T) {
|
func TestAddChannel(t *testing.T) {
|
||||||
nk := NewNick("test1")
|
nk := newNick("test1")
|
||||||
ch := NewChannel("#test1")
|
ch := newChannel("#test1")
|
||||||
cp := new(ChanPrivs)
|
cp := new(ChanPrivs)
|
||||||
|
|
||||||
nk.addChannel(ch, cp)
|
nk.addChannel(ch, cp)
|
||||||
|
@ -31,11 +43,12 @@ func TestAddChannel(t *testing.T) {
|
||||||
if c, ok := nk.lookup["#test1"]; !ok || c != ch {
|
if c, ok := nk.lookup["#test1"]; !ok || c != ch {
|
||||||
t.Errorf("Channel #test1 not properly stored in lookup map.")
|
t.Errorf("Channel #test1 not properly stored in lookup map.")
|
||||||
}
|
}
|
||||||
|
compareNick(t, nk)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDelChannel(t *testing.T) {
|
func TestDelChannel(t *testing.T) {
|
||||||
nk := NewNick("test1")
|
nk := newNick("test1")
|
||||||
ch := NewChannel("#test1")
|
ch := newChannel("#test1")
|
||||||
cp := new(ChanPrivs)
|
cp := new(ChanPrivs)
|
||||||
|
|
||||||
nk.addChannel(ch, cp)
|
nk.addChannel(ch, cp)
|
||||||
|
@ -49,11 +62,12 @@ func TestDelChannel(t *testing.T) {
|
||||||
if c, ok := nk.lookup["#test1"]; ok || c != nil {
|
if c, ok := nk.lookup["#test1"]; ok || c != nil {
|
||||||
t.Errorf("Channel #test1 not properly removed from lookup map.")
|
t.Errorf("Channel #test1 not properly removed from lookup map.")
|
||||||
}
|
}
|
||||||
|
compareNick(t, nk)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNickParseModes(t *testing.T) {
|
func TestNickParseModes(t *testing.T) {
|
||||||
nk := NewNick("test1")
|
nk := newNick("test1")
|
||||||
md := nk.Modes
|
md := nk.modes
|
||||||
|
|
||||||
// Modes should all be false for a new nick
|
// Modes should all be false for a new nick
|
||||||
if md.Invisible || md.Oper || md.WallOps || md.HiddenHost || md.SSL {
|
if md.Invisible || md.Oper || md.WallOps || md.HiddenHost || md.SSL {
|
||||||
|
@ -65,8 +79,9 @@ func TestNickParseModes(t *testing.T) {
|
||||||
md.HiddenHost = true
|
md.HiddenHost = true
|
||||||
|
|
||||||
// Parse a mode line that flips one true to false and two false to true
|
// Parse a mode line that flips one true to false and two false to true
|
||||||
nk.ParseModes("+z-x+w")
|
nk.parseModes("+z-x+w")
|
||||||
|
|
||||||
|
compareNick(t, nk)
|
||||||
if !md.Invisible || md.Oper || !md.WallOps || md.HiddenHost || !md.SSL {
|
if !md.Invisible || md.Oper || !md.WallOps || md.HiddenHost || !md.SSL {
|
||||||
t.Errorf("Modes not flipped correctly by ParseModes.")
|
t.Errorf("Modes not flipped correctly by ParseModes.")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue