goirc/state/tracker.go

267 lines
7.5 KiB
Go
Raw Permalink Normal View History

package state
import (
2011-11-13 14:02:12 +00:00
"github.com/fluffle/golog/logging"
)
2011-09-28 19:48:58 +00:00
// The state manager interface
type StateTracker interface {
2011-10-19 23:10:33 +00:00
// Nick methods
2011-09-28 19:48:58 +00:00
NewNick(nick string) *Nick
GetNick(nick string) *Nick
2011-10-06 22:54:34 +00:00
ReNick(old, neu string)
DelNick(nick string)
2011-10-19 23:10:33 +00:00
// Channel methods
2011-09-28 19:48:58 +00:00
NewChannel(channel string) *Channel
GetChannel(channel string) *Channel
2011-10-06 22:54:34 +00:00
DelChannel(channel string)
2011-10-19 23:10:33 +00:00
// Information about ME!
Me() *Nick
// And the tracking operations
IsOn(channel, nick string) (*ChanPrivs, bool)
Associate(channel *Channel, nick *Nick) *ChanPrivs
2011-10-19 23:10:33 +00:00
Dissociate(channel *Channel, nick *Nick)
Wipe()
2011-11-05 09:30:32 +00:00
// The state tracker can output a debugging string
String() string
2011-09-28 19:48:58 +00:00
}
2011-10-19 23:10:33 +00:00
// ... and a struct to implement it ...
2011-09-28 19:48:58 +00:00
type stateTracker struct {
// Map of channels we're on
chans map[string]*Channel
// Map of nicks we know about
nicks map[string]*Nick
2011-10-19 23:10:33 +00:00
// We need to keep state on who we are :-)
me *Nick
// For great logging justice.
l logging.Logger
2011-10-19 23:10:33 +00:00
}
// ... and a constructor to make it ...
func NewTracker(mynick string, l logging.Logger) *stateTracker {
2011-10-19 23:10:33 +00:00
st := &stateTracker{
chans: make(map[string]*Channel),
nicks: make(map[string]*Nick),
2011-11-13 13:32:53 +00:00
l: l,
}
2011-10-19 23:10:33 +00:00
st.me = st.NewNick(mynick)
return st
2011-10-08 09:40:58 +00:00
}
// ... and a method to wipe the state clean.
func (st *stateTracker) Wipe() {
// Deleting all the channels implicitly deletes every nick but me.
for _, ch := range st.chans {
st.delChannel(ch)
}
}
2011-10-19 23:10:33 +00:00
/******************************************************************************\
* tracker methods to create/look up nicks/channels
\******************************************************************************/
// Creates a new Nick, initialises it, and stores it so it
// can be properly tracked for state management purposes.
2011-10-19 23:10:33 +00:00
func (st *stateTracker) NewNick(n string) *Nick {
if _, ok := st.nicks[n]; ok {
st.l.Warn("StateTracker.NewNick(): %s already tracked.", n)
return nil
}
st.nicks[n] = NewNick(n, st.l)
2011-10-19 23:10:33 +00:00
return st.nicks[n]
}
// Returns a Nick for the nick n, if we're tracking it.
2011-09-28 19:48:58 +00:00
func (st *stateTracker) GetNick(n string) *Nick {
2011-10-19 23:10:33 +00:00
if nk, ok := st.nicks[n]; ok {
return nk
}
return nil
}
// Signals to the tracker that a Nick should be tracked
2011-10-06 22:54:34 +00:00
// under a "neu" nick rather than the old one.
func (st *stateTracker) ReNick(old, neu string) {
2011-10-19 23:10:33 +00:00
if nk, ok := st.nicks[old]; ok {
if _, ok := st.nicks[neu]; !ok {
2011-10-19 23:10:33 +00:00
nk.Nick = neu
2011-11-13 13:32:53 +00:00
delete(st.nicks, old)
2011-10-19 23:10:33 +00:00
st.nicks[neu] = nk
2011-10-19 23:23:54 +00:00
for ch, _ := range nk.chans {
// We also need to update the lookup maps of all the channels
// the nick is on, to keep things in sync.
2011-11-13 13:32:53 +00:00
delete(ch.lookup, old)
2011-10-19 23:23:54 +00:00
ch.lookup[neu] = nk
}
} else {
st.l.Warn("StateTracker.ReNick(): %s already exists.", neu)
}
} else {
st.l.Warn("StateTracker.ReNick(): %s not tracked.", old)
2011-10-06 22:54:34 +00:00
}
}
// Removes a Nick from being tracked.
2011-10-06 22:54:34 +00:00
func (st *stateTracker) DelNick(n string) {
2011-10-19 23:10:33 +00:00
if nk, ok := st.nicks[n]; ok {
if nk != st.me {
st.delNick(nk)
} else {
st.l.Warn("StateTracker.DelNick(): won't delete myself.")
2011-10-19 23:10:33 +00:00
}
} else {
st.l.Warn("StateTracker.DelNick(): %s not tracked.", n)
2011-10-06 22:54:34 +00:00
}
}
2011-10-19 23:10:33 +00:00
func (st *stateTracker) delNick(nk *Nick) {
if nk == st.me {
// Shouldn't get here => internal state tracking code is fubar.
st.l.Error("StateTracker.DelNick(): TRYING TO DELETE ME :-(")
2011-10-19 23:10:33 +00:00
return
}
2011-11-13 13:32:53 +00:00
delete(st.nicks, nk.Nick)
2011-10-19 23:10:33 +00:00
for ch, _ := range nk.chans {
nk.delChannel(ch)
ch.delNick(nk)
if len(ch.nicks) == 0 {
// Deleting a nick from tracking shouldn't empty any channels as
// *we* should be on the channel with them to be tracking them.
st.l.Error("StateTracker.delNick(): deleting nick %s emptied "+
"channel %s, this shouldn't happen!", nk.Nick, ch.Name)
2011-10-19 23:10:33 +00:00
}
}
}
// Creates a new Channel, initialises it, and stores it so it
// can be properly tracked for state management purposes.
2011-09-28 19:48:58 +00:00
func (st *stateTracker) NewChannel(c string) *Channel {
if _, ok := st.chans[c]; ok {
st.l.Warn("StateTracker.NewChannel(): %s already tracked.", c)
return nil
}
st.chans[c] = NewChannel(c, st.l)
return st.chans[c]
}
// Returns a Channel for the channel c, if we're tracking it.
2011-09-28 19:48:58 +00:00
func (st *stateTracker) GetChannel(c string) *Channel {
if ch, ok := st.chans[c]; ok {
return ch
}
return nil
}
// Removes a Channel from being tracked.
2011-10-06 22:54:34 +00:00
func (st *stateTracker) DelChannel(c string) {
2011-10-19 23:10:33 +00:00
if ch, ok := st.chans[c]; ok {
st.delChannel(ch)
} else {
st.l.Warn("StateTracker.DelChannel(): %s not tracked.", c)
2011-10-06 22:54:34 +00:00
}
}
2011-10-19 23:10:33 +00:00
func (st *stateTracker) delChannel(ch *Channel) {
2011-11-13 13:32:53 +00:00
delete(st.chans, ch.Name)
2011-10-19 23:10:33 +00:00
for nk, _ := range ch.nicks {
ch.delNick(nk)
nk.delChannel(ch)
if len(nk.chans) == 0 && nk != st.me {
// We're no longer in any channels with this nick.
st.delNick(nk)
}
}
}
// Returns the Nick the state tracker thinks is Me.
func (st *stateTracker) Me() *Nick {
return st.me
}
// Returns true if both the channel c and the nick n are tracked
// and the nick is associated with the channel.
func (st *stateTracker) IsOn(c, n string) (*ChanPrivs, bool) {
2011-10-06 22:54:34 +00:00
nk := st.GetNick(n)
ch := st.GetChannel(c)
if nk != nil && ch != nil {
return nk.IsOn(ch)
}
return nil, false
2011-10-06 22:54:34 +00:00
}
2011-10-19 23:10:33 +00:00
// Associates an already known nick with an already known channel.
func (st *stateTracker) Associate(ch *Channel, nk *Nick) *ChanPrivs {
2011-10-19 23:10:33 +00:00
if ch == nil || nk == nil {
st.l.Error("StateTracker.Associate(): passed nil values :-(")
return nil
} else if _ch, ok := st.chans[ch.Name]; !ok || ch != _ch {
// As we can implicitly delete both nicks and channels from being
// tracked by dissociating one from the other, we should verify that
// we're not being passed an old Nick or Channel.
st.l.Error("StateTracker.Associate(): channel %s not found in "+
"(or differs from) internal state.", ch.Name)
} else if _nk, ok := st.nicks[nk.Nick]; !ok || nk != _nk {
st.l.Error("StateTracker.Associate(): nick %s not found in "+
"(or differs from) internal state.", nk.Nick)
} else if _, ok := nk.IsOn(ch); ok {
st.l.Warn("StateTracker.Associate(): %s already on %s.",
2011-10-19 23:10:33 +00:00
nk.Nick, ch.Name)
return nil
2011-10-19 23:10:33 +00:00
}
cp := new(ChanPrivs)
ch.addNick(nk, cp)
nk.addChannel(ch, cp)
return cp
2011-10-19 23:10:33 +00:00
}
// Dissociates an already known nick from an already known channel.
// Does some tidying up to stop tracking nicks we're no longer on
// any common channels with, and channels we're no longer on.
func (st *stateTracker) Dissociate(ch *Channel, nk *Nick) {
if ch == nil || nk == nil {
st.l.Error("StateTracker.Dissociate(): passed nil values :-(")
} else if _ch, ok := st.chans[ch.Name]; !ok || ch != _ch {
// As we can implicitly delete both nicks and channels from being
// tracked by dissociating one from the other, we should verify that
// we're not being passed an old Nick or Channel.
st.l.Error("StateTracker.Dissociate(): channel %s not found in "+
"(or differs from) internal state.", ch.Name)
} else if _nk, ok := st.nicks[nk.Nick]; !ok || nk != _nk {
st.l.Error("StateTracker.Dissociate(): nick %s not found in "+
"(or differs from) internal state.", nk.Nick)
} else if _, ok := nk.IsOn(ch); !ok {
st.l.Warn("StateTracker.Dissociate(): %s not on %s.",
2011-10-19 23:10:33 +00:00
nk.Nick, ch.Name)
} else if nk == st.me {
2011-10-19 23:10:33 +00:00
// I'm leaving the channel for some reason, so it won't be tracked.
st.delChannel(ch)
} else {
2011-10-19 23:10:33 +00:00
// Remove the nick from the channel and the channel from the nick.
ch.delNick(nk)
nk.delChannel(ch)
if len(nk.chans) == 0 {
// We're no longer in any channels with this nick.
st.delNick(nk)
}
}
}
2011-11-05 06:06:59 +00:00
func (st *stateTracker) String() string {
str := "GoIRC Channels\n"
str += "--------------\n\n"
for _, ch := range st.chans {
str += ch.String() + "\n"
}
str += "GoIRC NickNames\n"
str += "---------------\n\n"
for _, n := range st.nicks {
if n != st.me {
str += n.String() + "\n"
}
}
return str
}