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::{prelude::Str, repo::Storage};
|
||||||
use lavina_core::repo::Storage;
|
|
||||||
use proto_irc::response::WriteResponse;
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use tokio::io::AsyncWrite;
|
use tokio::io::AsyncWrite;
|
||||||
|
|
||||||
pub mod whois;
|
pub mod whois;
|
||||||
|
|
||||||
pub trait Handler {
|
pub struct HandlerArgs<T: AsyncWrite + Unpin> {
|
||||||
fn handle(
|
server_name: Str,
|
||||||
&self,
|
client: Str,
|
||||||
server_name: Str,
|
writer: T,
|
||||||
client: Str,
|
storage: Storage,
|
||||||
writer: &mut (impl AsyncWrite + Unpin),
|
}
|
||||||
storage: Storage,
|
|
||||||
) -> impl Future<Output = anyhow::Result<()>>;
|
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 lavina_core::prelude::Str;
|
||||||
use proto_irc::response::WriteResponse;
|
use proto_irc::response::WriteResponse;
|
||||||
|
|
||||||
/// ERR_NOSUCHNICK (401)
|
/// ErrNoSuchNick401
|
||||||
pub struct ErrNoSuchNick401 {
|
pub struct ErrNoSuchNick401 {
|
||||||
client: Str,
|
client: Str,
|
||||||
nick: Str,
|
nick: Str,
|
||||||
|
@ -15,7 +15,7 @@ impl ErrNoSuchNick401 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ERR_NOSUCHSERVER (402)
|
/// ErrNoSuchServer402
|
||||||
struct ErrNoSuchServer402 {
|
struct ErrNoSuchServer402 {
|
||||||
client: Str,
|
client: Str,
|
||||||
/// target parameter in WHOIS
|
/// target parameter in WHOIS
|
||||||
|
@ -23,7 +23,7 @@ struct ErrNoSuchServer402 {
|
||||||
server_name: Str,
|
server_name: Str,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ERR_NONICKNAMEGIVEN (431)
|
/// ErrNoNicknameGiven431
|
||||||
pub struct ErrNoNicknameGiven431 {
|
pub struct ErrNoNicknameGiven431 {
|
||||||
client: Str,
|
client: Str,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,34 @@
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
use tokio::io::AsyncWrite;
|
||||||
use tracing::instrument::WithSubscriber;
|
|
||||||
|
|
||||||
use lavina_core::prelude::Str;
|
use lavina_core::prelude::Str;
|
||||||
use lavina_core::repo::Storage;
|
|
||||||
use proto_irc::client::command_args::Whois;
|
use proto_irc::client::command_args::Whois;
|
||||||
use proto_irc::response::{IrcResponseMessage, WriteResponse};
|
use proto_irc::response::{IrcResponseMessage, WriteResponse};
|
||||||
|
|
||||||
use crate::commands::whois::error::{ErrNoNicknameGiven431, ErrNoSuchNick401};
|
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 crate::commands::Handler;
|
||||||
|
|
||||||
|
use super::HandlerArgs;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
|
|
||||||
impl Handler for Whois {
|
impl<T: AsyncWrite + Unpin> Handler<T> for Whois {
|
||||||
async fn handle(
|
async fn handle(&self, body: HandlerArgs<T>) -> anyhow::Result<()> {
|
||||||
&self,
|
|
||||||
server_name: Str,
|
|
||||||
client: Str,
|
|
||||||
writer: &mut (impl AsyncWrite + Unpin),
|
|
||||||
mut storage: Storage,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
match self {
|
match self {
|
||||||
Whois::Nick(nick) => {
|
Whois::Nick(nick) => handle_nick_target(nick.clone(), body).await?,
|
||||||
handle_nick_target(nick.clone(), None, server_name, client, writer, storage.clone()).await?
|
Whois::TargetNick(_, nick) => handle_nick_target(nick.clone(), body).await?,
|
||||||
}
|
|
||||||
Whois::TargetNick(target, nick) => {
|
|
||||||
handle_nick_target(nick.clone(), Some(target.clone()), server_name, client, writer, storage).await?
|
|
||||||
}
|
|
||||||
Whois::EmptyArgs => {
|
Whois::EmptyArgs => {
|
||||||
|
let HandlerArgs {
|
||||||
|
server_name,
|
||||||
|
mut writer,
|
||||||
|
..
|
||||||
|
} = body;
|
||||||
IrcResponseMessage::empty_tags(
|
IrcResponseMessage::empty_tags(
|
||||||
Some(server_name.clone()),
|
Some(server_name.clone()),
|
||||||
ErrNoNicknameGiven431::new(server_name.clone()),
|
ErrNoNicknameGiven431::new(server_name.clone()),
|
||||||
)
|
)
|
||||||
.write_response(writer)
|
.write_response(&mut writer)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,15 +36,13 @@ impl Handler for Whois {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_nick_target(
|
async fn handle_nick_target(nick: Str, body: HandlerArgs<impl AsyncWrite + Unpin>) -> anyhow::Result<()> {
|
||||||
nick: Str,
|
let HandlerArgs {
|
||||||
// todo: implement logic with target
|
server_name,
|
||||||
_target: Option<Str>,
|
mut writer,
|
||||||
server_name: Str,
|
client,
|
||||||
client: Str,
|
mut storage,
|
||||||
writer: &mut (impl AsyncWrite + Unpin),
|
} = body;
|
||||||
mut storage: Storage,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
if let Some(user) = storage.retrieve_user_by_name(nick.clone().as_ref()).await? {
|
if let Some(user) = storage.retrieve_user_by_name(nick.clone().as_ref()).await? {
|
||||||
IrcResponseMessage::empty_tags(
|
IrcResponseMessage::empty_tags(
|
||||||
Some(server_name.clone()),
|
Some(server_name.clone()),
|
||||||
|
@ -58,19 +51,20 @@ async fn handle_nick_target(
|
||||||
nick.clone(),
|
nick.clone(),
|
||||||
Some(Str::from(user.name.clone())),
|
Some(Str::from(user.name.clone())),
|
||||||
server_name.clone(),
|
server_name.clone(),
|
||||||
|
nick.clone(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.write_response(writer)
|
.write_response(&mut writer)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
IrcResponseMessage::empty_tags(
|
IrcResponseMessage::empty_tags(
|
||||||
Some(server_name.clone()),
|
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?
|
.await?
|
||||||
} else {
|
} else {
|
||||||
ErrNoSuchNick401::new(client.clone(), nick.clone()).write_response(writer).await?
|
ErrNoSuchNick401::new(client.clone(), nick.clone()).write_response(&mut writer).await?
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,25 @@
|
||||||
|
use std::{net::IpAddr, time::SystemTime};
|
||||||
|
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||||
|
|
||||||
use lavina_core::prelude::Str;
|
use lavina_core::prelude::Str;
|
||||||
use proto_irc::response::WriteResponse;
|
use proto_irc::{response::WriteResponse, Chan};
|
||||||
|
|
||||||
struct RplWhoisCertfp276;
|
/// "<client> <nick> :has client certificate fingerprint <fingerprint>"
|
||||||
struct RplWhoisRegNick307;
|
/// 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 {
|
pub struct RplWhoIsUser311 {
|
||||||
client: Str,
|
client: Str,
|
||||||
/// unique name
|
/// unique name
|
||||||
|
@ -13,37 +28,124 @@ pub struct RplWhoIsUser311 {
|
||||||
username: Option<Str>,
|
username: Option<Str>,
|
||||||
/// server name
|
/// server name
|
||||||
host: Str,
|
host: Str,
|
||||||
|
realname: Str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RplWhoIsUser311 {
|
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 {
|
RplWhoIsUser311 {
|
||||||
client,
|
client,
|
||||||
nick,
|
nick,
|
||||||
username,
|
username,
|
||||||
host,
|
host,
|
||||||
|
realname,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// "<client> <nick> <server> :<server info>"
|
||||||
|
struct RplWhoisServer312 {
|
||||||
|
client: Str,
|
||||||
|
nick: Str,
|
||||||
|
server: Str,
|
||||||
|
/// description of the server
|
||||||
|
server_info: String,
|
||||||
|
}
|
||||||
|
|
||||||
struct _RplWhoisServer312;
|
/// "<client> <nick> :is an IRC operator"
|
||||||
struct _RplWhoisOperator313;
|
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,
|
client: Str,
|
||||||
nick: 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 {
|
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<()> {
|
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?;
|
||||||
|
|
|
@ -729,12 +729,12 @@ async fn handle_incoming_message(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ClientMessage::Whois { arg } => {
|
ClientMessage::Whois { arg } => {
|
||||||
arg.handle(
|
arg.handle(commands::HandlerArgs::new(
|
||||||
config.server_name.clone(),
|
config.server_name.clone(),
|
||||||
user.nickname.clone(),
|
user.nickname.clone(),
|
||||||
writer,
|
writer,
|
||||||
storage.clone(),
|
storage.clone(),
|
||||||
)
|
))
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
ClientMessage::Mode { target } => {
|
ClientMessage::Mode { target } => {
|
||||||
|
|
|
@ -8,10 +8,10 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
|
||||||
use tokio::net::tcp::{ReadHalf, WriteHalf};
|
use tokio::net::tcp::{ReadHalf, WriteHalf};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
use lavina_core::{player::PlayerRegistry, room::RoomRegistry};
|
|
||||||
use lavina_core::repo::{Storage, StorageConfig};
|
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::APP_VERSION;
|
||||||
|
use projection_irc::{launch, read_irc_message, RunningServer, ServerConfig};
|
||||||
|
|
||||||
struct TestScope<'a> {
|
struct TestScope<'a> {
|
||||||
reader: BufReader<ReadHalf<'a>>,
|
reader: BufReader<ReadHalf<'a>>,
|
||||||
|
@ -194,52 +194,6 @@ async fn scenario_cap_full_negotiation() -> Result<()> {
|
||||||
Ok(())
|
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]
|
#[tokio::test]
|
||||||
async fn scenario_cap_short_negotiation() -> Result<()> {
|
async fn scenario_cap_short_negotiation() -> Result<()> {
|
||||||
let mut server = TestServer::start().await?;
|
let mut server = TestServer::start().await?;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
//! Client-to-Server IRC protocol.
|
//! Client-to-Server IRC protocol.
|
||||||
pub mod client;
|
pub mod client;
|
||||||
mod prelude;
|
mod prelude;
|
||||||
|
pub mod response;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod testkit;
|
mod testkit;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod response;
|
|
||||||
|
|
||||||
use crate::prelude::Str;
|
use crate::prelude::Str;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue