forked from lavina/lavina
1
0
Fork 0

use struct as argument in trait

This commit is contained in:
homycdev 2024-04-29 21:20:48 +03:00
parent 67fbcc0c41
commit a9dfe22d8c
7 changed files with 182 additions and 116 deletions

View File

@ -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,
pub struct HandlerArgs<T: AsyncWrite + Unpin> {
server_name: Str,
client: Str,
writer: &mut (impl AsyncWrite + Unpin),
writer: T,
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<()>>;
}

View File

@ -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,
}

View File

@ -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(())
}

View File

@ -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?;

View File

@ -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 } => {

View File

@ -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?;

View File

@ -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;