From 9cf4176a838b296583e694f2f990d043cf34561d Mon Sep 17 00:00:00 2001 From: homycdev Date: Mon, 1 Apr 2024 21:48:12 +0300 Subject: [PATCH] WIP: implement WHOIS command --- crates/projection-irc/src/lib.rs | 21 +++++++ crates/proto-irc/src/client.rs | 98 +++++++++++++++++++++++++++++++- crates/proto-irc/src/server.rs | 22 +++++-- 3 files changed, 135 insertions(+), 6 deletions(-) diff --git a/crates/projection-irc/src/lib.rs b/crates/projection-irc/src/lib.rs index ee47f9b..a112eb0 100644 --- a/crates/projection-irc/src/lib.rs +++ b/crates/projection-irc/src/lib.rs @@ -724,6 +724,27 @@ 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 { + Some(target) => {} + None => {} + } + + ServerMessage { + tags: vec![], + sender: Some(config.server_name.clone()), + body: ServerMessageBody::N318EndOfWhois { + client: user.nickname.clone(), + nick, + msg: "End of /WHOIS list".into(), + }, + } + .write_async(writer) + .await?; + writer.flush().await? + } ClientMessage::Mode { target } => { match target { Recipient::Nick(nickname) => { diff --git a/crates/proto-irc/src/client.rs b/crates/proto-irc/src/client.rs index 66cf107..15096d7 100644 --- a/crates/proto-irc/src/client.rs +++ b/crates/proto-irc/src/client.rs @@ -1,9 +1,10 @@ -use super::*; - use anyhow::{anyhow, Result}; use nom::combinator::{all_consuming, opt}; +use nom::error::ErrorKind; use nonempty::NonEmpty; +use super::*; + /// Client-to-server command. #[derive(Clone, Debug, PartialEq, Eq)] pub enum ClientMessage { @@ -42,6 +43,11 @@ pub enum ClientMessage { Who { target: Recipient, // aka mask }, + /// WHOIS [] + Whois { + target: Option, // server_name or nick_name + nick: Str, + }, /// `TOPIC :` Topic { chan: Chan, @@ -74,6 +80,7 @@ pub fn client_message(input: &str) -> Result { client_message_join, client_message_mode, client_message_who, + client_message_whois, client_message_topic, client_message_part, client_message_privmsg, @@ -177,6 +184,32 @@ fn client_message_who(input: &str) -> IResult<&str, ClientMessage> { Ok((input, ClientMessage::Who { target })) } +fn client_message_whois(input: &str) -> IResult<&str, ClientMessage> { + let (input, _) = tag("WHOIS ")(input)?; + let args: Vec<_> = input.split_whitespace().collect(); + match args.as_slice()[..] { + [nick] => Ok(( + "", + ClientMessage::Whois { + target: None, + nick: nick.into(), + }, + )), + [target, nick, ..] => Ok(( + "", + ClientMessage::Whois { + target: Some(target.into()), + nick: nick.into(), + }, + )), + // fixme: idk how to deal with this in more elegant way + [] => Err(nom::Err::Failure(nom::error::Error { + input: "No args passed", + code: ErrorKind::Fail, + })), + } +} + fn client_message_topic(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("TOPIC ")(input)?; let (input, chan) = chan(input)?; @@ -305,6 +338,7 @@ mod test { use nonempty::nonempty; use super::*; + #[test] fn test_client_message_cap_ls() { let input = "CAP LS 302"; @@ -354,6 +388,66 @@ mod test { assert_matches!(result, Ok(result) => assert_eq!(expected, result)); } #[test] + fn test_client_message_whois() { + let test_user = "WHOIS val"; + let test_user_user = "WHOIS val val"; + let test_server_user = "WHOIS com.test.server user"; + let test_user_server = "WHOIS user com.test.server"; + let test_users_list = "WHOIS user_1,user_2,user_3"; + let test_server_users_list = "WHOIS com.test.server user_1,user_2,user_3"; + let test_more_than_two_params = "WHOIS test.server user_1,user_2,user_3 whatever spam"; + + let res_one_arg = client_message(test_user); + let res_user_user = client_message(test_user_user); + + let res_server_user = client_message(test_server_user); + let res_user_server = client_message(test_user_server); + let res_users_list = client_message(test_users_list); + let res_server_users_list = client_message(test_server_users_list); + let res_more_than_two_params = client_message(test_more_than_two_params); + + let expected_arg = ClientMessage::Whois { + target: None, + nick: "val".into(), + }; + let expected_user_user = ClientMessage::Whois { + target: Some("val".into()), + nick: "val".into(), + }; + + let expected_server_user = ClientMessage::Whois { + target: Some("com.test.server".into()), + nick: "user".into(), + }; + + let expected_user_server = ClientMessage::Whois { + target: Some("user".into()), + nick: "com.test.server".into(), + }; + + let expected_user_list = ClientMessage::Whois { + target: None, + nick: "user_1,user_2,user_3".into(), + }; + let expected_server_user_list = ClientMessage::Whois { + target: Some("com.test.server".into()), + nick: "user_1,user_2,user_3".into(), + }; + + let expected_more_than_two_params = ClientMessage::Whois { + target: Some("test.server".into()), + nick: "user_1,user_2,user_3".into(), + }; + + assert_matches!(res_one_arg, Ok(result) => assert_eq!(expected_arg, result)); + assert_matches!(res_user_user, Ok(result) => assert_eq!(expected_user_user, result)); + assert_matches!(res_server_user, Ok(result) => assert_eq!(expected_server_user, result)); + assert_matches!(res_user_server, Ok(result) => assert_eq!(expected_user_server, result)); + assert_matches!(res_users_list, Ok(result) => assert_eq!(expected_user_list, result)); + assert_matches!(res_server_users_list, Ok(result) => assert_eq!(expected_server_user_list, result)); + assert_matches!(res_more_than_two_params, Ok(result) => assert_eq!(expected_more_than_two_params, result)) + } + #[test] fn test_client_message_user() { let input = "USER SomeNick 8 * :Real Name"; let expected = ClientMessage::User { diff --git a/crates/proto-irc/src/server.rs b/crates/proto-irc/src/server.rs index 6e1bd66..2a01957 100644 --- a/crates/proto-irc/src/server.rs +++ b/crates/proto-irc/src/server.rs @@ -1,12 +1,11 @@ -use std::sync::Arc; - use nonempty::NonEmpty; use tokio::io::AsyncWrite; use tokio::io::AsyncWriteExt; -use super::*; use crate::user::PrefixedNick; +use super::*; + /// Server-to-client message. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ServerMessage { @@ -107,6 +106,12 @@ pub enum ServerMessageBody { /// Usually `b"End of WHO list"` msg: Str, }, + N318EndOfWhois { + client: Str, + nick: Str, + /// Usually `b"End of /WHOIS list"` + msg: Str, + }, N332Topic { client: Str, chat: Chan, @@ -273,6 +278,14 @@ impl ServerMessageBody { writer.write_all(b" :").await?; writer.write_all(msg.as_bytes()).await?; } + ServerMessageBody::N318EndOfWhois { client, nick, msg } => { + writer.write_all(b"b318 ").await?; + writer.write_all(client.as_bytes()).await?; + writer.write_all(b" ").await?; + writer.write_all(nick.as_bytes()).await?; + writer.write_all(b" :").await?; + writer.write_all(msg.as_bytes()).await?; + } ServerMessageBody::N332Topic { client, chat, topic } => { writer.write_all(b"332 ").await?; writer.write_all(client.as_bytes()).await?; @@ -458,9 +471,10 @@ fn server_message_body_cap(input: &str) -> IResult<&str, ServerMessageBody> { mod test { use assert_matches::*; - use super::*; use crate::testkit::*; + use super::*; + #[test] fn test_server_message_notice() { let input = "NOTICE * :*** Looking up your hostname...\r\n";