use std::collections::{HashMap, HashSet};
use actix::{Actor, Addr, Context, Handler, ResponseFuture};
use futures::TryFutureExt;
use irc_proto::{Command, Message, Prefix, Response};
use tracing::{instrument, Span};
use crate::{
channel::Channel,
client::Client,
messages::{Broadcast, ChannelJoin, ServerDisconnect, UserConnected, UserNickChange},
SERVER_NAME,
};
#[derive(Default)]
pub struct Server {
channels: HashMap<String, Addr<Channel>>,
clients: HashSet<Addr<Client>>,
}
impl Handler<UserConnected> for Server {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: UserConnected, _ctx: &mut Self::Context) -> Self::Result {
let responses = [
(
Response::RPL_WELCOME,
vec!["Welcome to the network jordan!jordan@proper.sick.kid"],
),
(Response::RPL_YOURHOST, vec!["Your host is a sick kid"]),
(
Response::RPL_CREATED,
vec!["This server was created at some point"],
),
(
Response::RPL_MYINFO,
vec![
SERVER_NAME,
"0.0.1",
"DOQRSZaghilopsuwz",
"CFILMPQSbcefgijklmnopqrstuvz",
"bkloveqjfI",
],
),
(
Response::RPL_ISUPPORT,
vec!["D", "are supported by this server"],
),
];
for (response, arguments) in responses {
let arguments = std::iter::once(msg.connection.nick.clone())
.chain(arguments.into_iter().map(ToString::to_string))
.collect();
msg.handle.do_send(Broadcast {
span: Span::current(),
message: Message {
tags: None,
prefix: Some(Prefix::ServerName(SERVER_NAME.to_string())),
command: Command::Response(response, arguments),
},
});
}
self.clients.insert(msg.handle);
}
}
impl Handler<ServerDisconnect> for Server {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: ServerDisconnect, _ctx: &mut Self::Context) -> Self::Result {
self.clients.remove(&msg.client);
}
}
impl Handler<ChannelJoin> for Server {
type Result = ResponseFuture<<ChannelJoin as actix::Message>::Result>;
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: ChannelJoin, _ctx: &mut Self::Context) -> Self::Result {
let channel = self
.channels
.entry(msg.channel_name.clone())
.or_insert_with(|| {
Channel {
name: msg.channel_name.clone(),
clients: HashMap::new(),
topic: None,
}
.start()
})
.clone();
Box::pin(
channel
.send(msg)
.map_err(anyhow::Error::new)
.and_then(futures::future::ready),
)
}
}
impl Handler<UserNickChange> for Server {
type Result = ();
#[instrument(parent = &msg.span, skip_all)]
fn handle(&mut self, msg: UserNickChange, _ctx: &mut Self::Context) -> Self::Result {
for client in &self.clients {
client.do_send(msg.clone());
}
}
}
impl Actor for Server {
type Context = Context<Self>;
}