forked from lavina/lavina
added tests, refactoring made
This commit is contained in:
parent
b6841ad033
commit
66a3bb7a68
|
@ -8,6 +8,7 @@
|
||||||
//! A player actor is a serial handler of commands from a single player. It is preferable to run all per-player validations in the player actor,
|
//! A player actor is a serial handler of commands from a single player. It is preferable to run all per-player validations in the player actor,
|
||||||
//! so that they don't overload the room actor.
|
//! so that they don't overload the room actor.
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
@ -124,6 +125,15 @@ impl PlayerConnection {
|
||||||
self.player_handle.send(ActorCommand::ClientCommand(cmd, self.connection_id.clone())).await;
|
self.player_handle.send(ActorCommand::ClientCommand(cmd, self.connection_id.clone())).await;
|
||||||
Ok(deferred.await?)
|
Ok(deferred.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handler in [Player::check_user_existance].
|
||||||
|
#[tracing::instrument(skip(self), name = "PlayerConnection::send_dialog_message")]
|
||||||
|
pub async fn check_user_existance(&self, recipient: PlayerId) -> Result<GetInfoResult> {
|
||||||
|
let (promise, deferred) = oneshot();
|
||||||
|
let cmd = ClientCommand::GetInfo { recipient, promise };
|
||||||
|
self.player_handle.send(ActorCommand::ClientCommand(cmd, self.connection_id.clone())).await;
|
||||||
|
Ok(deferred.await?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle to a player actor.
|
/// Handle to a player actor.
|
||||||
|
@ -200,6 +210,15 @@ pub enum ClientCommand {
|
||||||
body: Str,
|
body: Str,
|
||||||
promise: Promise<()>,
|
promise: Promise<()>,
|
||||||
},
|
},
|
||||||
|
GetInfo {
|
||||||
|
recipient: PlayerId,
|
||||||
|
promise: Promise<GetInfoResult>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum GetInfoResult {
|
||||||
|
UserExists,
|
||||||
|
UserDoesntExist,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum JoinResult {
|
pub enum JoinResult {
|
||||||
|
@ -500,6 +519,10 @@ impl Player {
|
||||||
self.send_dialog_message(connection_id, recipient, body).await;
|
self.send_dialog_message(connection_id, recipient, body).await;
|
||||||
let _ = promise.send(());
|
let _ = promise.send(());
|
||||||
}
|
}
|
||||||
|
ClientCommand::GetInfo { recipient, promise } => {
|
||||||
|
let result = self.check_user_existance(recipient).await;
|
||||||
|
let _ = promise.send(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,6 +619,15 @@ impl Player {
|
||||||
self.broadcast_update(update, connection_id).await;
|
self.broadcast_update(update, connection_id).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(self), name = "Player::check_user_existance")]
|
||||||
|
async fn check_user_existance(&self, recipient: PlayerId) -> GetInfoResult {
|
||||||
|
if self.storage.check_user_existance(recipient.as_inner().as_ref()).await.unwrap() {
|
||||||
|
GetInfoResult::UserExists
|
||||||
|
} else {
|
||||||
|
GetInfoResult::UserDoesntExist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Broadcasts an update to all connections except the one with the given id.
|
/// Broadcasts an update to all connections except the one with the given id.
|
||||||
///
|
///
|
||||||
/// This is called after handling a client command.
|
/// This is called after handling a client command.
|
||||||
|
|
|
@ -7,7 +7,7 @@ use anyhow::anyhow;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::sqlite::SqliteConnectOptions;
|
use sqlx::sqlite::SqliteConnectOptions;
|
||||||
use sqlx::{ConnectOptions, Connection, FromRow, Sqlite, SqliteConnection, Transaction};
|
use sqlx::{ConnectOptions, Connection, Execute, FromRow, Sqlite, SqliteConnection, Transaction};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
@ -56,6 +56,15 @@ impl Storage {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(self), name = "Storage::check_user_existance")]
|
||||||
|
pub async fn check_user_existance(&self, username: &str) -> Result<bool> {
|
||||||
|
let mut executor = self.conn.lock().await;
|
||||||
|
let result: Option<(String,)> =
|
||||||
|
sqlx::query_as("select name from users where name = ?;").bind(username).fetch_optional(&mut *executor).await?;
|
||||||
|
|
||||||
|
Ok(result.is_some())
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(self), name = "Storage::retrieve_room_by_name")]
|
#[tracing::instrument(skip(self), name = "Storage::retrieve_room_by_name")]
|
||||||
pub async fn retrieve_room_by_name(&self, name: &str) -> Result<Option<StoredRoom>> {
|
pub async fn retrieve_room_by_name(&self, name: &str) -> Result<Option<StoredRoom>> {
|
||||||
let mut executor = self.conn.lock().await;
|
let mut executor = self.conn.lock().await;
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
use lavina_core::{prelude::Str, repo::Storage};
|
|
||||||
use std::future::Future;
|
|
||||||
use tokio::io::AsyncWrite;
|
|
||||||
|
|
||||||
pub mod whois;
|
|
||||||
|
|
||||||
pub struct HandlerArgs<T: AsyncWrite + Unpin> {
|
|
||||||
server_name: Str,
|
|
||||||
client: Str,
|
|
||||||
writer: T,
|
|
||||||
storage: Storage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> HandlerArgs<T>
|
|
||||||
where
|
|
||||||
T: AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
pub fn new(server_name: Str, client: Str, writer: T, storage: Storage) -> HandlerArgs<T> {
|
|
||||||
HandlerArgs {
|
|
||||||
server_name,
|
|
||||||
client,
|
|
||||||
writer,
|
|
||||||
storage,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Handler<T>
|
|
||||||
where
|
|
||||||
T: AsyncWrite + Unpin,
|
|
||||||
{
|
|
||||||
fn handle(&self, arg: HandlerArgs<T>) -> impl Future<Output = anyhow::Result<()>>;
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
use tokio::io::AsyncWrite;
|
|
||||||
|
|
||||||
use lavina_core::prelude::Str;
|
|
||||||
use proto_irc::client::command_args::Whois;
|
|
||||||
use proto_irc::response::{IrcResponseMessage, WriteResponse};
|
|
||||||
|
|
||||||
use crate::commands::whois::error::{ErrNoNicknameGiven431, ErrNoSuchNick401};
|
|
||||||
use crate::commands::whois::response::{RplEndOfWhois318, RplWhoIsUser311};
|
|
||||||
use crate::commands::Handler;
|
|
||||||
|
|
||||||
use super::HandlerArgs;
|
|
||||||
|
|
||||||
pub mod error;
|
|
||||||
pub mod response;
|
|
||||||
|
|
||||||
impl<T: AsyncWrite + Unpin> Handler<T> for Whois {
|
|
||||||
async fn handle(&self, body: HandlerArgs<T>) -> anyhow::Result<()> {
|
|
||||||
match self {
|
|
||||||
Whois::Nick(nick) => handle_nick_target(nick.clone(), body).await?,
|
|
||||||
Whois::TargetNick(_, nick) => handle_nick_target(nick.clone(), body).await?,
|
|
||||||
Whois::EmptyArgs => {
|
|
||||||
let HandlerArgs {
|
|
||||||
server_name,
|
|
||||||
mut writer,
|
|
||||||
..
|
|
||||||
} = body;
|
|
||||||
IrcResponseMessage::empty_tags(
|
|
||||||
Some(server_name.clone()),
|
|
||||||
ErrNoNicknameGiven431::new(server_name.clone()),
|
|
||||||
)
|
|
||||||
.write_response(&mut writer)
|
|
||||||
.await?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_nick_target(nick: Str, body: HandlerArgs<impl AsyncWrite + Unpin>) -> anyhow::Result<()> {
|
|
||||||
let HandlerArgs {
|
|
||||||
server_name,
|
|
||||||
mut writer,
|
|
||||||
client,
|
|
||||||
mut storage,
|
|
||||||
} = body;
|
|
||||||
if let Some(user) = storage.retrieve_user_by_name(nick.clone().as_ref()).await? {
|
|
||||||
IrcResponseMessage::empty_tags(
|
|
||||||
Some(server_name.clone()),
|
|
||||||
RplWhoIsUser311::new(
|
|
||||||
client.clone(),
|
|
||||||
nick.clone(),
|
|
||||||
Some(Str::from(user.name.clone())),
|
|
||||||
server_name.clone(),
|
|
||||||
nick.clone(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.write_response(&mut writer)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
IrcResponseMessage::empty_tags(
|
|
||||||
Some(server_name.clone()),
|
|
||||||
RplEndOfWhois318::new(client.clone(), user.name.clone().into()),
|
|
||||||
)
|
|
||||||
.write_response(&mut writer)
|
|
||||||
.await?
|
|
||||||
} else {
|
|
||||||
ErrNoSuchNick401::new(client.clone(), nick.clone()).write_response(&mut writer).await?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
use lavina_core::{player::PlayerConnection, prelude::Str, LavinaCore};
|
||||||
|
use std::future::Future;
|
||||||
|
use tokio::io::AsyncWrite;
|
||||||
|
|
||||||
|
pub struct IrcConnection<'a, T: AsyncWrite + Unpin> {
|
||||||
|
pub server_name: Str,
|
||||||
|
/// client is nick of requester
|
||||||
|
pub client: Str,
|
||||||
|
pub writer: &'a mut T,
|
||||||
|
pub player_connection: &'a mut PlayerConnection,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Handler<T>
|
||||||
|
where
|
||||||
|
T: AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
|
fn handle(&self, arg: IrcConnection<T>) -> impl Future<Output = anyhow::Result<()>>;
|
||||||
|
}
|
|
@ -29,11 +29,12 @@ use proto_irc::user::PrefixedNick;
|
||||||
use proto_irc::{Chan, Recipient, Tag};
|
use proto_irc::{Chan, Recipient, Tag};
|
||||||
use sasl::AuthBody;
|
use sasl::AuthBody;
|
||||||
mod cap;
|
mod cap;
|
||||||
mod commands;
|
use handler::Handler;
|
||||||
|
mod whois;
|
||||||
|
|
||||||
use crate::cap::Capabilities;
|
use crate::cap::Capabilities;
|
||||||
use commands::Handler;
|
|
||||||
use proto_irc::response::IrcResponseMessage;
|
mod handler;
|
||||||
|
|
||||||
pub const APP_VERSION: &str = concat!("lavina", "_", env!("CARGO_PKG_VERSION"));
|
pub const APP_VERSION: &str = concat!("lavina", "_", env!("CARGO_PKG_VERSION"));
|
||||||
|
|
||||||
|
@ -80,7 +81,7 @@ async fn handle_socket(
|
||||||
match registered_user {
|
match registered_user {
|
||||||
Ok(user) => {
|
Ok(user) => {
|
||||||
log::debug!("User registered");
|
log::debug!("User registered");
|
||||||
handle_registered_socket(config, core.players, core.rooms, &mut reader, &mut writer, user).await?;
|
handle_registered_socket(config, core.players, core.rooms, &mut reader, &mut writer, user, &mut storage).await?;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::debug!("Registration failed: {err}");
|
log::debug!("Registration failed: {err}");
|
||||||
|
@ -811,13 +812,15 @@ async fn handle_incoming_message(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientMessage::Whois { arg } => {
|
ClientMessage::Whois { arg } => {
|
||||||
arg.handle(commands::HandlerArgs::new(
|
arg.handle(handler::IrcConnection {
|
||||||
config.server_name.clone(),
|
server_name: config.server_name.clone(),
|
||||||
user.nickname.clone(),
|
client: user.nickname.clone(),
|
||||||
writer,
|
writer,
|
||||||
storage.clone(),
|
player_connection: user_handle,
|
||||||
))
|
})
|
||||||
.await?
|
.await?;
|
||||||
|
|
||||||
|
writer.flush().await?;
|
||||||
}
|
}
|
||||||
ClientMessage::Mode { target } => {
|
ClientMessage::Mode { target } => {
|
||||||
match target {
|
match target {
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
use lavina_core::{
|
||||||
|
player::{GetInfoResult, PlayerId},
|
||||||
|
prelude::Str,
|
||||||
|
};
|
||||||
|
use proto_irc::{
|
||||||
|
client::command_args::Whois,
|
||||||
|
commands::whois::{
|
||||||
|
error::{ErrNoNicknameGiven431, ErrNoSuchNick401},
|
||||||
|
response::RplEndOfWhois318,
|
||||||
|
},
|
||||||
|
response::{IrcResponseMessage, WriteResponse},
|
||||||
|
};
|
||||||
|
use tokio::io::AsyncWrite;
|
||||||
|
|
||||||
|
use crate::handler::{Handler, IrcConnection};
|
||||||
|
|
||||||
|
impl<T: AsyncWrite + Unpin> Handler<T> for Whois {
|
||||||
|
async fn handle(&self, body: IrcConnection<'_, T>) -> anyhow::Result<()> {
|
||||||
|
match self {
|
||||||
|
Whois::Nick(nick) => handle_nick_target(nick.clone(), body).await?,
|
||||||
|
Whois::TargetNick(_, nick) => handle_nick_target(nick.clone(), body).await?,
|
||||||
|
Whois::EmptyArgs => {
|
||||||
|
let IrcConnection {
|
||||||
|
server_name,
|
||||||
|
mut writer,
|
||||||
|
..
|
||||||
|
} = body;
|
||||||
|
IrcResponseMessage::empty_tags(
|
||||||
|
Some(server_name.clone()),
|
||||||
|
ErrNoNicknameGiven431::new(server_name.clone()),
|
||||||
|
)
|
||||||
|
.write_response(&mut writer)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_nick_target(nick: Str, body: IrcConnection<'_, impl AsyncWrite + Unpin>) -> anyhow::Result<()> {
|
||||||
|
let IrcConnection {
|
||||||
|
server_name,
|
||||||
|
mut writer,
|
||||||
|
client,
|
||||||
|
player_connection,
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if let GetInfoResult::UserDoesntExist =
|
||||||
|
player_connection.check_user_existance(PlayerId::from(nick.clone())?).await?
|
||||||
|
{
|
||||||
|
IrcResponseMessage::empty_tags(
|
||||||
|
Some(server_name.clone()),
|
||||||
|
ErrNoSuchNick401::new(client.clone(), nick.clone()),
|
||||||
|
)
|
||||||
|
.write_response(&mut writer)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
|
||||||
|
IrcResponseMessage::empty_tags(
|
||||||
|
Some(server_name.clone()),
|
||||||
|
RplEndOfWhois318::new(client.clone(), nick.clone()),
|
||||||
|
)
|
||||||
|
.write_response(&mut writer)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -383,7 +383,15 @@ async fn scenario_two_users() -> Result<()> {
|
||||||
// The second user should receive the PART message
|
// The second user should receive the PART message
|
||||||
s2.expect(":tester1 PART #test").await?;
|
s2.expect(":tester1 PART #test").await?;
|
||||||
|
|
||||||
|
s1.send("WHOIS tester2").await?;
|
||||||
|
s1.expect(":testserver 318 tester1 tester2 :End of /WHOIS list").await?;
|
||||||
|
|
||||||
stream1.shutdown().await?;
|
stream1.shutdown().await?;
|
||||||
|
s2.send("WHOIS tester3").await?;
|
||||||
|
s2.expect(":testserver 401 tester2 tester3 :No such nick/channel").await?;
|
||||||
|
s2.expect(":testserver 318 tester2 tester3 :End of /WHOIS list").await?;
|
||||||
|
|
||||||
|
|
||||||
stream2.shutdown().await?;
|
stream2.shutdown().await?;
|
||||||
|
|
||||||
server.shutdown().await?;
|
server.shutdown().await?;
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod whois;
|
|
@ -1,7 +1,7 @@
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||||
|
|
||||||
use lavina_core::prelude::Str;
|
use crate::{prelude::Str, response::WriteResponse};
|
||||||
use proto_irc::response::WriteResponse;
|
|
||||||
|
|
||||||
/// ErrNoSuchNick401
|
/// ErrNoSuchNick401
|
||||||
pub struct ErrNoSuchNick401 {
|
pub struct ErrNoSuchNick401 {
|
||||||
|
@ -34,7 +34,7 @@ impl ErrNoNicknameGiven431 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteResponse for ErrNoSuchNick401 {
|
impl WriteResponse for ErrNoSuchNick401 {
|
||||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||||
writer.write_all(b"401 ").await?;
|
writer.write_all(b"401 ").await?;
|
||||||
writer.write_all(self.client.as_bytes()).await?;
|
writer.write_all(self.client.as_bytes()).await?;
|
||||||
writer.write_all(b" ").await?;
|
writer.write_all(b" ").await?;
|
||||||
|
@ -46,7 +46,7 @@ impl WriteResponse for ErrNoSuchNick401 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteResponse for ErrNoNicknameGiven431 {
|
impl WriteResponse for ErrNoNicknameGiven431 {
|
||||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||||
writer.write_all(b"431").await?;
|
writer.write_all(b"431").await?;
|
||||||
writer.write_all(self.client.as_bytes()).await?;
|
writer.write_all(self.client.as_bytes()).await?;
|
||||||
writer.write_all(b" :").await?;
|
writer.write_all(b" :").await?;
|
||||||
|
@ -56,7 +56,7 @@ impl WriteResponse for ErrNoNicknameGiven431 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteResponse for ErrNoSuchServer402 {
|
impl WriteResponse for ErrNoSuchServer402 {
|
||||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||||
writer.write_all(b"402 ").await?;
|
writer.write_all(b"402 ").await?;
|
||||||
writer.write_all(self.client.as_bytes()).await?;
|
writer.write_all(self.client.as_bytes()).await?;
|
||||||
writer.write_all(b" ").await?;
|
writer.write_all(b" ").await?;
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod error;
|
||||||
|
pub mod response;
|
|
@ -2,8 +2,7 @@ use std::{net::IpAddr, time::SystemTime};
|
||||||
|
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||||
|
|
||||||
use lavina_core::prelude::Str;
|
use crate::{prelude::Str, response::WriteResponse, Chan};
|
||||||
use proto_irc::{response::WriteResponse, Chan};
|
|
||||||
|
|
||||||
/// "<client> <nick> :has client certificate fingerprint <fingerprint>"
|
/// "<client> <nick> :has client certificate fingerprint <fingerprint>"
|
||||||
/// Clients MUST only be sent this numeric if they are either using the WHOIS command on themselves
|
/// Clients MUST only be sent this numeric if they are either using the WHOIS command on themselves
|
||||||
|
@ -150,12 +149,12 @@ impl RplEndOfWhois318 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteResponse for RplWhoIsUser311 {
|
impl WriteResponse for RplWhoIsUser311 {
|
||||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||||
writer.write_all(b"311 ").await?;
|
writer.write_all(b"311 ").await?;
|
||||||
writer.write_all(self.client.as_bytes()).await?;
|
writer.write_all(self.client.as_bytes()).await?;
|
||||||
writer.write_all(b" ").await?;
|
writer.write_all(b" ").await?;
|
||||||
writer.write_all(self.nick.as_bytes()).await?;
|
writer.write_all(self.nick.as_bytes()).await?;
|
||||||
if let Some(username) = self.username {
|
if let Some(username) = &self.username {
|
||||||
writer.write_all(b" ").await?;
|
writer.write_all(b" ").await?;
|
||||||
writer.write_all(username.as_bytes()).await?;
|
writer.write_all(username.as_bytes()).await?;
|
||||||
}
|
}
|
||||||
|
@ -173,7 +172,7 @@ impl WriteResponse for RplWhoIsUser311 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteResponse for RplEndOfWhois318 {
|
impl WriteResponse for RplEndOfWhois318 {
|
||||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||||
writer.write_all(b"318 ").await?;
|
writer.write_all(b"318 ").await?;
|
||||||
writer.write_all(self.client.as_bytes()).await?;
|
writer.write_all(self.client.as_bytes()).await?;
|
||||||
writer.write_all(b" ").await?;
|
writer.write_all(b" ").await?;
|
|
@ -6,6 +6,7 @@ pub mod server;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod testkit;
|
mod testkit;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
pub mod commands;
|
||||||
|
|
||||||
use crate::prelude::Str;
|
use crate::prelude::Str;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::prelude::Str;
|
||||||
use crate::Tag;
|
use crate::Tag;
|
||||||
|
|
||||||
pub trait WriteResponse {
|
pub trait WriteResponse {
|
||||||
fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> impl Future<Output = std::io::Result<()>>;
|
fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> impl Future<Output = std::io::Result<()>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Server-to-client enum agnostic message
|
/// Server-to-client enum agnostic message
|
||||||
|
@ -34,7 +34,7 @@ impl<T> IrcResponseMessage<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: WriteResponse> WriteResponse for IrcResponseMessage<T> {
|
impl<T: WriteResponse> WriteResponse for IrcResponseMessage<T> {
|
||||||
async fn write_response(self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
async fn write_response(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
||||||
if let Some(sender) = &self.sender {
|
if let Some(sender) = &self.sender {
|
||||||
writer.write_all(b":").await?;
|
writer.write_all(b":").await?;
|
||||||
writer.write_all(sender.as_bytes()).await?;
|
writer.write_all(sender.as_bytes()).await?;
|
||||||
|
|
Loading…
Reference in New Issue