mirror of
https://github.com/shazow/ssh-chat.git
synced 2025-07-21 13:04:30 +02:00
Messing with the API more, tests pass.
This commit is contained in:
parent
bf3cc264e6
commit
137e84db79
@ -16,7 +16,7 @@ func TestChannel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
u := NewUser("foo", s)
|
u := NewUserScreen("foo", s)
|
||||||
ch := NewChannel("", out)
|
ch := NewChannel("", out)
|
||||||
err := ch.Join(u)
|
err := ch.Join(u)
|
||||||
|
|
||||||
@ -26,6 +26,6 @@ func TestChannel(t *testing.T) {
|
|||||||
|
|
||||||
expected := []byte(" * foo joined. (Connected: 1)")
|
expected := []byte(" * foo joined. (Connected: 1)")
|
||||||
if !reflect.DeepEqual(s.received, expected) {
|
if !reflect.DeepEqual(s.received, expected) {
|
||||||
t.Errorf("Got: `%s`, Expected: `%s`", s.received, expected)
|
t.Errorf("Got: `%s`; Expected: `%s`", s.received, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package chat
|
package chat
|
||||||
|
|
||||||
const messageBuffer = 20
|
|
||||||
|
|
||||||
// Host knows about all the commands and channels.
|
// Host knows about all the commands and channels.
|
||||||
type Host struct {
|
type Host struct {
|
||||||
defaultChannel *Channel
|
defaultChannel *Channel
|
||||||
|
@ -37,6 +37,7 @@ func (m *Message) From(u *User) *Message {
|
|||||||
|
|
||||||
// Render message based on the given theme
|
// Render message based on the given theme
|
||||||
func (m *Message) Render(*Theme) string {
|
func (m *Message) Render(*Theme) string {
|
||||||
|
// TODO: Return []byte?
|
||||||
// TODO: Render based on theme
|
// TODO: Render based on theme
|
||||||
// TODO: Cache based on theme
|
// TODO: Cache based on theme
|
||||||
var msg string
|
var msg string
|
||||||
|
@ -5,7 +5,7 @@ import "testing"
|
|||||||
func TestSet(t *testing.T) {
|
func TestSet(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
s := NewSet()
|
s := NewSet()
|
||||||
u := NewUser("foo", nil)
|
u := NewUser("foo")
|
||||||
|
|
||||||
if s.In(u) {
|
if s.In(u) {
|
||||||
t.Errorf("Set should be empty.")
|
t.Errorf("Set should be empty.")
|
||||||
@ -20,7 +20,7 @@ func TestSet(t *testing.T) {
|
|||||||
t.Errorf("Set should contain user.")
|
t.Errorf("Set should contain user.")
|
||||||
}
|
}
|
||||||
|
|
||||||
u2 := NewUser("bar", nil)
|
u2 := NewUser("bar")
|
||||||
err = s.Add(u2)
|
err = s.Add(u2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
69
chat/user.go
69
chat/user.go
@ -1,31 +1,46 @@
|
|||||||
package chat
|
package chat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const messageBuffer = 20
|
||||||
|
|
||||||
|
var ErrUserClosed = errors.New("user closed")
|
||||||
|
|
||||||
// User definition, implemented set Item interface and io.Writer
|
// User definition, implemented set Item interface and io.Writer
|
||||||
type User struct {
|
type User struct {
|
||||||
name string
|
name string
|
||||||
op bool
|
op bool
|
||||||
colorIdx int
|
colorIdx int
|
||||||
joined time.Time
|
joined time.Time
|
||||||
screen io.Writer
|
msg chan Message
|
||||||
|
done chan struct{}
|
||||||
Config UserConfig
|
Config UserConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUser(name string, screen io.Writer) *User {
|
func NewUser(name string) *User {
|
||||||
u := User{
|
u := User{
|
||||||
screen: screen,
|
|
||||||
joined: time.Now(),
|
|
||||||
Config: *DefaultUserConfig,
|
Config: *DefaultUserConfig,
|
||||||
|
joined: time.Now(),
|
||||||
|
msg: make(chan Message, messageBuffer),
|
||||||
|
done: make(chan struct{}, 1),
|
||||||
}
|
}
|
||||||
u.SetName(name)
|
u.SetName(name)
|
||||||
|
|
||||||
return &u
|
return &u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewUserScreen(name string, screen io.Writer) *User {
|
||||||
|
u := NewUser(name)
|
||||||
|
go u.Consume(screen)
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
// Return unique identifier for user
|
// Return unique identifier for user
|
||||||
func (u *User) Id() Id {
|
func (u *User) Id() Id {
|
||||||
return Id(u.name)
|
return Id(u.name)
|
||||||
@ -52,9 +67,49 @@ func (u *User) SetOp(op bool) {
|
|||||||
u.op = op
|
u.op = op
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to user's screen
|
// Block until user is closed
|
||||||
func (u *User) Write(p []byte) (n int, err error) {
|
func (u *User) Wait() {
|
||||||
return u.screen.Write(p)
|
<-u.done
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect user, stop accepting messages
|
||||||
|
func (u *User) Close() {
|
||||||
|
close(u.done)
|
||||||
|
close(u.msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume message buffer into an io.Writer. Will block, should be called in a
|
||||||
|
// goroutine.
|
||||||
|
func (u *User) Consume(out io.Writer) {
|
||||||
|
for m := range u.msg {
|
||||||
|
u.consumeMsg(m, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume one message and stop, mostly for testing
|
||||||
|
func (u *User) ConsumeOne(out io.Writer) {
|
||||||
|
u.consumeMsg(<-u.msg, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) consumeMsg(m Message, out io.Writer) {
|
||||||
|
s := m.Render(u.Config.Theme)
|
||||||
|
_, err := out.Write([]byte(s))
|
||||||
|
if err != nil {
|
||||||
|
logger.Printf("Write failed to %s, closing: %s", u.Name(), err)
|
||||||
|
u.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add message to consume by user
|
||||||
|
func (u *User) Send(m Message) error {
|
||||||
|
select {
|
||||||
|
case u.msg <- m:
|
||||||
|
default:
|
||||||
|
logger.Printf("Msg buffer full, closing: %s", u.Name())
|
||||||
|
u.Close()
|
||||||
|
return ErrUserClosed
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container for per-user configurations.
|
// Container for per-user configurations.
|
||||||
|
@ -16,11 +16,14 @@ func (s *MockScreen) Write(data []byte) (n int, err error) {
|
|||||||
|
|
||||||
func TestMakeUser(t *testing.T) {
|
func TestMakeUser(t *testing.T) {
|
||||||
s := &MockScreen{}
|
s := &MockScreen{}
|
||||||
u := NewUser("foo", s)
|
u := NewUser("foo")
|
||||||
|
m := NewMessage("hello")
|
||||||
|
|
||||||
line := []byte("hello")
|
defer u.Close()
|
||||||
u.Write(line)
|
u.Send(*m)
|
||||||
if !reflect.DeepEqual(s.received, line) {
|
u.ConsumeOne(s)
|
||||||
t.Errorf("Expected hello but got: %s", s.received)
|
|
||||||
|
if !reflect.DeepEqual(string(s.received), m.String()) {
|
||||||
|
t.Errorf("Got: `%s`; Expected: `%s`", s.received, m.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user