forked from lavina/lavina
use struct as argument in trait
This commit is contained in:
parent
67fbcc0c41
commit
a9dfe22d8c
|
@ -1,17 +1,33 @@
|
|||
use lavina_core::prelude::Str;
|
||||
use lavina_core::repo::Storage;
|
||||
use proto_irc::response::WriteResponse;
|
||||
use lavina_core::{prelude::Str, repo::Storage};
|
||||
use std::future::Future;
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
pub mod whois;
|
||||
|
||||
pub trait Handler {
|
||||
fn handle(
|
||||
&self,
|
||||
server_name: Str,
|
||||
client: Str,
|
||||
writer: &mut (impl AsyncWrite + Unpin),
|
||||
storage: Storage,
|
||||
) -> impl Future<Output = anyhow::Result<()>>;
|
||||
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<()>>;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use tokio::io::{AsyncWrite, AsyncWriteExt};
|
|||
use lavina_core::prelude::Str;
|
||||
use proto_irc::response::WriteResponse;
|
||||
|
||||
/// ERR_NOSUCHNICK (401)
|
||||
/// ErrNoSuchNick401
|
||||
pub struct ErrNoSuchNick401 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
|
@ -15,7 +15,7 @@ impl ErrNoSuchNick401 {
|
|||
}
|
||||
}
|
||||
|
||||
/// ERR_NOSUCHSERVER (402)
|
||||
/// ErrNoSuchServer402
|
||||
struct ErrNoSuchServer402 {
|
||||
client: Str,
|
||||
/// target parameter in WHOIS
|
||||
|
@ -23,7 +23,7 @@ struct ErrNoSuchServer402 {
|
|||
server_name: Str,
|
||||
}
|
||||
|
||||
/// ERR_NONICKNAMEGIVEN (431)
|
||||
/// ErrNoNicknameGiven431
|
||||
pub struct ErrNoNicknameGiven431 {
|
||||
client: Str,
|
||||
}
|
||||
|
|
|
@ -1,39 +1,34 @@
|
|||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||
use tracing::instrument::WithSubscriber;
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
use lavina_core::prelude::Str;
|
||||
use lavina_core::repo::Storage;
|
||||
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::{RplWhoIsUser311, RPL_ENDOFWHOIS_318};
|
||||
use crate::commands::whois::response::{RplEndOfWhois318, RplWhoIsUser311};
|
||||
use crate::commands::Handler;
|
||||
|
||||
use super::HandlerArgs;
|
||||
|
||||
pub mod error;
|
||||
pub mod response;
|
||||
|
||||
impl Handler for Whois {
|
||||
async fn handle(
|
||||
&self,
|
||||
server_name: Str,
|
||||
client: Str,
|
||||
writer: &mut (impl AsyncWrite + Unpin),
|
||||
mut storage: Storage,
|
||||
) -> anyhow::Result<()> {
|
||||
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(), None, server_name, client, writer, storage.clone()).await?
|
||||
}
|
||||
Whois::TargetNick(target, nick) => {
|
||||
handle_nick_target(nick.clone(), Some(target.clone()), server_name, client, writer, storage).await?
|
||||
}
|
||||
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(writer)
|
||||
.write_response(&mut writer)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
|
@ -41,15 +36,13 @@ impl Handler for Whois {
|
|||
}
|
||||
}
|
||||
|
||||
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),
|
||||
mut storage: Storage,
|
||||
) -> anyhow::Result<()> {
|
||||
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()),
|
||||
|
@ -58,19 +51,20 @@ async fn handle_nick_target(
|
|||
nick.clone(),
|
||||
Some(Str::from(user.name.clone())),
|
||||
server_name.clone(),
|
||||
nick.clone(),
|
||||
),
|
||||
)
|
||||
.write_response(writer)
|
||||
.write_response(&mut writer)
|
||||
.await?;
|
||||
|
||||
IrcResponseMessage::empty_tags(
|
||||
Some(server_name.clone()),
|
||||
RPL_ENDOFWHOIS_318::new(client.clone(), user.name.clone().into()),
|
||||
RplEndOfWhois318::new(client.clone(), user.name.clone().into()),
|
||||
)
|
||||
.write_response(writer)
|
||||
.write_response(&mut writer)
|
||||
.await?
|
||||
} else {
|
||||
ErrNoSuchNick401::new(client.clone(), nick.clone()).write_response(writer).await?
|
||||
ErrNoSuchNick401::new(client.clone(), nick.clone()).write_response(&mut writer).await?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
use std::{net::IpAddr, time::SystemTime};
|
||||
|
||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||
|
||||
use lavina_core::prelude::Str;
|
||||
use proto_irc::response::WriteResponse;
|
||||
use proto_irc::{response::WriteResponse, Chan};
|
||||
|
||||
struct RplWhoisCertfp276;
|
||||
struct RplWhoisRegNick307;
|
||||
/// "<client> <nick> :has client certificate fingerprint <fingerprint>"
|
||||
/// Clients MUST only be sent this numeric if they are either using the WHOIS command on themselves
|
||||
/// or they are an operator.
|
||||
struct RplWhoisCertfp276 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
fingerprint: Str,
|
||||
}
|
||||
|
||||
/// "<client> <nick> :has identified for this nick"
|
||||
struct RplWhoisRegNick307 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
}
|
||||
/// "<client> <nick> <username> <host> * :<realname>"
|
||||
pub struct RplWhoIsUser311 {
|
||||
client: Str,
|
||||
/// unique name
|
||||
|
@ -13,37 +28,124 @@ pub struct RplWhoIsUser311 {
|
|||
username: Option<Str>,
|
||||
/// server name
|
||||
host: Str,
|
||||
realname: Str,
|
||||
}
|
||||
|
||||
impl RplWhoIsUser311 {
|
||||
pub fn new(client: Str, nick: Str, username: Option<Str>, host: Str) -> Self {
|
||||
pub fn new(client: Str, nick: Str, username: Option<Str>, host: Str, realname: Str) -> Self {
|
||||
RplWhoIsUser311 {
|
||||
client,
|
||||
nick,
|
||||
username,
|
||||
host,
|
||||
realname,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// "<client> <nick> <server> :<server info>"
|
||||
struct RplWhoisServer312 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
server: Str,
|
||||
/// description of the server
|
||||
server_info: String,
|
||||
}
|
||||
|
||||
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> <nick> :is an IRC operator"
|
||||
struct RplWhoisOperator313 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
}
|
||||
impl RPL_ENDOFWHOIS_318 {
|
||||
|
||||
/// Sent as a reply to the WHOIS command, this numeric indicates how long the client with the nickname <nick> has been idle.
|
||||
/// <secs> is the number of seconds since the client has been active.
|
||||
/// Servers generally denote specific commands (for instance, perhaps JOIN, PRIVMSG, NOTICE, etc) as updating the ‘idle time’,
|
||||
/// and calculate this off when the idle time was last updated.
|
||||
/// <signon> is a unix timestamp representing when the user joined the network. The text used in the last param of this message may vary.
|
||||
struct RplWhoisIdle317 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
/// number of seconds client been active
|
||||
secs: f32,
|
||||
sigon: SystemTime,
|
||||
}
|
||||
|
||||
struct PrefixedChan {
|
||||
prefix: Option<Str>,
|
||||
chan: Chan,
|
||||
}
|
||||
|
||||
struct RplWhoisChannels319 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
highest_membership: PrefixedChan,
|
||||
/// The last parameter of this numeric is a list of [prefix]<channel> pairs,
|
||||
/// delimited by a SPACE character (' ', 0x20).
|
||||
/// RPL_WHOISCHANNELS can be sent multiple times in the same whois reply,
|
||||
/// if the target is on too many channels to fit in a single message.
|
||||
chanels: [PrefixedChan; 20],
|
||||
}
|
||||
|
||||
/// is used for extra human-readable information on the client with nickname <nick>.
|
||||
/// This should only be used for non-essential information,
|
||||
/// that does not need to be machine-readable or understood by client software.
|
||||
struct RplWhoisSpecial320 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
}
|
||||
/// "<client> <nick> <account> :is logged in as"
|
||||
struct RplWhoisAccount330 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
account: Str,
|
||||
}
|
||||
|
||||
/// "<client> <nick> :is actually ..."
|
||||
/// "<client> <nick> <host|ip> :Is actually using host"
|
||||
/// "<client> <nick> <username>@<hostname> <ip> :Is actually using host"
|
||||
struct RplWhoisActually338 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
username: Str,
|
||||
/// CANNOT start with a colon (':', 0x3A)
|
||||
host: Str,
|
||||
ip: IpAddr,
|
||||
}
|
||||
|
||||
/// "<client> <nick> :is connecting from *@localhost 127.0.0.1"
|
||||
struct RplWhoisHost378 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
host: Str,
|
||||
}
|
||||
|
||||
struct Mode(Str);
|
||||
/// "<client> <nick> :is using modes +ailosw"
|
||||
struct RplWhoisModes379 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
modes: Vec<Mode>,
|
||||
}
|
||||
|
||||
/// "<client> <nick> :is using a secure connection"
|
||||
struct RplWhoisSecure671 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
is_secure: bool,
|
||||
}
|
||||
/// "<client> <nick> :<message>"
|
||||
struct RplAway301 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
default_away_message: Str,
|
||||
}
|
||||
pub struct RplEndOfWhois318 {
|
||||
client: Str,
|
||||
nick: Str,
|
||||
}
|
||||
impl RplEndOfWhois318 {
|
||||
pub fn new(client: Str, nick: Str) -> Self {
|
||||
RPL_ENDOFWHOIS_318 { client, nick }
|
||||
RplEndOfWhois318 { client, nick }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,7 +172,7 @@ impl WriteResponse for RplWhoIsUser311 {
|
|||
}
|
||||
}
|
||||
|
||||
impl WriteResponse for RPL_ENDOFWHOIS_318 {
|
||||
impl WriteResponse for RplEndOfWhois318 {
|
||||
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?;
|
||||
|
|
|
@ -729,12 +729,12 @@ async fn handle_incoming_message(
|
|||
}
|
||||
},
|
||||
ClientMessage::Whois { arg } => {
|
||||
arg.handle(
|
||||
arg.handle(commands::HandlerArgs::new(
|
||||
config.server_name.clone(),
|
||||
user.nickname.clone(),
|
||||
writer,
|
||||
storage.clone(),
|
||||
)
|
||||
))
|
||||
.await?
|
||||
}
|
||||
ClientMessage::Mode { target } => {
|
||||
|
|
|
@ -8,10 +8,10 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
|
|||
use tokio::net::tcp::{ReadHalf, WriteHalf};
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
use lavina_core::{player::PlayerRegistry, room::RoomRegistry};
|
||||
use lavina_core::repo::{Storage, StorageConfig};
|
||||
use projection_irc::{launch, read_irc_message, RunningServer, ServerConfig};
|
||||
use lavina_core::{player::PlayerRegistry, room::RoomRegistry};
|
||||
use projection_irc::APP_VERSION;
|
||||
use projection_irc::{launch, read_irc_message, RunningServer, ServerConfig};
|
||||
|
||||
struct TestScope<'a> {
|
||||
reader: BufReader<ReadHalf<'a>>,
|
||||
|
@ -194,52 +194,6 @@ 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?;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
//! Client-to-Server IRC protocol.
|
||||
pub mod client;
|
||||
mod prelude;
|
||||
pub mod response;
|
||||
pub mod server;
|
||||
#[cfg(test)]
|
||||
mod testkit;
|
||||
pub mod user;
|
||||
pub mod response;
|
||||
|
||||
use crate::prelude::Str;
|
||||
|
||||
|
|
Loading…
Reference in New Issue