Skip to content

Commit 6485757

Browse files
authored
Add solution for challenge 8 by Cpoing (#288)
1 parent 103f266 commit 6485757

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Package challenge8 contains the solution for Challenge 8: Chat Server with Channels.
2+
package challenge8
3+
4+
import (
5+
"errors"
6+
"sync"
7+
// Add any other necessary imports
8+
)
9+
10+
// Client represents a connected chat client
11+
type Client struct {
12+
// TODO: Implement this struct
13+
// Hint: username, message channel, mutex, disconnected flag
14+
username string
15+
messages chan string
16+
connected bool
17+
mutex sync.RWMutex
18+
}
19+
20+
// Send sends a message to the client
21+
func (c *Client) Send(message string) {
22+
// TODO: Implement this method
23+
// Hint: thread-safe, non-blocking send
24+
c.mutex.Lock()
25+
defer c.mutex.Unlock()
26+
if !c.connected {
27+
return
28+
}
29+
30+
c.messages <- message
31+
}
32+
33+
// Receive returns the next message for the client (blocking)
34+
func (c *Client) Receive() string {
35+
// TODO: Implement this method
36+
// Hint: read from channel, handle closed channel
37+
if message, ok := <-c.messages; ok {
38+
return message
39+
}
40+
return ""
41+
}
42+
43+
// ChatServer manages client connections and message routing
44+
type ChatServer struct {
45+
// TODO: Implement this struct
46+
// Hint: clients map, mutex
47+
clients map[string]*Client
48+
mutex sync.RWMutex
49+
}
50+
51+
// NewChatServer creates a new chat server instance
52+
func NewChatServer() *ChatServer {
53+
// TODO: Implement this function
54+
return &ChatServer{
55+
clients: make(map[string]*Client),
56+
}
57+
}
58+
59+
// Connect adds a new client to the chat server
60+
func (s *ChatServer) Connect(username string) (*Client, error) {
61+
// TODO: Implement this method
62+
// Hint: check username, create client, add to map
63+
s.mutex.Lock()
64+
defer s.mutex.Unlock()
65+
66+
if _, exists := s.clients[username]; exists {
67+
return nil, ErrUsernameAlreadyTaken
68+
}
69+
client := &Client{
70+
username: username,
71+
messages: make(chan string),
72+
connected: true,
73+
}
74+
s.clients[username] = client
75+
return client, nil
76+
77+
}
78+
79+
// Disconnect removes a client from the chat server
80+
func (s *ChatServer) Disconnect(client *Client) {
81+
// TODO: Implement this method
82+
// Hint: remove from map, close channels
83+
s.mutex.Lock()
84+
defer s.mutex.Unlock()
85+
86+
delete(s.clients, client.username)
87+
client.connected = false
88+
close(client.messages)
89+
}
90+
91+
// Broadcast sends a message to all connected clients
92+
func (s *ChatServer) Broadcast(sender *Client, message string) {
93+
// TODO: Implement this method
94+
// Hint: format message, send to all clients
95+
s.mutex.Lock()
96+
defer s.mutex.Unlock()
97+
for _, client := range s.clients {
98+
client.Send(message)
99+
}
100+
}
101+
102+
// PrivateMessage sends a message to a specific client
103+
func (s *ChatServer) PrivateMessage(sender *Client, recipient string, message string) error {
104+
// TODO: Implement this method
105+
// Hint: find recipient, check errors, send message
106+
s.mutex.Lock()
107+
defer s.mutex.Unlock()
108+
if !sender.connected {
109+
return ErrClientDisconnected
110+
}
111+
if client, exists := s.clients[recipient]; !exists {
112+
return ErrRecipientNotFound
113+
} else if !client.connected {
114+
return ErrClientDisconnected
115+
} else {
116+
client.Send(message)
117+
return nil
118+
}
119+
120+
}
121+
122+
// Common errors that can be returned by the Chat Server
123+
var (
124+
ErrUsernameAlreadyTaken = errors.New("username already taken")
125+
ErrRecipientNotFound = errors.New("recipient not found")
126+
ErrClientDisconnected = errors.New("client disconnected")
127+
// Add more error types as needed
128+
)

0 commit comments

Comments
 (0)