rewrite irc projection to use str

This commit is contained in:
Nikita Vilunov 2023-04-13 21:15:48 +02:00
parent d0c579863e
commit 55b69f4c8a
10 changed files with 228 additions and 229 deletions

View File

@ -27,20 +27,23 @@ use crate::{
/// Opaque player identifier. Cannot contain spaces, must be shorter than 32. /// Opaque player identifier. Cannot contain spaces, must be shorter than 32.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct PlayerId(ByteVec); pub struct PlayerId(Box<str>);
impl PlayerId { impl PlayerId {
pub fn from_bytes(bytes: ByteVec) -> Result<PlayerId> { pub fn from_bytes(bytes: Box<str>) -> Result<PlayerId> {
if bytes.len() > 32 { if bytes.len() > 32 {
return Err(fail("Nickname cannot be longer than 32 symbols")); return Err(fail("Nickname cannot be longer than 32 symbols"));
} }
if bytes.contains(&b' ') { if bytes.contains(' ') {
return Err(anyhow::Error::msg("Nickname cannot contain spaces")); return Err(anyhow::Error::msg("Nickname cannot contain spaces"));
} }
Ok(PlayerId(bytes)) Ok(PlayerId(bytes))
} }
pub fn as_bytes(&self) -> &ByteVec { pub fn as_bytes(&self) -> &Box<str> {
&self.0 &self.0
} }
pub fn into_bytes(self) -> Box<str> {
self.0
}
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -52,7 +55,7 @@ pub struct PlayerConnection {
player_handle: PlayerHandle, player_handle: PlayerHandle,
} }
impl PlayerConnection { impl PlayerConnection {
pub async fn send_message(&mut self, room_id: RoomId, body: String) -> Result<()> { pub async fn send_message(&mut self, room_id: RoomId, body: Box<str>) -> Result<()> {
self.player_handle self.player_handle
.send_message(room_id, self.connection_id.clone(), body) .send_message(room_id, self.connection_id.clone(), body)
.await .await
@ -126,7 +129,7 @@ impl PlayerHandle {
&self, &self,
room_id: RoomId, room_id: RoomId,
connection_id: ConnectionId, connection_id: ConnectionId,
body: String, body: Box<str>,
) -> Result<()> { ) -> Result<()> {
let (promise, deferred) = oneshot(); let (promise, deferred) = oneshot();
let cmd = Cmd::SendMessage { let cmd = Cmd::SendMessage {
@ -183,7 +186,7 @@ pub enum Cmd {
}, },
SendMessage { SendMessage {
room_id: RoomId, room_id: RoomId,
body: String, body: Box<str>,
promise: Promise<()>, promise: Promise<()>,
}, },
ChangeTopic { ChangeTopic {
@ -208,7 +211,7 @@ pub enum Updates {
NewMessage { NewMessage {
room_id: RoomId, room_id: RoomId,
author_id: PlayerId, author_id: PlayerId,
body: String, body: Box<str>,
}, },
RoomJoined { RoomJoined {
room_id: RoomId, room_id: RoomId,
@ -285,7 +288,7 @@ impl Player {
player_id, player_id,
connections: AnonTable::new(), connections: AnonTable::new(),
my_rooms: HashMap::new(), my_rooms: HashMap::new(),
banned_from: HashSet::from([RoomId::from_bytes(b"empty".to_vec()).unwrap()]), banned_from: HashSet::from([RoomId::from_bytes("empty".into()).unwrap()]),
rx, rx,
handle, handle,
rooms, rooms,

View File

@ -24,7 +24,7 @@ impl RoomId {
"Room name cannot be longer than 32 symbols", "Room name cannot be longer than 32 symbols",
)); ));
} }
if bytes.contains(&b' ') { if bytes.contains(' ') {
return Err(anyhow::Error::msg("Room name cannot contain spaces")); return Err(anyhow::Error::msg("Room name cannot contain spaces"));
} }
Ok(RoomId(bytes)) Ok(RoomId(bytes))
@ -60,7 +60,7 @@ impl RoomRegistry {
let room = Room { let room = Room {
room_id: room_id.clone(), room_id: room_id.clone(),
subscriptions: HashMap::new(), subscriptions: HashMap::new(),
topic: b"New room".to_vec(), topic: "New room".into(),
}; };
let room_handle = RoomHandle(Arc::new(AsyncRwLock::new(room))); let room_handle = RoomHandle(Arc::new(AsyncRwLock::new(room)));
inner.rooms.insert(room_id, room_handle.clone()); inner.rooms.insert(room_id, room_handle.clone());
@ -112,7 +112,7 @@ impl RoomHandle {
lock.broadcast_update(update, player_id).await; lock.broadcast_update(update, player_id).await;
} }
pub async fn send_message(&self, player_id: PlayerId, body: String) { pub async fn send_message(&self, player_id: PlayerId, body: Box<str>) {
let lock = self.0.read().await; let lock = self.0.read().await;
lock.send_message(player_id, body).await; lock.send_message(player_id, body).await;
} }
@ -157,7 +157,7 @@ impl Room {
self.broadcast_update(update, &player_id).await; self.broadcast_update(update, &player_id).await;
} }
async fn send_message(&self, author_id: PlayerId, body: String) { async fn send_message(&self, author_id: PlayerId, body: Box<str>) {
tracing::info!("Adding a message to room"); tracing::info!("Adding a message to room");
let update = Updates::NewMessage { let update = Updates::NewMessage {
room_id: self.room_id.clone(), room_id: self.room_id.clone(),

View File

@ -11,7 +11,7 @@ pub mod log {
pub type Result<T> = std::result::Result<T, anyhow::Error>; pub type Result<T> = std::result::Result<T, anyhow::Error>;
pub type ByteVec = Vec<u8>; pub type ByteVec = Box<str>;
pub fn fail(msg: &str) -> anyhow::Error { pub fn fail(msg: &str) -> anyhow::Error {
anyhow::Error::msg(msg.to_owned()) anyhow::Error::msg(msg.to_owned())

View File

@ -23,19 +23,19 @@ mod test;
#[derive(Deserialize, Debug, Clone)] #[derive(Deserialize, Debug, Clone)]
pub struct ServerConfig { pub struct ServerConfig {
pub listen_on: SocketAddr, pub listen_on: SocketAddr,
pub server_name: String, pub server_name: Box<str>,
} }
#[derive(Debug)] #[derive(Debug)]
struct RegisteredUser { struct RegisteredUser {
nickname: Vec<u8>, nickname: Box<str>,
/** /**
* Username is mostly unused in modern IRC. * Username is mostly unused in modern IRC.
* *
* [https://stackoverflow.com/questions/31666247/what-is-the-difference-between-the-nick-username-and-real-name-in-irc-and-wha] * [https://stackoverflow.com/questions/31666247/what-is-the-difference-between-the-nick-username-and-real-name-in-irc-and-wha]
*/ */
username: Vec<u8>, username: Box<str>,
realname: Vec<u8>, realname: Box<str>,
} }
async fn handle_socket( async fn handle_socket(
@ -52,11 +52,11 @@ async fn handle_socket(
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone().into()),
body: ServerMessageBody::Notice { body: ServerMessageBody::Notice {
first_target: b"*".to_vec(), first_target: "*".into(),
rest_targets: vec![], rest_targets: vec![],
text: b"Welcome to my server!".to_vec(), text: "Welcome to my server!".into(),
}, },
} }
.write_async(&mut writer) .write_async(&mut writer)
@ -83,24 +83,28 @@ async fn handle_registration<'a>(
) -> Result<RegisteredUser> { ) -> Result<RegisteredUser> {
let mut buffer = vec![]; let mut buffer = vec![];
let mut future_nickname: Option<Vec<u8>> = None; let mut future_nickname: Option<Box<str>> = None;
let mut future_username: Option<(Vec<u8>, Vec<u8>)> = None; let mut future_username: Option<(Box<str>, Box<str>)> = None;
loop { loop {
let res = reader.read_until(b'\n', &mut buffer).await; let res = reader.read_until(b'\n', &mut buffer).await;
match res { let res = match res {
Ok(len) => { Ok(len) => {
if len == 0 { if len == 0 {
log::info!("Terminating socket"); log::info!("Terminating socket");
break Err(anyhow::Error::msg("EOF")); break Err(anyhow::Error::msg("EOF"));
} }
match std::str::from_utf8(&buffer[..len]) {
Ok(res) => res,
Err(e) => break Err(e.into()),
}
} }
Err(err) => { Err(err) => {
log::warn!("Failed to read from socket: {err}"); log::warn!("Failed to read from socket: {err}");
break Err(err.into()); break Err(err.into());
} }
} };
let parsed = client_message(&buffer[..]); let parsed = client_message(res);
match parsed { match parsed {
Ok((_, msg)) => { Ok((_, msg)) => {
log::info!("Incoming IRC message: {msg:?}"); log::info!("Incoming IRC message: {msg:?}");
@ -154,51 +158,51 @@ async fn handle_registered_socket<'a>(
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N001Welcome { body: ServerMessageBody::N001Welcome {
client: user.nickname.clone(), client: user.nickname.clone(),
text: b"Welcome to Kek Server".to_vec(), text: "Welcome to Kek Server".into(),
}, },
} }
.write_async(writer) .write_async(writer)
.await?; .await?;
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N002YourHost { body: ServerMessageBody::N002YourHost {
client: user.nickname.clone(), client: user.nickname.clone(),
text: b"Welcome to Kek Server".to_vec(), text: "Welcome to Kek Server".into(),
}, },
} }
.write_async(writer) .write_async(writer)
.await?; .await?;
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N003Created { body: ServerMessageBody::N003Created {
client: user.nickname.clone(), client: user.nickname.clone(),
text: b"Welcome to Kek Server".to_vec(), text: "Welcome to Kek Server".into(),
}, },
} }
.write_async(writer) .write_async(writer)
.await?; .await?;
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N004MyInfo { body: ServerMessageBody::N004MyInfo {
client: user.nickname.clone(), client: user.nickname.clone(),
hostname: config.server_name.as_bytes().to_vec(), hostname: config.server_name.clone(),
softname: b"kek-0.1.alpha.3".to_vec(), softname: "kek-0.1.alpha.3".into(),
}, },
} }
.write_async(writer) .write_async(writer)
.await?; .await?;
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N005ISupport { body: ServerMessageBody::N005ISupport {
client: user.nickname.clone(), client: user.nickname.clone(),
params: b"CHANTYPES=#".to_vec(), params: "CHANTYPES=#".into(),
}, },
} }
.write_async(writer) .write_async(writer)
@ -229,7 +233,8 @@ async fn handle_registered_socket<'a>(
} else { } else {
len len
}; };
if let HandleResult::Leave = handle_incoming_message(&buffer[0..len], &config, &user, &rooms, &mut connection, writer).await? { let incoming = std::str::from_utf8(&buffer[0..len])?;
if let HandleResult::Leave = handle_incoming_message(incoming, &config, &user, &rooms, &mut connection, writer).await? {
break; break;
} }
buffer.clear(); buffer.clear();
@ -246,9 +251,9 @@ async fn handle_registered_socket<'a>(
} }
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::Error { body: ServerMessageBody::Error {
reason: b"Leaving the server".to_vec(), reason: "Leaving the server".into(),
}, },
} }
.write_async(writer) .write_async(writer)
@ -315,7 +320,7 @@ async fn handle_update(
sender: Some(author_id.as_bytes().clone()), sender: Some(author_id.as_bytes().clone()),
body: ServerMessageBody::PrivateMessage { body: ServerMessageBody::PrivateMessage {
target: Recipient::Chan(Chan::Global(room_id.as_bytes().clone())), target: Recipient::Chan(Chan::Global(room_id.as_bytes().clone())),
body: body.as_bytes().to_vec(), body: body.clone(),
}, },
} }
.write_async(writer) .write_async(writer)
@ -325,7 +330,7 @@ async fn handle_update(
Updates::RoomTopicChanged { room_id, new_topic } => { Updates::RoomTopicChanged { room_id, new_topic } => {
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N332Topic { body: ServerMessageBody::N332Topic {
client: user.nickname.clone(), client: user.nickname.clone(),
chat: Chan::Global(room_id.as_bytes().clone()), chat: Chan::Global(room_id.as_bytes().clone()),
@ -357,7 +362,7 @@ enum HandleResult {
} }
async fn handle_incoming_message( async fn handle_incoming_message(
buffer: &[u8], buffer: &str,
config: &ServerConfig, config: &ServerConfig,
user: &RegisteredUser, user: &RegisteredUser,
rooms: &RoomRegistry, rooms: &RoomRegistry,
@ -372,7 +377,7 @@ async fn handle_incoming_message(
tags: vec![], tags: vec![],
sender: None, sender: None,
body: ServerMessageBody::Pong { body: ServerMessageBody::Pong {
from: config.server_name.as_bytes().to_vec(), from: config.server_name.clone(),
token, token,
}, },
} }
@ -387,12 +392,9 @@ async fn handle_incoming_message(
handle_part(config, user, user_handle, &chan, writer).await?; handle_part(config, user, user_handle, &chan, writer).await?;
} }
ClientMessage::PrivateMessage { recipient, body } => match recipient { ClientMessage::PrivateMessage { recipient, body } => match recipient {
Recipient::Chan(Chan::Global(chan)) => match String::from_utf8(body) { Recipient::Chan(Chan::Global(chan)) => {
Ok(body) => {
let room_id = RoomId::from_bytes(chan)?; let room_id = RoomId::from_bytes(chan)?;
user_handle.send_message(room_id, body.clone()).await?; user_handle.send_message(room_id, body).await?;
}
Err(err) => log::warn!("failed to parse incoming message: {err}"),
}, },
_ => log::warn!("Unsupported target type"), _ => log::warn!("Unsupported target type"),
}, },
@ -405,7 +407,7 @@ async fn handle_incoming_message(
.await?; .await?;
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N332Topic { body: ServerMessageBody::N332Topic {
client: user.nickname.clone(), client: user.nickname.clone(),
chat: Chan::Global(room_id.as_bytes().clone()), chat: Chan::Global(room_id.as_bytes().clone()),
@ -422,25 +424,22 @@ async fn handle_incoming_message(
ClientMessage::Who { target } => match &target { ClientMessage::Who { target } => match &target {
Recipient::Nick(nick) => { Recipient::Nick(nick) => {
// TODO handle non-existing user // TODO handle non-existing user
let mut username = Vec::with_capacity(nick.len() + 1); let mut username = format!("~{nick}");
username.push(b'~'); let mut host = format!("user/{nick}");
username.extend_from_slice(nick.as_slice());
let mut host = b"user/".to_vec();
host.extend_from_slice(nick.as_slice());
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: user_to_who_msg(config, user, nick), body: user_to_who_msg(config, user, nick),
} }
.write_async(writer) .write_async(writer)
.await?; .await?;
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N315EndOfWho { body: ServerMessageBody::N315EndOfWho {
client: user.nickname.clone(), client: user.nickname.clone(),
mask: target.clone(), mask: target.clone(),
msg: b"End of WHO list".to_vec(), msg: "End of WHO list".into(),
}, },
} }
.write_async(writer) .write_async(writer)
@ -454,7 +453,7 @@ async fn handle_incoming_message(
for member in room_info.members { for member in room_info.members {
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: user_to_who_msg(config, user, member.as_bytes()), body: user_to_who_msg(config, user, member.as_bytes()),
} }
.write_async(writer) .write_async(writer)
@ -463,11 +462,11 @@ async fn handle_incoming_message(
} }
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N315EndOfWho { body: ServerMessageBody::N315EndOfWho {
client: user.nickname.clone(), client: user.nickname.clone(),
mask: target.clone(), mask: target.clone(),
msg: b"End of WHO list".to_vec(), msg: "End of WHO list".into(),
}, },
} }
.write_async(writer) .write_async(writer)
@ -484,10 +483,10 @@ async fn handle_incoming_message(
if nickname == user.nickname { if nickname == user.nickname {
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N221UserModeIs { body: ServerMessageBody::N221UserModeIs {
client: user.nickname.clone(), client: user.nickname.clone(),
modes: b"+r".to_vec(), modes: "+r".into(),
}, },
} }
.write_async(writer) .write_async(writer)
@ -523,19 +522,16 @@ fn user_to_who_msg(
target_user_nickname: &ByteVec, target_user_nickname: &ByteVec,
) -> ServerMessageBody { ) -> ServerMessageBody {
// Username is equal to nickname // Username is equal to nickname
let mut username = Vec::with_capacity(target_user_nickname.len() + 1); let username = format!("~{target_user_nickname}").into();
username.push(b'~');
username.extend_from_slice(target_user_nickname.as_slice());
// User's host is not public, replace it with `user/<nickname>` pattern // User's host is not public, replace it with `user/<nickname>` pattern
let mut host = b"user/".to_vec(); let mut host = format!("user/{target_user_nickname}").into();
host.extend_from_slice(target_user_nickname.as_slice());
ServerMessageBody::N352WhoReply { ServerMessageBody::N352WhoReply {
client: requestor.nickname.clone(), client: requestor.nickname.clone(),
username, username,
host, host,
server: config.server_name.as_bytes().to_vec(), server: config.server_name.clone(),
flags: AwayStatus::Here, flags: AwayStatus::Here,
nickname: target_user_nickname.clone(), nickname: target_user_nickname.clone(),
hops: 0, hops: 0,
@ -559,11 +555,11 @@ async fn handle_join(
} else { } else {
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N474BannedFromChan { body: ServerMessageBody::N474BannedFromChan {
client: user.nickname.clone(), client: user.nickname.clone(),
chan: chan.clone(), chan: chan.clone(),
message: b"U dun goofed".to_vec(), message: "U dun goofed".into(),
}, },
} }
.write_async(writer) .write_async(writer)
@ -616,7 +612,7 @@ async fn produce_on_join_cmd_messages(
.await?; .await?;
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N332Topic { body: ServerMessageBody::N332Topic {
client: user.nickname.clone(), client: user.nickname.clone(),
chat: chan.clone(), chat: chan.clone(),
@ -625,29 +621,29 @@ async fn produce_on_join_cmd_messages(
} }
.write_async(writer) .write_async(writer)
.await?; .await?;
let mut members = if let Some(head) = room_info.members.first() { let mut members: String = if let Some(head) = room_info.members.first() {
head.as_bytes().clone() head.as_bytes().clone()
} else { } else {
user.nickname.clone() user.nickname.clone()
}; }.into();
for i in &room_info.members[1..] { for i in &room_info.members[1..] {
members.push(b' '); members.push(' ');
members.extend(i.as_bytes()); members.push_str(i.as_bytes());
} }
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N353NamesReply { body: ServerMessageBody::N353NamesReply {
client: user.nickname.clone(), client: user.nickname.clone(),
chan: chan.clone(), chan: chan.clone(),
members, members: members.into(),
}, },
} }
.write_async(writer) .write_async(writer)
.await?; .await?;
ServerMessage { ServerMessage {
tags: vec![], tags: vec![],
sender: Some(config.server_name.as_bytes().to_vec()), sender: Some(config.server_name.clone()),
body: ServerMessageBody::N366NamesReplyEnd { body: ServerMessageBody::N366NamesReplyEnd {
client: user.nickname.clone(), client: user.nickname.clone(),
chan: chan.clone(), chan: chan.clone(),

View File

@ -254,8 +254,8 @@ async fn socket_auth(
let _ = xmpp::sasl::Auth::parse(xml_reader, reader_buf).await?; let _ = xmpp::sasl::Auth::parse(xml_reader, reader_buf).await?;
xmpp::sasl::Success.write_xml(xml_writer).await?; xmpp::sasl::Success.write_xml(xml_writer).await?;
Ok(Authenticated { Ok(Authenticated {
player_id: PlayerId::from_bytes(b"darova".to_vec())?, player_id: PlayerId::from_bytes("darova".into())?,
xmpp_name: Name("darova".to_owned()), xmpp_name: Name("darova".into()),
xmpp_resource: Resource("darova".to_owned()), xmpp_resource: Resource("darova".to_owned()),
xmpp_muc_name: Resource("darova".to_owned()), xmpp_muc_name: Resource("darova".to_owned()),
}) })
@ -337,15 +337,15 @@ async fn socket_final(
resource: Some(authenticated.xmpp_resource.clone()), resource: Some(authenticated.xmpp_resource.clone()),
}), }),
from: Some(Jid { from: Some(Jid {
name: Some(Name(std::str::from_utf8(room_id.as_bytes())?.to_owned())), name: Some(Name(room_id.into_bytes().into())),
server: Server("rooms.localhost".into()), server: Server("rooms.localhost".into()),
resource: Some(Resource(std::str::from_utf8(author_id.as_bytes())?.to_owned())), resource: Some(Resource(author_id.into_bytes().into())),
}), }),
id: None, id: None,
r#type: xmpp::client::MessageType::Groupchat, r#type: xmpp::client::MessageType::Groupchat,
lang: None, lang: None,
subject: None, subject: None,
body: body, body: body.into(),
} }
.serialize(&mut events); .serialize(&mut events);
} }
@ -394,8 +394,8 @@ async fn handle_packet(
if server.0 == "rooms.localhost" && m.r#type == MessageType::Groupchat { if server.0 == "rooms.localhost" && m.r#type == MessageType::Groupchat {
user_handle user_handle
.send_message( .send_message(
RoomId::from_bytes(name.0.clone().into_bytes())?, RoomId::from_bytes(name.0.clone().into())?,
m.body.clone(), m.body.clone().into(),
) )
.await?; .await?;
Message { Message {
@ -413,7 +413,7 @@ async fn handle_packet(
r#type: xmpp::client::MessageType::Groupchat, r#type: xmpp::client::MessageType::Groupchat,
lang: None, lang: None,
subject: None, subject: None,
body: m.body, body: m.body.clone(),
} }
.serialize(output); .serialize(output);
false false
@ -446,7 +446,7 @@ async fn handle_packet(
}) = p.to }) = p.to
{ {
let a = user_handle let a = user_handle
.join_room(RoomId::from_bytes(name.0.clone().into_bytes())?) .join_room(RoomId::from_bytes(name.0.clone())?)
.await?; .await?;
Presence::<()> { Presence::<()> {
to: Some(Jid { to: Some(Jid {
@ -455,7 +455,7 @@ async fn handle_packet(
resource: Some(user.xmpp_resource.clone()), resource: Some(user.xmpp_resource.clone()),
}), }),
from: Some(Jid { from: Some(Jid {
name: Some(name), name: Some(name.clone()),
server: Server("rooms.localhost".into()), server: Server("rooms.localhost".into()),
resource: Some(user.xmpp_muc_name.clone()), resource: Some(user.xmpp_muc_name.clone()),
}), }),
@ -483,7 +483,7 @@ async fn handle_iq(output: &mut Vec<Event<'static>>, iq: Iq<IqClientBody>, rooms
to: None, to: None,
r#type: xmpp::client::IqType::Result, r#type: xmpp::client::IqType::Result,
body: BindResponse(Jid { body: BindResponse(Jid {
name: Some(Name("darova".to_string())), name: Some(Name("darova".into())),
server: Server("localhost".to_string()), server: Server("localhost".to_string()),
resource: Some(Resource("kek".to_string())), resource: Some(Resource("kek".to_string())),
}), }),
@ -605,7 +605,7 @@ async fn disco_items(to: Option<&str>, req: &ItemQuery, rooms: &RoomRegistry) ->
.into_iter() .into_iter()
.map(|room_info| Item { .map(|room_info| Item {
jid: Jid { jid: Jid {
name: Some(Name(String::from_utf8(room_info.id.into_bytes()).unwrap())), name: Some(Name(room_info.id.into_bytes())),
server: Server("rooms.localhost".to_string()), server: Server("rooms.localhost".to_string()),
resource: None, resource: None,
}, },

View File

@ -9,20 +9,20 @@ pub enum ClientMessage {
}, },
/// PING <token> /// PING <token>
Ping { Ping {
token: ByteVec, token: Box<str>,
}, },
/// PONG <token> /// PONG <token>
Pong { Pong {
token: ByteVec, token: Box<str>,
}, },
/// NICK <nickname> /// NICK <nickname>
Nick { Nick {
nickname: ByteVec, nickname: Box<str>,
}, },
/// USER <username> 0 * :<realname> /// USER <username> 0 * :<realname>
User { User {
username: ByteVec, username: Box<str>,
realname: ByteVec, realname: Box<str>,
}, },
/// JOIN <chan> /// JOIN <chan>
Join(Chan), Join(Chan),
@ -37,24 +37,24 @@ pub enum ClientMessage {
/// TOPIC <chan> :<topic> /// TOPIC <chan> :<topic>
Topic { Topic {
chan: Chan, chan: Chan,
topic: ByteVec, topic: Box<str>,
}, },
Part { Part {
chan: Chan, chan: Chan,
message: ByteVec, message: Box<str>,
}, },
/// PRIVMSG <target> :<msg> /// PRIVMSG <target> :<msg>
PrivateMessage { PrivateMessage {
recipient: Recipient, recipient: Recipient,
body: ByteVec, body: Box<str>,
}, },
/// QUIT :<reason> /// QUIT :<reason>
Quit { Quit {
reason: ByteVec, reason: Box<str>,
}, },
} }
pub fn client_message(input: &[u8]) -> IResult<&[u8], ClientMessage> { pub fn client_message(input: &str) -> IResult<&str, ClientMessage> {
alt(( alt((
client_message_capability, client_message_capability,
client_message_ping, client_message_ping,
@ -71,50 +71,50 @@ pub fn client_message(input: &[u8]) -> IResult<&[u8], ClientMessage> {
))(input) ))(input)
} }
fn client_message_capability(input: &[u8]) -> IResult<&[u8], ClientMessage> { fn client_message_capability(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("CAP ")(input)?; let (input, _) = tag("CAP ")(input)?;
let (input, subcommand) = capability_subcommand(input)?; let (input, subcommand) = capability_subcommand(input)?;
Ok((input, ClientMessage::Capability { subcommand })) Ok((input, ClientMessage::Capability { subcommand }))
} }
fn client_message_ping(input: &[u8]) -> IResult<&[u8], ClientMessage> { fn client_message_ping(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("PING ")(input)?; let (input, _) = tag("PING ")(input)?;
let (input, token) = token(input)?; let (input, token) = token(input)?;
Ok(( Ok((
input, input,
ClientMessage::Ping { ClientMessage::Ping {
token: token.to_owned(), token: token.into(),
}, },
)) ))
} }
fn client_message_pong(input: &[u8]) -> IResult<&[u8], ClientMessage> { fn client_message_pong(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("PONG ")(input)?; let (input, _) = tag("PONG ")(input)?;
let (input, token) = token(input)?; let (input, token) = token(input)?;
Ok(( Ok((
input, input,
ClientMessage::Pong { ClientMessage::Pong {
token: token.to_owned(), token: token.into(),
}, },
)) ))
} }
fn client_message_nick(input: &[u8]) -> IResult<&[u8], ClientMessage> { fn client_message_nick(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("NICK ")(input)?; let (input, _) = tag("NICK ")(input)?;
let (input, nickname) = receiver(input)?; let (input, nickname) = receiver(input)?;
Ok(( Ok((
input, input,
ClientMessage::Nick { ClientMessage::Nick {
nickname: nickname.to_owned(), nickname: nickname.into(),
}, },
)) ))
} }
fn client_message_user(input: &[u8]) -> IResult<&[u8], ClientMessage> { fn client_message_user(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("USER ")(input)?; let (input, _) = tag("USER ")(input)?;
let (input, username) = receiver(input)?; let (input, username) = receiver(input)?;
let (input, _) = tag(" ")(input)?; let (input, _) = tag(" ")(input)?;
@ -127,70 +127,70 @@ fn client_message_user(input: &[u8]) -> IResult<&[u8], ClientMessage> {
Ok(( Ok((
input, input,
ClientMessage::User { ClientMessage::User {
username: username.to_owned(), username: username.into(),
realname: realname.to_owned(), realname: realname.into(),
}, },
)) ))
} }
fn client_message_join(input: &[u8]) -> IResult<&[u8], ClientMessage> { fn client_message_join(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("JOIN ")(input)?; let (input, _) = tag("JOIN ")(input)?;
let (input, chan) = chan(input)?; let (input, chan) = chan(input)?;
Ok((input, ClientMessage::Join(chan))) Ok((input, ClientMessage::Join(chan)))
} }
fn client_message_mode(input: &[u8]) -> IResult<&[u8], ClientMessage> { fn client_message_mode(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("MODE ")(input)?; let (input, _) = tag("MODE ")(input)?;
let (input, target) = recipient(input)?; let (input, target) = recipient(input)?;
Ok((input, ClientMessage::Mode { target })) Ok((input, ClientMessage::Mode { target }))
} }
fn client_message_who(input: &[u8]) -> IResult<&[u8], ClientMessage> { fn client_message_who(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("WHO ")(input)?; let (input, _) = tag("WHO ")(input)?;
let (input, target) = recipient(input)?; let (input, target) = recipient(input)?;
Ok((input, ClientMessage::Who { target })) Ok((input, ClientMessage::Who { target }))
} }
fn client_message_topic(input: &[u8]) -> IResult<&[u8], 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)?;
let (input, _) = tag(b" :")(input)?; let (input, _) = tag(" :")(input)?;
let (input, topic) = token(input)?; let (input, topic) = token(input)?;
let topic = topic.to_vec(); let topic = topic.into();
Ok((input, ClientMessage::Topic { chan, topic })) Ok((input, ClientMessage::Topic { chan, topic }))
} }
fn client_message_part(input: &[u8]) -> IResult<&[u8], ClientMessage> { fn client_message_part(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("PART ")(input)?; let (input, _) = tag("PART ")(input)?;
let (input, chan) = chan(input)?; let (input, chan) = chan(input)?;
let (input, _) = tag(b" :")(input)?; let (input, _) = tag(" :")(input)?;
let (input, message) = token(input)?; let (input, message) = token(input)?;
let message = message.to_vec(); let message = message.into();
Ok((input, ClientMessage::Part { chan, message })) Ok((input, ClientMessage::Part { chan, message }))
} }
fn client_message_privmsg(input: &[u8]) -> IResult<&[u8], ClientMessage> { fn client_message_privmsg(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("PRIVMSG ")(input)?; let (input, _) = tag("PRIVMSG ")(input)?;
let (input, recipient) = recipient(input)?; let (input, recipient) = recipient(input)?;
let (input, _) = tag(" :")(input)?; let (input, _) = tag(" :")(input)?;
let (input, body) = token(input)?; let (input, body) = token(input)?;
let body = body.to_vec(); let body = body.into();
Ok((input, ClientMessage::PrivateMessage { recipient, body })) Ok((input, ClientMessage::PrivateMessage { recipient, body }))
} }
fn client_message_quit(input: &[u8]) -> IResult<&[u8], ClientMessage> { fn client_message_quit(input: &str) -> IResult<&str, ClientMessage> {
let (input, _) = tag("QUIT :")(input)?; let (input, _) = tag("QUIT :")(input)?;
let (input, reason) = token(input)?; let (input, reason) = token(input)?;
Ok(( Ok((
input, input,
ClientMessage::Quit { ClientMessage::Quit {
reason: reason.to_vec(), reason: reason.into(),
}, },
)) ))
} }
@ -203,23 +203,23 @@ pub enum CapabilitySubcommand {
End, End,
} }
fn capability_subcommand(input: &[u8]) -> IResult<&[u8], CapabilitySubcommand> { fn capability_subcommand(input: &str) -> IResult<&str, CapabilitySubcommand> {
alt((capability_subcommand_ls, capability_subcommand_end))(input) alt((capability_subcommand_ls, capability_subcommand_end))(input)
} }
fn capability_subcommand_ls(input: &[u8]) -> IResult<&[u8], CapabilitySubcommand> { fn capability_subcommand_ls(input: &str) -> IResult<&str, CapabilitySubcommand> {
let (input, _) = tag("LS ")(input)?; let (input, _) = tag("LS ")(input)?;
let (input, code) = take(3usize)(input)?; let (input, code) = take(3usize)(input)?;
Ok(( Ok((
input, input,
CapabilitySubcommand::List { CapabilitySubcommand::List {
code: code.try_into().unwrap(), code: code.as_bytes().try_into().unwrap(),
}, },
)) ))
} }
fn capability_subcommand_end(input: &[u8]) -> IResult<&[u8], CapabilitySubcommand> { fn capability_subcommand_end(input: &str) -> IResult<&str, CapabilitySubcommand> {
let (input, _) = tag("END")(input)?; let (input, _) = tag("END")(input)?;
Ok((input, CapabilitySubcommand::End)) Ok((input, CapabilitySubcommand::End))
} }
@ -231,7 +231,7 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn test_client_message_cap_ls() { fn test_client_message_cap_ls() {
let input = b"CAP LS 302"; let input = "CAP LS 302";
let expected = ClientMessage::Capability { let expected = ClientMessage::Capability {
subcommand: CapabilitySubcommand::List { code: *b"302" }, subcommand: CapabilitySubcommand::List { code: *b"302" },
}; };
@ -242,7 +242,7 @@ mod test {
#[test] #[test]
fn test_client_message_cap_end() { fn test_client_message_cap_end() {
let input = b"CAP END"; let input = "CAP END";
let expected = ClientMessage::Capability { let expected = ClientMessage::Capability {
subcommand: CapabilitySubcommand::End, subcommand: CapabilitySubcommand::End,
}; };
@ -253,9 +253,9 @@ mod test {
#[test] #[test]
fn test_client_message_ping() { fn test_client_message_ping() {
let input = b"PING 1337"; let input = "PING 1337";
let expected = ClientMessage::Ping { let expected = ClientMessage::Ping {
token: b"1337".to_vec(), token: "1337".into(),
}; };
let result = client_message(input); let result = client_message(input);
@ -263,9 +263,9 @@ mod test {
} }
#[test] #[test]
fn test_client_message_pong() { fn test_client_message_pong() {
let input = b"PONG 1337"; let input = "PONG 1337";
let expected = ClientMessage::Pong { let expected = ClientMessage::Pong {
token: b"1337".to_vec(), token: "1337".into(),
}; };
let result = client_message(input); let result = client_message(input);
@ -273,9 +273,9 @@ mod test {
} }
#[test] #[test]
fn test_client_message_nick() { fn test_client_message_nick() {
let input = b"NICK SomeNick"; let input = "NICK SomeNick";
let expected = ClientMessage::Nick { let expected = ClientMessage::Nick {
nickname: b"SomeNick".to_vec(), nickname: "SomeNick".into(),
}; };
let result = client_message(input); let result = client_message(input);
@ -283,10 +283,10 @@ mod test {
} }
#[test] #[test]
fn test_client_message_user() { fn test_client_message_user() {
let input = b"USER SomeNick 8 * :Real Name"; let input = "USER SomeNick 8 * :Real Name";
let expected = ClientMessage::User { let expected = ClientMessage::User {
username: b"SomeNick".to_vec(), username: "SomeNick".into(),
realname: b"Real Name".to_vec(), realname: "Real Name".into(),
}; };
let result = client_message(input); let result = client_message(input);
@ -294,10 +294,10 @@ mod test {
} }
#[test] #[test]
fn test_client_message_part() { fn test_client_message_part() {
let input = b"PART #chan :Pokasiki !!!"; let input = "PART #chan :Pokasiki !!!";
let expected = ClientMessage::Part { let expected = ClientMessage::Part {
chan: Chan::Global(b"chan".to_vec()), chan: Chan::Global("chan".into()),
message: b"Pokasiki !!!".to_vec(), message: "Pokasiki !!!".into(),
}; };
let result = client_message(input); let result = client_message(input);

View File

@ -11,7 +11,7 @@ use nom::{
}; };
use tokio::io::{AsyncWrite, AsyncWriteExt}; use tokio::io::{AsyncWrite, AsyncWriteExt};
type ByteVec = Vec<u8>; type ByteVec = Box<str>;
/// Single message tag value. /// Single message tag value.
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -20,48 +20,48 @@ pub struct Tag {
value: Option<u8>, value: Option<u8>,
} }
fn receiver(input: &[u8]) -> IResult<&[u8], &[u8]> { fn receiver(input: &str) -> IResult<&str, &str> {
take_while(|i| i != b'\n' && i != b'\r' && i != b' ')(input) take_while(|i| i != '\n' && i != '\r' && i != ' ')(input)
} }
fn token(input: &[u8]) -> IResult<&[u8], &[u8]> { fn token(input: &str) -> IResult<&str, &str> {
take_while(|i| i != b'\n' && i != b'\r')(input) take_while(|i| i != '\n' && i != '\r')(input)
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum Chan { pub enum Chan {
/// #<name> — network-global channel, available from any server in the network. /// #<name> — network-global channel, available from any server in the network.
Global(ByteVec), Global(Box<str>),
/// &<name> — server-local channel, available only to connections to the same server. Rarely used in practice. /// &<name> — server-local channel, available only to connections to the same server. Rarely used in practice.
Local(ByteVec), Local(Box<str>),
} }
impl Chan { impl Chan {
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> { pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> {
match self { match self {
Chan::Global(name) => { Chan::Global(name) => {
writer.write_all(b"#").await?; writer.write_all(b"#").await?;
writer.write_all(&name).await?; writer.write_all(name.as_bytes()).await?;
} }
Chan::Local(name) => { Chan::Local(name) => {
writer.write_all(b"&").await?; writer.write_all(b"&").await?;
writer.write_all(&name).await?; writer.write_all(name.as_bytes()).await?;
} }
} }
Ok(()) Ok(())
} }
} }
fn chan(input: &[u8]) -> IResult<&[u8], Chan> { fn chan(input: &str) -> IResult<&str, Chan> {
fn chan_global(input: &[u8]) -> IResult<&[u8], Chan> { fn chan_global(input: &str) -> IResult<&str, Chan> {
let (input, _) = tag("#")(input)?; let (input, _) = tag("#")(input)?;
let (input, name) = receiver(input)?; let (input, name) = receiver(input)?;
Ok((input, Chan::Global(name.to_vec()))) Ok((input, Chan::Global(name.into())))
} }
fn chan_local(input: &[u8]) -> IResult<&[u8], Chan> { fn chan_local(input: &str) -> IResult<&str, Chan> {
let (input, _) = tag("&")(input)?; let (input, _) = tag("&")(input)?;
let (input, name) = receiver(input)?; let (input, name) = receiver(input)?;
Ok((input, Chan::Local(name.to_vec()))) Ok((input, Chan::Local(name.into())))
} }
alt((chan_global, chan_local))(input) alt((chan_global, chan_local))(input)
@ -75,22 +75,22 @@ pub enum Recipient {
impl Recipient { impl Recipient {
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> { pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> {
match self { match self {
Recipient::Nick(nick) => writer.write_all(&nick).await?, Recipient::Nick(nick) => writer.write_all(nick.as_bytes()).await?,
Recipient::Chan(chan) => chan.write_async(writer).await?, Recipient::Chan(chan) => chan.write_async(writer).await?,
} }
Ok(()) Ok(())
} }
} }
fn recipient(input: &[u8]) -> IResult<&[u8], Recipient> { fn recipient(input: &str) -> IResult<&str, Recipient> {
fn recipient_chan(input: &[u8]) -> IResult<&[u8], Recipient> { fn recipient_chan(input: &str) -> IResult<&str, Recipient> {
let (input, chan) = chan(input)?; let (input, chan) = chan(input)?;
Ok((input, Recipient::Chan(chan))) Ok((input, Recipient::Chan(chan)))
} }
fn recipient_nick(input: &[u8]) -> IResult<&[u8], Recipient> { fn recipient_nick(input: &str) -> IResult<&str, Recipient> {
let (input, nick) = receiver(input)?; let (input, nick) = receiver(input)?;
Ok((input, Recipient::Nick(nick.to_vec()))) Ok((input, Recipient::Nick(nick.into())))
} }
alt((recipient_chan, recipient_nick))(input) alt((recipient_chan, recipient_nick))(input)
@ -105,8 +105,8 @@ mod test {
#[test] #[test]
fn test_chan_global() { fn test_chan_global() {
let input = b"#testchan"; let input = "#testchan";
let expected = Chan::Global(b"testchan".to_vec()); let expected = Chan::Global("testchan".into());
let result = chan(input); let result = chan(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
@ -116,13 +116,13 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!(bytes.as_slice(), input); assert_eq!(bytes.as_slice(), input.as_bytes());
} }
#[test] #[test]
fn test_chan_local() { fn test_chan_local() {
let input = b"&localchan"; let input = "&localchan";
let expected = Chan::Local(b"localchan".to_vec()); let expected = Chan::Local("localchan".into());
let result = chan(input); let result = chan(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
@ -132,13 +132,13 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!(bytes.as_slice(), input); assert_eq!(bytes.as_slice(), input.as_bytes());
} }
#[test] #[test]
fn test_recipient_user() { fn test_recipient_user() {
let input = b"User"; let input = "User";
let expected = Recipient::Nick(b"User".to_vec()); let expected = Recipient::Nick("User".into());
let result = recipient(input); let result = recipient(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
@ -148,6 +148,6 @@ mod test {
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!(bytes.as_slice(), input); assert_eq!(bytes.as_slice(), input.as_bytes());
} }
} }

View File

@ -18,7 +18,7 @@ impl ServerMessage {
match &self.sender { match &self.sender {
Some(ref sender) => { Some(ref sender) => {
writer.write_all(b":").await?; writer.write_all(b":").await?;
writer.write_all(sender.as_slice()).await?; writer.write_all(sender.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
} }
None => {} None => {}
@ -29,9 +29,9 @@ impl ServerMessage {
} }
} }
pub fn server_message(input: &[u8]) -> IResult<&[u8], ServerMessage> { pub fn server_message(input: &str) -> IResult<&str, ServerMessage> {
let (input, command) = server_message_body(input)?; let (input, command) = server_message_body(input)?;
let (input, _) = tag(b"\n")(input)?; let (input, _) = tag("\n")(input)?;
let message = ServerMessage { let message = ServerMessage {
tags: vec![], tags: vec![],
@ -143,25 +143,25 @@ impl ServerMessageBody {
text, text,
} => { } => {
writer.write_all(b"NOTICE ").await?; writer.write_all(b"NOTICE ").await?;
writer.write_all(&first_target).await?; writer.write_all(first_target.as_bytes()).await?;
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(&text).await?; writer.write_all(text.as_bytes()).await?;
} }
ServerMessageBody::Ping { token } => { ServerMessageBody::Ping { token } => {
writer.write_all(b"PING ").await?; writer.write_all(b"PING ").await?;
writer.write_all(&token).await?; writer.write_all(token.as_bytes()).await?;
} }
ServerMessageBody::Pong { from, token } => { ServerMessageBody::Pong { from, token } => {
writer.write_all(b"PONG ").await?; writer.write_all(b"PONG ").await?;
writer.write_all(&from).await?; writer.write_all(from.as_bytes()).await?;
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(&token).await?; writer.write_all(token.as_bytes()).await?;
} }
ServerMessageBody::PrivateMessage { target, body } => { ServerMessageBody::PrivateMessage { target, body } => {
writer.write_all(b"PRIVMSG ").await?; writer.write_all(b"PRIVMSG ").await?;
target.write_async(writer).await?; target.write_async(writer).await?;
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(&body).await?; writer.write_all(body.as_bytes()).await?;
} }
ServerMessageBody::Join(chan) => { ServerMessageBody::Join(chan) => {
writer.write_all(b"JOIN ").await?; writer.write_all(b"JOIN ").await?;
@ -173,25 +173,25 @@ impl ServerMessageBody {
} }
ServerMessageBody::Error { reason } => { ServerMessageBody::Error { reason } => {
writer.write_all(b"ERROR :").await?; writer.write_all(b"ERROR :").await?;
writer.write_all(&reason).await?; writer.write_all(reason.as_bytes()).await?;
} }
ServerMessageBody::N001Welcome { client, text } => { ServerMessageBody::N001Welcome { client, text } => {
writer.write_all(b"001 ").await?; writer.write_all(b"001 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(text).await?; writer.write_all(text.as_bytes()).await?;
} }
ServerMessageBody::N002YourHost { client, text } => { ServerMessageBody::N002YourHost { client, text } => {
writer.write_all(b"002 ").await?; writer.write_all(b"002 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(text).await?; writer.write_all(text.as_bytes()).await?;
} }
ServerMessageBody::N003Created { client, text } => { ServerMessageBody::N003Created { client, text } => {
writer.write_all(b"003 ").await?; writer.write_all(b"003 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(text).await?; writer.write_all(text.as_bytes()).await?;
} }
ServerMessageBody::N004MyInfo { ServerMessageBody::N004MyInfo {
client, client,
@ -199,34 +199,34 @@ impl ServerMessageBody {
softname, softname,
} => { } => {
writer.write_all(b"004 ").await?; writer.write_all(b"004 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
writer.write_all(&hostname).await?; writer.write_all(hostname.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
writer.write_all(&softname).await?; writer.write_all(softname.as_bytes()).await?;
writer.write_all(b" r CFILPQbcefgijklmnopqrstvz").await?; writer.write_all(b" r CFILPQbcefgijklmnopqrstvz").await?;
// TODO remove hardcoded modes // TODO remove hardcoded modes
} }
ServerMessageBody::N005ISupport { client, params } => { ServerMessageBody::N005ISupport { client, params } => {
writer.write_all(b"005 ").await?; writer.write_all(b"005 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
writer.write_all(&params).await?; writer.write_all(params.as_bytes()).await?;
writer.write_all(b" :are supported by this server").await?; writer.write_all(b" :are supported by this server").await?;
} }
ServerMessageBody::N221UserModeIs { client, modes } => { ServerMessageBody::N221UserModeIs { client, modes } => {
writer.write_all(b"221 ").await?; writer.write_all(b"221 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
writer.write_all(&modes).await?; writer.write_all(modes.as_bytes()).await?;
} }
ServerMessageBody::N315EndOfWho { client, mask, msg } => { ServerMessageBody::N315EndOfWho { client, mask, msg } => {
writer.write_all(b"315 ").await?; writer.write_all(b"315 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
mask.write_async(writer).await?; mask.write_async(writer).await?;
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(&msg).await?; writer.write_all(msg.as_bytes()).await?;
} }
ServerMessageBody::N332Topic { ServerMessageBody::N332Topic {
client, client,
@ -234,11 +234,11 @@ impl ServerMessageBody {
topic, topic,
} => { } => {
writer.write_all(b"332 ").await?; writer.write_all(b"332 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
chat.write_async(writer).await?; chat.write_async(writer).await?;
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(&topic).await?; writer.write_all(topic.as_bytes()).await?;
} }
ServerMessageBody::N352WhoReply { ServerMessageBody::N352WhoReply {
client, client,
@ -251,24 +251,24 @@ impl ServerMessageBody {
realname, realname,
} => { } => {
writer.write_all(b"352 ").await?; writer.write_all(b"352 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" * ").await?; writer.write_all(b" * ").await?;
writer.write_all(&username).await?; writer.write_all(username.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
writer.write_all(&host).await?; writer.write_all(host.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
writer.write_all(&server).await?; writer.write_all(server.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
match flags { match flags {
AwayStatus::Here => writer.write_all(b"H").await?, AwayStatus::Here => writer.write_all(b"H").await?,
AwayStatus::Gone => writer.write_all(b"G").await?, AwayStatus::Gone => writer.write_all(b"G").await?,
} }
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
writer.write_all(&nickname).await?; writer.write_all(nickname.as_bytes()).await?;
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(hops.to_string().as_bytes()).await?; writer.write_all(hops.to_string().as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
writer.write_all(&realname).await?; writer.write_all(realname.as_bytes()).await?;
} }
ServerMessageBody::N353NamesReply { ServerMessageBody::N353NamesReply {
client, client,
@ -276,15 +276,15 @@ impl ServerMessageBody {
members, members,
} => { } => {
writer.write_all(b"353 ").await?; writer.write_all(b"353 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" = ").await?; writer.write_all(b" = ").await?;
chan.write_async(writer).await?; chan.write_async(writer).await?;
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(&members).await?; writer.write_all(members.as_bytes()).await?;
} }
ServerMessageBody::N366NamesReplyEnd { client, chan } => { ServerMessageBody::N366NamesReplyEnd { client, chan } => {
writer.write_all(b"366 ").await?; writer.write_all(b"366 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
chan.write_async(writer).await?; chan.write_async(writer).await?;
writer.write_all(b" :End of /NAMES list").await?; writer.write_all(b" :End of /NAMES list").await?;
@ -295,11 +295,11 @@ impl ServerMessageBody {
message, message,
} => { } => {
writer.write_all(b"474 ").await?; writer.write_all(b"474 ").await?;
writer.write_all(&client).await?; writer.write_all(client.as_bytes()).await?;
writer.write_all(b" ").await?; writer.write_all(b" ").await?;
chan.write_async(writer).await?; chan.write_async(writer).await?;
writer.write_all(b" :").await?; writer.write_all(b" :").await?;
writer.write_all(&message).await?; writer.write_all(message.as_bytes()).await?;
} }
} }
Ok(()) Ok(())
@ -312,7 +312,7 @@ pub enum AwayStatus {
Gone, Gone,
} }
fn server_message_body(input: &[u8]) -> IResult<&[u8], ServerMessageBody> { fn server_message_body(input: &str) -> IResult<&str, ServerMessageBody> {
alt(( alt((
server_message_body_notice, server_message_body_notice,
server_message_body_ping, server_message_body_ping,
@ -320,14 +320,14 @@ fn server_message_body(input: &[u8]) -> IResult<&[u8], ServerMessageBody> {
))(input) ))(input)
} }
fn server_message_body_notice(input: &[u8]) -> IResult<&[u8], ServerMessageBody> { fn server_message_body_notice(input: &str) -> IResult<&str, ServerMessageBody> {
let (input, _) = tag("NOTICE ")(input)?; let (input, _) = tag("NOTICE ")(input)?;
let (input, first_target) = receiver(input)?; let (input, first_target) = receiver(input)?;
let (input, _) = tag(" :")(input)?; let (input, _) = tag(" :")(input)?;
let (input, text) = token(input)?; let (input, text) = token(input)?;
let first_target = first_target.to_owned(); let first_target = first_target.into();
let text = text.to_owned(); let text = text.into();
Ok(( Ok((
input, input,
ServerMessageBody::Notice { ServerMessageBody::Notice {
@ -338,19 +338,19 @@ fn server_message_body_notice(input: &[u8]) -> IResult<&[u8], ServerMessageBody>
)) ))
} }
fn server_message_body_ping(input: &[u8]) -> IResult<&[u8], ServerMessageBody> { fn server_message_body_ping(input: &str) -> IResult<&str, ServerMessageBody> {
let (input, _) = tag("PING ")(input)?; let (input, _) = tag("PING ")(input)?;
let (input, token) = token(input)?; let (input, token) = token(input)?;
Ok(( Ok((
input, input,
ServerMessageBody::Ping { ServerMessageBody::Ping {
token: token.to_owned(), token: token.into(),
}, },
)) ))
} }
fn server_message_body_pong(input: &[u8]) -> IResult<&[u8], ServerMessageBody> { fn server_message_body_pong(input: &str) -> IResult<&str, ServerMessageBody> {
let (input, _) = tag("PONG ")(input)?; let (input, _) = tag("PONG ")(input)?;
let (input, from) = receiver(input)?; let (input, from) = receiver(input)?;
let (input, _) = tag(" :")(input)?; let (input, _) = tag(" :")(input)?;
@ -359,8 +359,8 @@ fn server_message_body_pong(input: &[u8]) -> IResult<&[u8], ServerMessageBody> {
Ok(( Ok((
input, input,
ServerMessageBody::Pong { ServerMessageBody::Pong {
from: from.to_owned(), from: from.into(),
token: token.to_owned(), token: token.into(),
}, },
)) ))
} }
@ -374,14 +374,14 @@ mod test {
#[test] #[test]
fn test_server_message_notice() { fn test_server_message_notice() {
let input = b"NOTICE * :*** Looking up your hostname...\n"; let input = "NOTICE * :*** Looking up your hostname...\n";
let expected = ServerMessage { let expected = ServerMessage {
tags: vec![], tags: vec![],
sender: None, sender: None,
body: ServerMessageBody::Notice { body: ServerMessageBody::Notice {
first_target: b"*".to_vec(), first_target: "*".into(),
rest_targets: vec![], rest_targets: vec![],
text: b"*** Looking up your hostname...".to_vec(), text: "*** Looking up your hostname...".into(),
}, },
}; };
@ -392,18 +392,18 @@ mod test {
sync_future(expected.write_async(&mut bytes)) sync_future(expected.write_async(&mut bytes))
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!(bytes, input); assert_eq!(bytes, input.as_bytes());
} }
#[test] #[test]
fn test_server_message_pong() { fn test_server_message_pong() {
let input = b"PONG server.example :LAG004911\n"; let input = "PONG server.example :LAG004911\n";
let expected = ServerMessage { let expected = ServerMessage {
tags: vec![], tags: vec![],
sender: None, sender: None,
body: ServerMessageBody::Pong { body: ServerMessageBody::Pong {
from: b"server.example".to_vec(), from: "server.example".into(),
token: b"LAG004911".to_vec(), token: "LAG004911".into(),
}, },
}; };
@ -414,6 +414,6 @@ mod test {
sync_future(expected.write_async(&mut bytes)) sync_future(expected.write_async(&mut bytes))
.unwrap() .unwrap()
.unwrap(); .unwrap();
assert_eq!(bytes, input); assert_eq!(bytes, input.as_bytes());
} }
} }

View File

@ -12,7 +12,7 @@ pub const XMLNS: &'static str = "urn:ietf:params:xml:ns:xmpp-bind";
// TODO remove `pub` in newtypes, introduce validation // TODO remove `pub` in newtypes, introduce validation
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone)]
pub struct Name(pub String); pub struct Name(pub Box<str>);
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone)]
pub struct Server(pub String); pub struct Server(pub String);
@ -49,7 +49,7 @@ impl Jid {
.captures(i) .captures(i)
.ok_or(ffail!("Incorrectly format jid: {i}"))?; .ok_or(ffail!("Incorrectly format jid: {i}"))?;
let name = m.get(2).map(|name| Name(name.as_str().to_string())); let name = m.get(2).map(|name| Name(name.as_str().into()));
let server = m.get(3).unwrap(); let server = m.get(3).unwrap();
let server = Server(server.as_str().to_string()); let server = Server(server.as_str().to_string());
let resource = m let resource = m

View File

@ -613,7 +613,7 @@ mod tests {
from: None, from: None,
id: Some("aacea".to_string()), id: Some("aacea".to_string()),
to: Some(Jid { to: Some(Jid {
name: Some(Name("nikita".to_owned())), name: Some(Name("nikita".into())),
server: Server("vlnv.dev".to_owned()), server: Server("vlnv.dev".to_owned()),
resource: None resource: None
}), }),