forked from lavina/lavina
1
0
Fork 0

WIP: implement WHOIS command

This commit is contained in:
homycdev 2024-04-01 21:48:12 +03:00
parent d436631450
commit 9cf4176a83
3 changed files with 135 additions and 6 deletions

View File

@ -724,6 +724,27 @@ async fn handle_incoming_message(
log::warn!("Local chans not supported"); 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 } => { ClientMessage::Mode { target } => {
match target { match target {
Recipient::Nick(nickname) => { Recipient::Nick(nickname) => {

View File

@ -1,9 +1,10 @@
use super::*;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use nom::combinator::{all_consuming, opt}; use nom::combinator::{all_consuming, opt};
use nom::error::ErrorKind;
use nonempty::NonEmpty; use nonempty::NonEmpty;
use super::*;
/// Client-to-server command. /// Client-to-server command.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum ClientMessage { pub enum ClientMessage {
@ -42,6 +43,11 @@ pub enum ClientMessage {
Who { Who {
target: Recipient, // aka mask target: Recipient, // aka mask
}, },
/// WHOIS [<target>] <nick>
Whois {
target: Option<Str>, // server_name or nick_name
nick: Str,
},
/// `TOPIC <chan> :<topic>` /// `TOPIC <chan> :<topic>`
Topic { Topic {
chan: Chan, chan: Chan,
@ -74,6 +80,7 @@ pub fn client_message(input: &str) -> Result<ClientMessage> {
client_message_join, client_message_join,
client_message_mode, client_message_mode,
client_message_who, client_message_who,
client_message_whois,
client_message_topic, client_message_topic,
client_message_part, client_message_part,
client_message_privmsg, client_message_privmsg,
@ -177,6 +184,32 @@ fn client_message_who(input: &str) -> IResult<&str, ClientMessage> {
Ok((input, ClientMessage::Who { target })) 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> { fn client_message_topic(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("TOPIC ")(input)?; let (input, _) = tag("TOPIC ")(input)?;
let (input, chan) = chan(input)?; let (input, chan) = chan(input)?;
@ -305,6 +338,7 @@ mod test {
use nonempty::nonempty; use nonempty::nonempty;
use super::*; use super::*;
#[test] #[test]
fn test_client_message_cap_ls() { fn test_client_message_cap_ls() {
let input = "CAP LS 302"; let input = "CAP LS 302";
@ -354,6 +388,66 @@ mod test {
assert_matches!(result, Ok(result) => assert_eq!(expected, result)); assert_matches!(result, Ok(result) => assert_eq!(expected, result));
} }
#[test] #[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() { fn test_client_message_user() {
let input = "USER SomeNick 8 * :Real Name"; let input = "USER SomeNick 8 * :Real Name";
let expected = ClientMessage::User { let expected = ClientMessage::User {

View File

@ -1,12 +1,11 @@
use std::sync::Arc;
use nonempty::NonEmpty; use nonempty::NonEmpty;
use tokio::io::AsyncWrite; use tokio::io::AsyncWrite;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use super::*;
use crate::user::PrefixedNick; use crate::user::PrefixedNick;
use super::*;
/// Server-to-client message. /// Server-to-client message.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct ServerMessage { pub struct ServerMessage {
@ -107,6 +106,12 @@ pub enum ServerMessageBody {
/// Usually `b"End of WHO list"` /// Usually `b"End of WHO list"`
msg: Str, msg: Str,
}, },
N318EndOfWhois {
client: Str,
nick: Str,
/// Usually `b"End of /WHOIS list"`
msg: Str,
},
N332Topic { N332Topic {
client: Str, client: Str,
chat: Chan, chat: Chan,
@ -273,6 +278,14 @@ impl ServerMessageBody {
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(msg.as_bytes()).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 } => { ServerMessageBody::N332Topic { client, chat, topic } => {
writer.write_all(b"332 ").await?; writer.write_all(b"332 ").await?;
writer.write_all(client.as_bytes()).await?; writer.write_all(client.as_bytes()).await?;
@ -458,9 +471,10 @@ fn server_message_body_cap(input: &str) -> IResult<&str, ServerMessageBody> {
mod test { mod test {
use assert_matches::*; use assert_matches::*;
use super::*;
use crate::testkit::*; use crate::testkit::*;
use super::*;
#[test] #[test]
fn test_server_message_notice() { fn test_server_message_notice() {
let input = "NOTICE * :*** Looking up your hostname...\r\n"; let input = "NOTICE * :*** Looking up your hostname...\r\n";