forked from lavina/lavina
- added trait to handle incoming commands from client
- implemented handle trait for whois command
This commit is contained in:
parent
5b30312238
commit
07f2730c8f
|
@ -0,0 +1,17 @@
|
|||
use lavina_core::prelude::Str;
|
||||
use lavina_core::repo::Storage;
|
||||
use std::future::Future;
|
||||
use tokio::io::AsyncWrite;
|
||||
use proto_irc::response::SendResponseBody;
|
||||
|
||||
pub mod whois;
|
||||
|
||||
pub trait Handler<Command> {
|
||||
fn handle(
|
||||
&self,
|
||||
server_name: &Str,
|
||||
client: &Str,
|
||||
writer: &mut (impl AsyncWrite + Unpin),
|
||||
storage: &mut Storage,
|
||||
) -> impl Future<Output = anyhow::Result<()>>;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||
|
||||
use lavina_core::prelude::Str;
|
||||
use proto_irc::response::SendResponseBody;
|
||||
|
||||
pub struct ERR_NOSUCHNICK_401 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
}
|
||||
|
||||
impl ERR_NOSUCHNICK_401 {
|
||||
pub fn new(client: Str, nick: Str) -> Self {
|
||||
ERR_NOSUCHNICK_401 { client, nick }
|
||||
}
|
||||
}
|
||||
|
||||
struct ERR_NOSUCHSERVER_402 {
|
||||
client: Str,
|
||||
/// target parameter in WHOIS
|
||||
/// example: `/whois <target> <nick>`
|
||||
server_name: Str,
|
||||
}
|
||||
pub struct ERR_NONICKNAMEGIVEN_431 {
|
||||
client: Str,
|
||||
}
|
||||
impl ERR_NONICKNAMEGIVEN_431 {
|
||||
pub fn new(client: Str) -> Self {
|
||||
ERR_NONICKNAMEGIVEN_431 { client }
|
||||
}
|
||||
}
|
||||
|
||||
impl SendResponseBody for ERR_NOSUCHNICK_401 {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
writer.write_all(b"401 ").await?;
|
||||
writer.write_all(self.client.as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(self.nick.as_bytes()).await?;
|
||||
writer.write_all(b" :").await?;
|
||||
writer.write_all("No such nick/channel".as_bytes()).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SendResponseBody for ERR_NONICKNAMEGIVEN_431 {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
writer.write_all(b"431").await?;
|
||||
writer.write_all(self.client.as_bytes()).await?;
|
||||
writer.write_all(b" :").await?;
|
||||
writer.write_all("No nickname given".as_bytes()).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SendResponseBody for ERR_NOSUCHSERVER_402 {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
writer.write_all(b"402 ").await?;
|
||||
writer.write_all(self.client.as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(self.server_name.as_bytes()).await?;
|
||||
writer.write_all(b" :").await?;
|
||||
writer.write_all("No such server".as_bytes()).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||
use tracing::instrument::WithSubscriber;
|
||||
|
||||
use lavina_core::prelude::Str;
|
||||
use lavina_core::repo::Storage;
|
||||
use proto_irc::client::command_args::Whois;
|
||||
use proto_irc::response::{IrcResponseMessage, SendResponseBody, SendResponseMessage};
|
||||
|
||||
use crate::commands::whois::error::{ERR_NONICKNAMEGIVEN_431, ERR_NOSUCHNICK_401};
|
||||
use crate::commands::whois::response::{RplWhoIsUser311, RPL_ENDOFWHOIS_318};
|
||||
use crate::commands::Handler;
|
||||
|
||||
pub mod error;
|
||||
pub mod response;
|
||||
|
||||
impl Handler<Whois> for Whois {
|
||||
async fn handle(
|
||||
&self,
|
||||
server_name: &Str,
|
||||
client: &Str,
|
||||
writer: &mut (impl AsyncWrite + Unpin),
|
||||
storage: &mut Storage,
|
||||
) -> anyhow::Result<()> {
|
||||
match self {
|
||||
Whois::Nick(nick) => handle_nick_target(nick, None, server_name, client, writer, storage).await?,
|
||||
Whois::TargetNick(target, nick) => {
|
||||
handle_nick_target(nick, Some(target), server_name, client, writer, storage).await?
|
||||
}
|
||||
Whois::EmptyArgs => {
|
||||
IrcResponseMessage::empty_tags(
|
||||
Some(server_name.clone()),
|
||||
ERR_NONICKNAMEGIVEN_431::new(server_name.clone()),
|
||||
)
|
||||
.write_response(writer)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_nick_target(
|
||||
nick: &Str,
|
||||
// todo: implement logic with target
|
||||
_target: Option<&Str>,
|
||||
server_name: &Str,
|
||||
client: &Str,
|
||||
writer: &mut (impl AsyncWrite + Unpin),
|
||||
storage: &mut Storage,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(user) = storage.retrieve_user_by_name(nick).await? {
|
||||
IrcResponseMessage::empty_tags(
|
||||
Some(server_name.clone()),
|
||||
RplWhoIsUser311::new(
|
||||
client.clone(),
|
||||
nick.clone(),
|
||||
Some(Str::from(user.name.clone())),
|
||||
server_name.clone(),
|
||||
),
|
||||
)
|
||||
.write_response(writer)
|
||||
.await?;
|
||||
|
||||
IrcResponseMessage::empty_tags(
|
||||
Some(server_name.clone()),
|
||||
RPL_ENDOFWHOIS_318::new(client.clone(), user.name.clone().into()),
|
||||
)
|
||||
.write_response(writer)
|
||||
.await?
|
||||
} else {
|
||||
ERR_NOSUCHNICK_401::new(client.clone(), nick.clone()).write_response(writer).await?
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||
|
||||
use lavina_core::prelude::Str;
|
||||
use proto_irc::response::SendResponseBody;
|
||||
|
||||
struct RplWhoiscertfp276;
|
||||
struct RplWhoIsRegNick307;
|
||||
pub struct RplWhoIsUser311 {
|
||||
client: Str,
|
||||
/// unique name
|
||||
nick: Str,
|
||||
/// username not unique
|
||||
username: Option<Str>,
|
||||
/// server name
|
||||
host: Str,
|
||||
}
|
||||
|
||||
impl RplWhoIsUser311 {
|
||||
pub fn new(client: Str, nick: Str, username: Option<Str>, host: Str) -> Self {
|
||||
RplWhoIsUser311 {
|
||||
client,
|
||||
nick,
|
||||
username,
|
||||
host,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct _RplWhoisserver312;
|
||||
struct _RplWhoisoperator313;
|
||||
struct _RplWhoisidle317;
|
||||
struct _RplWhoischannels319;
|
||||
struct _RplWhoisspecial320;
|
||||
struct _RplWhoisaccount330;
|
||||
struct _RplWhoisactually338;
|
||||
struct _RplWhoishost378;
|
||||
struct _RplWhoismodes379;
|
||||
struct _RplWhoissecure671;
|
||||
struct _RplAway301;
|
||||
pub struct RPL_ENDOFWHOIS_318 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
}
|
||||
impl RPL_ENDOFWHOIS_318 {
|
||||
pub fn new(client: Str, nick: Str) -> Self {
|
||||
RPL_ENDOFWHOIS_318 { client, nick }
|
||||
}
|
||||
}
|
||||
|
||||
impl SendResponseBody for RplWhoIsUser311 {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
writer.write_all(b"311 ").await?;
|
||||
writer.write_all(self.client.as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(self.nick.as_bytes()).await?;
|
||||
if let Some(username) = self.username {
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(username.as_bytes()).await?;
|
||||
}
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(self.host.as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all("*".as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(b" :").await?;
|
||||
//todo no entity in db that represents whole irc user entity
|
||||
writer.write_all("<realname>".as_bytes()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SendResponseBody for RPL_ENDOFWHOIS_318 {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
writer.write_all(b"318 ").await?;
|
||||
writer.write_all(self.client.as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(self.nick.as_bytes()).await?;
|
||||
writer.write_all(b" :").await?;
|
||||
writer.write_all("End of /WHOIS list".as_bytes()).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -25,10 +25,12 @@ use proto_irc::server::{AwayStatus, ServerMessage, ServerMessageBody};
|
|||
use proto_irc::user::PrefixedNick;
|
||||
use proto_irc::{Chan, Recipient};
|
||||
use sasl::AuthBody;
|
||||
|
||||
mod cap;
|
||||
mod commands;
|
||||
|
||||
use crate::cap::Capabilities;
|
||||
use commands::Handler;
|
||||
use proto_irc::response::IrcResponseMessage;
|
||||
|
||||
pub const APP_VERSION: &str = concat!("lavina", "_", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
|
@ -75,7 +77,7 @@ async fn handle_socket(
|
|||
match registered_user {
|
||||
Ok(user) => {
|
||||
log::debug!("User registered");
|
||||
handle_registered_socket(config, players, rooms, &mut reader, &mut writer, user).await?;
|
||||
handle_registered_socket(config, players, rooms, &mut reader, &mut writer, user, &mut storage).await?;
|
||||
}
|
||||
Err(err) => {
|
||||
log::debug!("Registration failed: {err}");
|
||||
|
@ -387,6 +389,7 @@ async fn handle_registered_socket<'a>(
|
|||
reader: &mut BufReader<ReadHalf<'a>>,
|
||||
writer: &mut BufWriter<WriteHalf<'a>>,
|
||||
user: RegisteredUser,
|
||||
storage: &mut Storage,
|
||||
) -> Result<()> {
|
||||
let mut buffer = vec![];
|
||||
log::info!("Handling registered user: {user:?}");
|
||||
|
@ -466,7 +469,7 @@ async fn handle_registered_socket<'a>(
|
|||
len
|
||||
};
|
||||
let incoming = std::str::from_utf8(&buffer[0..len-2])?;
|
||||
if let HandleResult::Leave = handle_incoming_message(incoming, &config, &user, &rooms, &mut connection, writer).await? {
|
||||
if let HandleResult::Leave = handle_incoming_message(incoming, &config, &user, &rooms, &mut connection, writer, storage).await? {
|
||||
break;
|
||||
}
|
||||
buffer.clear();
|
||||
|
@ -615,6 +618,7 @@ async fn handle_incoming_message(
|
|||
rooms: &RoomRegistry,
|
||||
user_handle: &mut PlayerConnection,
|
||||
writer: &mut (impl AsyncWrite + Unpin),
|
||||
storage: &mut Storage,
|
||||
) -> Result<HandleResult> {
|
||||
log::debug!("Incoming raw IRC message: '{buffer}'");
|
||||
let parsed = client_message(buffer);
|
||||
|
@ -724,55 +728,16 @@ async fn handle_incoming_message(
|
|||
log::warn!("Local chans not supported");
|
||||
}
|
||||
},
|
||||
ClientMessage::Whois { target, nick } => {
|
||||
// todo: finish replpies from the server to the command
|
||||
match (target, nick) {
|
||||
(Some(target), Some(nick)) => {
|
||||
ServerMessage {
|
||||
tags: vec![],
|
||||
sender: Some(config.server_name.clone()),
|
||||
body: ServerMessageBody::N318EndOfWhois {
|
||||
client: user.nickname.clone(),
|
||||
nick: nick,
|
||||
msg: "End of /WHOIS list".into(),
|
||||
},
|
||||
}
|
||||
.write_async(writer)
|
||||
.await?;
|
||||
writer.flush().await?
|
||||
}
|
||||
(Some(target), None) => {
|
||||
todo!()
|
||||
}
|
||||
(None, Some(nick)) => {
|
||||
ServerMessage {
|
||||
tags: vec![],
|
||||
sender: Some(config.server_name.clone()),
|
||||
body: ServerMessageBody::N318EndOfWhois {
|
||||
client: user.nickname.clone(),
|
||||
nick: nick,
|
||||
msg: "End of /WHOIS list".into(),
|
||||
},
|
||||
}
|
||||
.write_async(writer)
|
||||
.await?;
|
||||
writer.flush().await?
|
||||
}
|
||||
(None, None) => {
|
||||
ServerMessage {
|
||||
tags: vec![],
|
||||
sender: Some(config.server_name.clone()),
|
||||
body: ServerMessageBody::N431ErrNoNicknameGiven {
|
||||
client: user.nickname.clone(),
|
||||
message: "No nickname given".into(),
|
||||
},
|
||||
}
|
||||
.write_async(writer)
|
||||
.await?;
|
||||
writer.flush().await?
|
||||
}
|
||||
}
|
||||
ClientMessage::Whois { arg } => {
|
||||
arg.handle(
|
||||
&config.server_name,
|
||||
&user.nickname,
|
||||
writer,
|
||||
storage,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
|
||||
ClientMessage::Mode { target } => {
|
||||
match target {
|
||||
Recipient::Nick(nickname) => {
|
||||
|
|
|
@ -8,10 +8,11 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
|
|||
use tokio::net::tcp::{ReadHalf, WriteHalf};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
use lavina_core::repo::{Storage, StorageConfig};
|
||||
use lavina_core::{player::PlayerRegistry, room::RoomRegistry};
|
||||
use projection_irc::APP_VERSION;
|
||||
use lavina_core::repo::{Storage, StorageConfig};
|
||||
use projection_irc::{launch, read_irc_message, RunningServer, ServerConfig};
|
||||
use projection_irc::APP_VERSION;
|
||||
|
||||
struct TestScope<'a> {
|
||||
reader: BufReader<ReadHalf<'a>>,
|
||||
writer: WriteHalf<'a>,
|
||||
|
@ -193,6 +194,52 @@ async fn scenario_cap_full_negotiation() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// #[tokio::test]
|
||||
// async fn scenario_whois_command() -> Result<()> {
|
||||
// let mut server = TestServer::start().await?;
|
||||
//
|
||||
// server.storage.create_user("tester").await?;
|
||||
// server.storage.set_password("tester", "password").await?;
|
||||
// let mut stream = TcpStream::connect(server.server.addr).await?;
|
||||
// let mut s = TestScope::new(&mut stream);
|
||||
//
|
||||
//
|
||||
//
|
||||
// s.send("PASS password").await?;
|
||||
// s.send("NICK tester").await?;
|
||||
// s.send("USER UserName 0 * :Real Name").await?;
|
||||
//
|
||||
// s.expect(":testserver 001 tester :Welcome to testserver Server").await?;
|
||||
// s.expect(":testserver 002 tester :Welcome to testserver Server").await?;
|
||||
// s.expect(":testserver 003 tester :Welcome to testserver Server").await?;
|
||||
// s.expect(
|
||||
// format!(
|
||||
// ":testserver 004 tester testserver {} r CFILPQbcefgijklmnopqrstvz",
|
||||
// &APP_VERSION
|
||||
// )
|
||||
// .as_str(),
|
||||
// )
|
||||
// .await?;
|
||||
// s.expect(":testserver 005 tester CHANTYPES=# :are supported by this server").await?;
|
||||
// s.expect_nothing().await?;
|
||||
//
|
||||
// s.send("WHOIS tester").await?;
|
||||
// s.expect(" ").await?;
|
||||
//
|
||||
// s.send("QUIT :Leaving").await?;
|
||||
// s.expect(":testserver ERROR :Leaving the server").await?;
|
||||
// s.expect_eof().await?;
|
||||
//
|
||||
// stream.shutdown().await?;
|
||||
//
|
||||
// // wrap up
|
||||
//
|
||||
// server.server.terminate().await?;
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn scenario_cap_short_negotiation() -> Result<()> {
|
||||
let mut server = TestServer::start().await?;
|
||||
|
|
|
@ -190,7 +190,7 @@ async fn handle_socket(
|
|||
pin!(termination);
|
||||
select! {
|
||||
biased;
|
||||
_ = &mut termination =>{
|
||||
_ = &mut termination => {
|
||||
log::info!("Socket handling was terminated");
|
||||
return Ok(())
|
||||
},
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use nom::combinator::{all_consuming, opt};
|
||||
use nom::error::ErrorKind;
|
||||
use nonempty::NonEmpty;
|
||||
|
||||
use super::*;
|
||||
|
@ -45,8 +44,7 @@ pub enum ClientMessage {
|
|||
},
|
||||
/// WHOIS [<target>] <nick>
|
||||
Whois {
|
||||
target: Option<Str>, // server_name or nick_name
|
||||
nick: Option<Str>,
|
||||
arg: command_args::Whois,
|
||||
},
|
||||
/// `TOPIC <chan> :<topic>`
|
||||
Topic {
|
||||
|
@ -69,6 +67,17 @@ pub enum ClientMessage {
|
|||
Authenticate(Str),
|
||||
}
|
||||
|
||||
pub mod command_args {
|
||||
use crate::prelude::Str;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Whois {
|
||||
Nick(Str),
|
||||
TargetNick(Str, Str),
|
||||
EmptyArgs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client_message(input: &str) -> Result<ClientMessage> {
|
||||
let res = all_consuming(alt((
|
||||
client_message_capability,
|
||||
|
@ -191,23 +200,19 @@ fn client_message_whois(input: &str) -> IResult<&str, ClientMessage> {
|
|||
[nick] => Ok((
|
||||
"",
|
||||
ClientMessage::Whois {
|
||||
target: None,
|
||||
nick: Some(nick.into()),
|
||||
arg: command_args::Whois::Nick(nick.into()),
|
||||
},
|
||||
)),
|
||||
[target, nick, ..] => Ok((
|
||||
"",
|
||||
ClientMessage::Whois {
|
||||
target: Some(target.into()),
|
||||
nick: Some(nick.into()),
|
||||
arg: command_args::Whois::TargetNick(target.into(), nick.into()),
|
||||
},
|
||||
)),
|
||||
// fixme: idk how to deal with this in more elegant way
|
||||
[] => Ok((
|
||||
"",
|
||||
ClientMessage::Whois {
|
||||
target: None,
|
||||
nick: None,
|
||||
arg: command_args::Whois::EmptyArgs,
|
||||
},
|
||||
)),
|
||||
}
|
||||
|
@ -412,41 +417,33 @@ mod test {
|
|||
let res_none_none_params = client_message(test_none_none_params);
|
||||
|
||||
let expected_arg = ClientMessage::Whois {
|
||||
target: None,
|
||||
nick: Some("val".into()),
|
||||
arg: command_args::Whois::Nick("val".into()),
|
||||
};
|
||||
let expected_user_user = ClientMessage::Whois {
|
||||
target: Some("val".into()),
|
||||
nick: Some("val".into()),
|
||||
arg: command_args::Whois::TargetNick("val".into(), "val".into()),
|
||||
};
|
||||
|
||||
let expected_server_user = ClientMessage::Whois {
|
||||
target: Some("com.test.server".into()),
|
||||
nick: Some("user".into()),
|
||||
arg: command_args::Whois::TargetNick("com.test.server".into(), "user".into()),
|
||||
};
|
||||
|
||||
let expected_user_server = ClientMessage::Whois {
|
||||
target: Some("user".into()),
|
||||
nick: Some("com.test.server".into()),
|
||||
arg: command_args::Whois::TargetNick("user".into(), "com.test.server".into()),
|
||||
};
|
||||
|
||||
let expected_user_list = ClientMessage::Whois {
|
||||
target: None,
|
||||
nick: Some("user_1,user_2,user_3".into()),
|
||||
arg: command_args::Whois::Nick("user_1,user_2,user_3".into()),
|
||||
};
|
||||
let expected_server_user_list = ClientMessage::Whois {
|
||||
target: Some("com.test.server".into()),
|
||||
nick: Some("user_1,user_2,user_3".into()),
|
||||
arg: command_args::Whois::TargetNick("com.test.server".into(), "user_1,user_2,user_3".into()),
|
||||
};
|
||||
|
||||
let expected_more_than_two_params = ClientMessage::Whois {
|
||||
target: Some("test.server".into()),
|
||||
nick: Some("user_1,user_2,user_3".into()),
|
||||
arg: command_args::Whois::TargetNick("test.server".into(), "user_1,user_2,user_3".into()),
|
||||
};
|
||||
|
||||
let expected_none_none_params = ClientMessage::Whois {
|
||||
target: None,
|
||||
nick: None,
|
||||
arg: command_args::Whois::EmptyArgs,
|
||||
};
|
||||
|
||||
assert_matches!(res_one_arg, Ok(result) => assert_eq!(expected_arg, result));
|
||||
|
|
|
@ -5,6 +5,7 @@ pub mod server;
|
|||
#[cfg(test)]
|
||||
mod testkit;
|
||||
pub mod user;
|
||||
pub mod response;
|
||||
|
||||
use crate::prelude::Str;
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
use std::future::Future;
|
||||
|
||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||
|
||||
use crate::prelude::Str;
|
||||
use crate::Tag;
|
||||
|
||||
pub trait SendResponseBody {
|
||||
fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> impl Future<Output = std::io::Result<()>>;
|
||||
}
|
||||
|
||||
pub trait SendResponseMessage<Message> {
|
||||
fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> impl Future<Output = std::io::Result<()>>;
|
||||
}
|
||||
|
||||
/// Server-to-client enum agnostic message
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct IrcResponseMessage<T: SendResponseBody> {
|
||||
/// Optional tags section, prefixed with `@`
|
||||
pub tags: Vec<Tag>,
|
||||
/// Optional server name, prefixed with `:`.
|
||||
pub sender: Option<Str>,
|
||||
pub body: T,
|
||||
}
|
||||
|
||||
impl<T: SendResponseBody> IrcResponseMessage<T> {
|
||||
pub fn empty_tags(sender: Option<Str>, body: T) -> Self {
|
||||
IrcResponseMessage {
|
||||
tags: vec![],
|
||||
sender,
|
||||
body,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(tags: Vec<Tag>, sender: Option<Str>, body: T) -> Self {
|
||||
IrcResponseMessage { tags, sender, body }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SendResponseBody> SendResponseMessage<IrcResponseMessage<T>> for IrcResponseMessage<T> {
|
||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||
if let Some(sender) = &self.sender {
|
||||
writer.write_all(b":").await?;
|
||||
writer.write_all(sender.as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
}
|
||||
self.body.write_response(writer).await?;
|
||||
writer.write_all(b"\r\n").await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -283,7 +283,7 @@ impl ServerMessageBody {
|
|||
writer.write_all(msg.as_bytes()).await?;
|
||||
}
|
||||
ServerMessageBody::N318EndOfWhois { client, nick, msg } => {
|
||||
writer.write_all(b"b318 ").await?;
|
||||
writer.write_all(b"318 ").await?;
|
||||
writer.write_all(client.as_bytes()).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(nick.as_bytes()).await?;
|
||||
|
|
Loading…
Reference in New Issue