2023-02-10 21:27:29 +00:00
|
|
|
use tokio::io::AsyncWrite;
|
|
|
|
use tokio::io::AsyncWriteExt;
|
|
|
|
|
2023-02-09 19:26:05 +00:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
/// Server-to-client message.
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub struct ServerMessage {
|
|
|
|
/// Optional tags section, prefixed with `@`
|
|
|
|
pub tags: Vec<Tag>,
|
|
|
|
/// Optional server name, prefixed with `:`.
|
2023-02-10 18:47:58 +00:00
|
|
|
pub sender: Option<ByteVec>,
|
2023-02-09 19:26:05 +00:00
|
|
|
pub body: ServerMessageBody,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ServerMessage {
|
2023-02-10 21:27:29 +00:00
|
|
|
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
2023-02-10 18:47:58 +00:00
|
|
|
match &self.sender {
|
|
|
|
Some(ref sender) => {
|
2023-02-10 21:27:29 +00:00
|
|
|
writer.write_all(b":").await?;
|
|
|
|
writer.write_all(sender.as_slice()).await?;
|
|
|
|
writer.write_all(b" ").await?;
|
2023-02-10 18:47:58 +00:00
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
2023-02-10 21:27:29 +00:00
|
|
|
self.body.write_async(writer).await?;
|
|
|
|
writer.write_all(b"\n").await?;
|
2023-02-09 19:26:05 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn server_message(input: &[u8]) -> IResult<&[u8], ServerMessage> {
|
|
|
|
let (input, command) = server_message_body(input)?;
|
|
|
|
let (input, _) = tag(b"\n")(input)?;
|
|
|
|
|
|
|
|
let message = ServerMessage {
|
|
|
|
tags: vec![],
|
|
|
|
sender: None,
|
|
|
|
body: command,
|
|
|
|
};
|
|
|
|
Ok((input, message))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub enum ServerMessageBody {
|
|
|
|
Notice {
|
|
|
|
first_target: ByteVec,
|
|
|
|
rest_targets: Vec<ByteVec>,
|
|
|
|
text: ByteVec,
|
|
|
|
},
|
|
|
|
Ping {
|
|
|
|
token: ByteVec,
|
|
|
|
},
|
|
|
|
Pong {
|
|
|
|
from: ByteVec,
|
|
|
|
token: ByteVec,
|
|
|
|
},
|
2023-02-10 18:47:58 +00:00
|
|
|
N001Welcome {
|
|
|
|
client: ByteVec,
|
|
|
|
text: ByteVec,
|
|
|
|
},
|
|
|
|
N002YourHost {
|
|
|
|
client: ByteVec,
|
|
|
|
text: ByteVec,
|
|
|
|
},
|
|
|
|
N003Created {
|
|
|
|
client: ByteVec,
|
|
|
|
text: ByteVec,
|
|
|
|
},
|
|
|
|
N004MyInfo {
|
|
|
|
client: ByteVec,
|
|
|
|
hostname: ByteVec,
|
|
|
|
softname: ByteVec,
|
|
|
|
// TODO user modes, channel modes, channel modes with a parameter
|
|
|
|
},
|
|
|
|
N005ISupport {
|
|
|
|
client: ByteVec,
|
|
|
|
params: ByteVec, // TODO make this a datatype
|
|
|
|
},
|
2023-02-09 19:26:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ServerMessageBody {
|
2023-02-10 21:27:29 +00:00
|
|
|
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
|
2023-02-09 19:26:05 +00:00
|
|
|
match self {
|
|
|
|
ServerMessageBody::Notice {
|
|
|
|
first_target,
|
|
|
|
rest_targets,
|
|
|
|
text,
|
|
|
|
} => {
|
2023-02-10 21:27:29 +00:00
|
|
|
writer.write_all(b"NOTICE ").await?;
|
|
|
|
writer.write_all(&first_target).await?;
|
|
|
|
writer.write_all(b" :").await?;
|
|
|
|
writer.write_all(&text).await?;
|
2023-02-09 19:26:05 +00:00
|
|
|
}
|
|
|
|
ServerMessageBody::Ping { token } => {
|
2023-02-10 21:27:29 +00:00
|
|
|
writer.write_all(b"PING ").await?;
|
|
|
|
writer.write_all(&token).await?;
|
2023-02-09 19:26:05 +00:00
|
|
|
}
|
|
|
|
ServerMessageBody::Pong { from, token } => {
|
2023-02-10 21:27:29 +00:00
|
|
|
writer.write_all(b"PONG ").await?;
|
|
|
|
writer.write_all(&from).await?;
|
|
|
|
writer.write_all(b" :").await?;
|
|
|
|
writer.write_all(&token).await?;
|
2023-02-09 19:26:05 +00:00
|
|
|
}
|
2023-02-10 18:47:58 +00:00
|
|
|
ServerMessageBody::N001Welcome { client, text } => {
|
2023-02-10 21:27:29 +00:00
|
|
|
writer.write_all(b"001 ").await?;
|
|
|
|
writer.write_all(&client).await?;
|
|
|
|
writer.write_all(b" :").await?;
|
|
|
|
writer.write_all(text).await?;
|
2023-02-10 18:47:58 +00:00
|
|
|
}
|
|
|
|
ServerMessageBody::N002YourHost { client, text } => {
|
2023-02-10 21:27:29 +00:00
|
|
|
writer.write_all(b"002 ").await?;
|
|
|
|
writer.write_all(&client).await?;
|
|
|
|
writer.write_all(b" :").await?;
|
|
|
|
writer.write_all(text).await?;
|
2023-02-10 18:47:58 +00:00
|
|
|
}
|
|
|
|
ServerMessageBody::N003Created { client, text } => {
|
2023-02-10 21:27:29 +00:00
|
|
|
writer.write_all(b"003 ").await?;
|
|
|
|
writer.write_all(&client).await?;
|
|
|
|
writer.write_all(b" :").await?;
|
|
|
|
writer.write_all(text).await?;
|
2023-02-10 18:47:58 +00:00
|
|
|
}
|
|
|
|
ServerMessageBody::N004MyInfo {
|
|
|
|
client,
|
|
|
|
hostname,
|
|
|
|
softname,
|
|
|
|
} => {
|
2023-02-10 21:27:29 +00:00
|
|
|
writer.write_all(b"004 ").await?;
|
|
|
|
writer.write_all(&client).await?;
|
|
|
|
writer.write_all(b" ").await?;
|
|
|
|
writer.write_all(&hostname).await?;
|
|
|
|
writer.write_all(b" ").await?;
|
|
|
|
writer.write_all(&softname).await?;
|
|
|
|
writer
|
|
|
|
.write_all(b" DGMQRSZagiloswz CFILPQbcefgijklmnopqrstvz bkloveqjfI")
|
|
|
|
.await?;
|
2023-02-10 18:47:58 +00:00
|
|
|
// TODO remove hardcoded modes
|
|
|
|
}
|
|
|
|
ServerMessageBody::N005ISupport { client, params } => {
|
2023-02-10 21:27:29 +00:00
|
|
|
writer.write_all(b"005 ").await?;
|
|
|
|
writer.write_all(&client).await?;
|
|
|
|
writer.write_all(b" ").await?;
|
|
|
|
writer.write_all(¶ms).await?;
|
|
|
|
writer.write_all(b" :are supported by this server").await?;
|
2023-02-10 18:47:58 +00:00
|
|
|
}
|
2023-02-09 19:26:05 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn server_message_body(input: &[u8]) -> IResult<&[u8], ServerMessageBody> {
|
|
|
|
alt((
|
|
|
|
server_message_body_notice,
|
|
|
|
server_message_body_ping,
|
|
|
|
server_message_body_pong,
|
|
|
|
))(input)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn server_message_body_notice(input: &[u8]) -> IResult<&[u8], ServerMessageBody> {
|
|
|
|
let (input, _) = tag("NOTICE ")(input)?;
|
|
|
|
let (input, first_target) = receiver(input)?;
|
|
|
|
let (input, _) = tag(" :")(input)?;
|
|
|
|
let (input, text) = token(input)?;
|
|
|
|
|
|
|
|
let first_target = first_target.to_owned();
|
|
|
|
let text = text.to_owned();
|
|
|
|
Ok((
|
|
|
|
input,
|
|
|
|
ServerMessageBody::Notice {
|
|
|
|
first_target,
|
|
|
|
rest_targets: vec![],
|
|
|
|
text,
|
|
|
|
},
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn server_message_body_ping(input: &[u8]) -> IResult<&[u8], ServerMessageBody> {
|
|
|
|
let (input, _) = tag("PING ")(input)?;
|
|
|
|
let (input, token) = token(input)?;
|
|
|
|
|
|
|
|
Ok((
|
|
|
|
input,
|
|
|
|
ServerMessageBody::Ping {
|
|
|
|
token: token.to_owned(),
|
|
|
|
},
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn server_message_body_pong(input: &[u8]) -> IResult<&[u8], ServerMessageBody> {
|
|
|
|
let (input, _) = tag("PONG ")(input)?;
|
|
|
|
let (input, from) = receiver(input)?;
|
|
|
|
let (input, _) = tag(" :")(input)?;
|
|
|
|
let (input, token) = token(input)?;
|
|
|
|
|
|
|
|
Ok((
|
|
|
|
input,
|
|
|
|
ServerMessageBody::Pong {
|
|
|
|
from: from.to_owned(),
|
|
|
|
token: token.to_owned(),
|
|
|
|
},
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use assert_matches::*;
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_server_message_notice() {
|
|
|
|
let input = b"NOTICE * :*** Looking up your hostname...\n";
|
|
|
|
let expected = ServerMessage {
|
|
|
|
tags: vec![],
|
|
|
|
sender: None,
|
|
|
|
body: ServerMessageBody::Notice {
|
|
|
|
first_target: b"*".to_vec(),
|
|
|
|
rest_targets: vec![],
|
|
|
|
text: b"*** Looking up your hostname...".to_vec(),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let result = server_message(input);
|
|
|
|
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
|
|
|
|
|
|
|
|
let mut bytes = vec![];
|
|
|
|
expected.write(&mut bytes).unwrap();
|
|
|
|
assert_eq!(bytes, input);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_server_message_pong() {
|
|
|
|
let input = b"PONG server.example :LAG004911\n";
|
|
|
|
let expected = ServerMessage {
|
|
|
|
tags: vec![],
|
|
|
|
sender: None,
|
|
|
|
body: ServerMessageBody::Pong {
|
|
|
|
from: b"server.example".to_vec(),
|
|
|
|
token: b"LAG004911".to_vec(),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let result = server_message(input);
|
|
|
|
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
|
|
|
|
|
|
|
|
let mut bytes = vec![];
|
|
|
|
expected.write(&mut bytes).unwrap();
|
|
|
|
assert_eq!(bytes, input);
|
|
|
|
}
|
|
|
|
}
|