From 1f238680e8310cdfb3acfdc3c3b68ac6a896fc58 Mon Sep 17 00:00:00 2001 From: homycdev Date: Sun, 31 Mar 2024 15:11:33 +0300 Subject: [PATCH] added WHOIS command to irc client --- crates/projection-irc/src/lib.rs | 8 +++ crates/proto-irc/src/client.rs | 95 ++++++++++++++++++++++++++++++++ crates/proto-irc/src/server.rs | 50 ++++++++++------- 3 files changed, 132 insertions(+), 21 deletions(-) diff --git a/crates/projection-irc/src/lib.rs b/crates/projection-irc/src/lib.rs index 27b1d69..f64b35c 100644 --- a/crates/projection-irc/src/lib.rs +++ b/crates/projection-irc/src/lib.rs @@ -724,6 +724,14 @@ 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 => {} + } + } + 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 676fd40..9b4001a 100644 --- a/crates/proto-irc/src/client.rs +++ b/crates/proto-irc/src/client.rs @@ -1,7 +1,10 @@ use super::*; use anyhow::{anyhow, Result}; +use nom::bytes::complete::take_until; +use nom::character::complete::space0; use nom::combinator::{all_consuming, opt}; +use nom::error::ErrorKind; use nonempty::NonEmpty; /// Client-to-server command. @@ -42,6 +45,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 +82,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 +186,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)?; @@ -354,6 +389,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 bc7844d..91e8df8 100644 --- a/crates/proto-irc/src/server.rs +++ b/crates/proto-irc/src/server.rs @@ -1,3 +1,4 @@ +use std::ptr::write; use std::sync::Arc; use nonempty::NonEmpty; @@ -107,6 +108,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, @@ -158,7 +165,7 @@ pub enum ServerMessageBody { N904SaslFail { nick: Str, text: Str, - } + }, } impl ServerMessageBody { @@ -273,11 +280,17 @@ impl ServerMessageBody { writer.write_all(b" :").await?; writer.write_all(msg.as_bytes()).await?; } - ServerMessageBody::N332Topic { - client, - chat, - topic, - } => { + + 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?; writer.write_all(b" ").await?; @@ -315,20 +328,14 @@ impl ServerMessageBody { writer.write_all(b" ").await?; writer.write_all(realname.as_bytes()).await?; } - ServerMessageBody::N353NamesReply { - client, - chan, - members, - } => { + ServerMessageBody::N353NamesReply { client, chan, members } => { writer.write_all(b"353 ").await?; writer.write_all(client.as_bytes()).await?; writer.write_all(b" = ").await?; chan.write_async(writer).await?; writer.write_all(b" :").await?; for member in members { - writer - .write_all(member.prefix.to_string().as_bytes()) - .await?; + writer.write_all(member.prefix.to_string().as_bytes()).await?; writer.write_all(member.nick.as_bytes()).await?; writer.write_all(b" ").await?; } @@ -340,11 +347,7 @@ impl ServerMessageBody { chan.write_async(writer).await?; writer.write_all(b" :End of /NAMES list").await?; } - ServerMessageBody::N474BannedFromChan { - client, - chan, - message, - } => { + ServerMessageBody::N474BannedFromChan { client, chan, message } => { writer.write_all(b"474 ").await?; writer.write_all(client.as_bytes()).await?; writer.write_all(b" ").await?; @@ -359,7 +362,12 @@ impl ServerMessageBody { writer.write_all(b" :").await?; writer.write_all(message.as_bytes()).await?; } - ServerMessageBody::N900LoggedIn { nick, address, account, message } => { + ServerMessageBody::N900LoggedIn { + nick, + address, + account, + message, + } => { writer.write_all(b"900 ").await?; writer.write_all(nick.as_bytes()).await?; writer.write_all(b" ").await?; @@ -404,7 +412,7 @@ fn server_message_body(input: &str) -> IResult<&str, ServerMessageBody> { server_message_body_notice, server_message_body_ping, server_message_body_pong, - server_message_body_cap + server_message_body_cap, ))(input) }