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.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct PlayerId(ByteVec);
pub struct PlayerId(Box<str>);
impl PlayerId {
pub fn from_bytes(bytes: ByteVec) -> Result<PlayerId> {
pub fn from_bytes(bytes: Box<str>) -> Result<PlayerId> {
if bytes.len() > 32 {
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"));
}
Ok(PlayerId(bytes))
}
pub fn as_bytes(&self) -> &ByteVec {
pub fn as_bytes(&self) -> &Box<str> {
&self.0
}
pub fn into_bytes(self) -> Box<str> {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -52,7 +55,7 @@ pub struct PlayerConnection {
player_handle: PlayerHandle,
}
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
.send_message(room_id, self.connection_id.clone(), body)
.await
@ -126,7 +129,7 @@ impl PlayerHandle {
&self,
room_id: RoomId,
connection_id: ConnectionId,
body: String,
body: Box<str>,
) -> Result<()> {
let (promise, deferred) = oneshot();
let cmd = Cmd::SendMessage {
@ -183,7 +186,7 @@ pub enum Cmd {
},
SendMessage {
room_id: RoomId,
body: String,
body: Box<str>,
promise: Promise<()>,
},
ChangeTopic {
@ -208,7 +211,7 @@ pub enum Updates {
NewMessage {
room_id: RoomId,
author_id: PlayerId,
body: String,
body: Box<str>,
},
RoomJoined {
room_id: RoomId,
@ -285,7 +288,7 @@ impl Player {
player_id,
connections: AnonTable::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,
handle,
rooms,

View File

@ -24,7 +24,7 @@ impl RoomId {
"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"));
}
Ok(RoomId(bytes))
@ -60,7 +60,7 @@ impl RoomRegistry {
let room = Room {
room_id: room_id.clone(),
subscriptions: HashMap::new(),
topic: b"New room".to_vec(),
topic: "New room".into(),
};
let room_handle = RoomHandle(Arc::new(AsyncRwLock::new(room)));
inner.rooms.insert(room_id, room_handle.clone());
@ -112,7 +112,7 @@ impl RoomHandle {
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;
lock.send_message(player_id, body).await;
}
@ -157,7 +157,7 @@ impl Room {
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");
let update = Updates::NewMessage {
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 ByteVec = Vec<u8>;
pub type ByteVec = Box<str>;
pub fn fail(msg: &str) -> anyhow::Error {
anyhow::Error::msg(msg.to_owned())

View File

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

View File

@ -254,8 +254,8 @@ async fn socket_auth(
let _ = xmpp::sasl::Auth::parse(xml_reader, reader_buf).await?;
xmpp::sasl::Success.write_xml(xml_writer).await?;
Ok(Authenticated {
player_id: PlayerId::from_bytes(b"darova".to_vec())?,
xmpp_name: Name("darova".to_owned()),
player_id: PlayerId::from_bytes("darova".into())?,
xmpp_name: Name("darova".into()),
xmpp_resource: 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()),
}),
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()),
resource: Some(Resource(std::str::from_utf8(author_id.as_bytes())?.to_owned())),
resource: Some(Resource(author_id.into_bytes().into())),
}),
id: None,
r#type: xmpp::client::MessageType::Groupchat,
lang: None,
subject: None,
body: body,
body: body.into(),
}
.serialize(&mut events);
}
@ -394,8 +394,8 @@ async fn handle_packet(
if server.0 == "rooms.localhost" && m.r#type == MessageType::Groupchat {
user_handle
.send_message(
RoomId::from_bytes(name.0.clone().into_bytes())?,
m.body.clone(),
RoomId::from_bytes(name.0.clone().into())?,
m.body.clone().into(),
)
.await?;
Message {
@ -413,7 +413,7 @@ async fn handle_packet(
r#type: xmpp::client::MessageType::Groupchat,
lang: None,
subject: None,
body: m.body,
body: m.body.clone(),
}
.serialize(output);
false
@ -446,7 +446,7 @@ async fn handle_packet(
}) = p.to
{
let a = user_handle
.join_room(RoomId::from_bytes(name.0.clone().into_bytes())?)
.join_room(RoomId::from_bytes(name.0.clone())?)
.await?;
Presence::<()> {
to: Some(Jid {
@ -455,7 +455,7 @@ async fn handle_packet(
resource: Some(user.xmpp_resource.clone()),
}),
from: Some(Jid {
name: Some(name),
name: Some(name.clone()),
server: Server("rooms.localhost".into()),
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,
r#type: xmpp::client::IqType::Result,
body: BindResponse(Jid {
name: Some(Name("darova".to_string())),
name: Some(Name("darova".into())),
server: Server("localhost".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()
.map(|room_info| Item {
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()),
resource: None,
},

View File

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

View File

@ -11,7 +11,7 @@ use nom::{
};
use tokio::io::{AsyncWrite, AsyncWriteExt};
type ByteVec = Vec<u8>;
type ByteVec = Box<str>;
/// Single message tag value.
#[derive(Clone, Debug, PartialEq, Eq)]
@ -20,48 +20,48 @@ pub struct Tag {
value: Option<u8>,
}
fn receiver(input: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(|i| i != b'\n' && i != b'\r' && i != b' ')(input)
fn receiver(input: &str) -> IResult<&str, &str> {
take_while(|i| i != '\n' && i != '\r' && i != ' ')(input)
}
fn token(input: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(|i| i != b'\n' && i != b'\r')(input)
fn token(input: &str) -> IResult<&str, &str> {
take_while(|i| i != '\n' && i != '\r')(input)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Chan {
/// #<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.
Local(ByteVec),
Local(Box<str>),
}
impl Chan {
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> {
match self {
Chan::Global(name) => {
writer.write_all(b"#").await?;
writer.write_all(&name).await?;
writer.write_all(name.as_bytes()).await?;
}
Chan::Local(name) => {
writer.write_all(b"&").await?;
writer.write_all(&name).await?;
writer.write_all(name.as_bytes()).await?;
}
}
Ok(())
}
}
fn chan(input: &[u8]) -> IResult<&[u8], Chan> {
fn chan_global(input: &[u8]) -> IResult<&[u8], Chan> {
fn chan(input: &str) -> IResult<&str, Chan> {
fn chan_global(input: &str) -> IResult<&str, Chan> {
let (input, _) = tag("#")(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, name) = receiver(input)?;
Ok((input, Chan::Local(name.to_vec())))
Ok((input, Chan::Local(name.into())))
}
alt((chan_global, chan_local))(input)
@ -75,22 +75,22 @@ pub enum Recipient {
impl Recipient {
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> {
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?,
}
Ok(())
}
}
fn recipient(input: &[u8]) -> IResult<&[u8], Recipient> {
fn recipient_chan(input: &[u8]) -> IResult<&[u8], Recipient> {
fn recipient(input: &str) -> IResult<&str, Recipient> {
fn recipient_chan(input: &str) -> IResult<&str, Recipient> {
let (input, chan) = chan(input)?;
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)?;
Ok((input, Recipient::Nick(nick.to_vec())))
Ok((input, Recipient::Nick(nick.into())))
}
alt((recipient_chan, recipient_nick))(input)
@ -105,8 +105,8 @@ mod test {
#[test]
fn test_chan_global() {
let input = b"#testchan";
let expected = Chan::Global(b"testchan".to_vec());
let input = "#testchan";
let expected = Chan::Global("testchan".into());
let result = chan(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
@ -116,13 +116,13 @@ mod test {
.unwrap()
.unwrap();
assert_eq!(bytes.as_slice(), input);
assert_eq!(bytes.as_slice(), input.as_bytes());
}
#[test]
fn test_chan_local() {
let input = b"&localchan";
let expected = Chan::Local(b"localchan".to_vec());
let input = "&localchan";
let expected = Chan::Local("localchan".into());
let result = chan(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
@ -132,13 +132,13 @@ mod test {
.unwrap()
.unwrap();
assert_eq!(bytes.as_slice(), input);
assert_eq!(bytes.as_slice(), input.as_bytes());
}
#[test]
fn test_recipient_user() {
let input = b"User";
let expected = Recipient::Nick(b"User".to_vec());
let input = "User";
let expected = Recipient::Nick("User".into());
let result = recipient(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
@ -148,6 +148,6 @@ mod test {
.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 {
Some(ref sender) => {
writer.write_all(b":").await?;
writer.write_all(sender.as_slice()).await?;
writer.write_all(sender.as_bytes()).await?;
writer.write_all(b" ").await?;
}
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, _) = tag(b"\n")(input)?;
let (input, _) = tag("\n")(input)?;
let message = ServerMessage {
tags: vec![],
@ -143,25 +143,25 @@ impl ServerMessageBody {
text,
} => {
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(&text).await?;
writer.write_all(text.as_bytes()).await?;
}
ServerMessageBody::Ping { token } => {
writer.write_all(b"PING ").await?;
writer.write_all(&token).await?;
writer.write_all(token.as_bytes()).await?;
}
ServerMessageBody::Pong { from, token } => {
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(&token).await?;
writer.write_all(token.as_bytes()).await?;
}
ServerMessageBody::PrivateMessage { target, body } => {
writer.write_all(b"PRIVMSG ").await?;
target.write_async(writer).await?;
writer.write_all(b" :").await?;
writer.write_all(&body).await?;
writer.write_all(body.as_bytes()).await?;
}
ServerMessageBody::Join(chan) => {
writer.write_all(b"JOIN ").await?;
@ -173,25 +173,25 @@ impl ServerMessageBody {
}
ServerMessageBody::Error { reason } => {
writer.write_all(b"ERROR :").await?;
writer.write_all(&reason).await?;
writer.write_all(reason.as_bytes()).await?;
}
ServerMessageBody::N001Welcome { client, text } => {
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(text).await?;
writer.write_all(text.as_bytes()).await?;
}
ServerMessageBody::N002YourHost { client, text } => {
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(text).await?;
writer.write_all(text.as_bytes()).await?;
}
ServerMessageBody::N003Created { client, text } => {
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(text).await?;
writer.write_all(text.as_bytes()).await?;
}
ServerMessageBody::N004MyInfo {
client,
@ -199,34 +199,34 @@ impl ServerMessageBody {
softname,
} => {
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(&hostname).await?;
writer.write_all(hostname.as_bytes()).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?;
// TODO remove hardcoded modes
}
ServerMessageBody::N005ISupport { client, params } => {
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(&params).await?;
writer.write_all(params.as_bytes()).await?;
writer.write_all(b" :are supported by this server").await?;
}
ServerMessageBody::N221UserModeIs { client, modes } => {
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(&modes).await?;
writer.write_all(modes.as_bytes()).await?;
}
ServerMessageBody::N315EndOfWho { client, mask, msg } => {
writer.write_all(b"315 ").await?;
writer.write_all(&client).await?;
writer.write_all(client.as_bytes()).await?;
writer.write_all(b" ").await?;
mask.write_async(writer).await?;
writer.write_all(b" :").await?;
writer.write_all(&msg).await?;
writer.write_all(msg.as_bytes()).await?;
}
ServerMessageBody::N332Topic {
client,
@ -234,11 +234,11 @@ impl ServerMessageBody {
topic,
} => {
writer.write_all(b"332 ").await?;
writer.write_all(&client).await?;
writer.write_all(client.as_bytes()).await?;
writer.write_all(b" ").await?;
chat.write_async(writer).await?;
writer.write_all(b" :").await?;
writer.write_all(&topic).await?;
writer.write_all(topic.as_bytes()).await?;
}
ServerMessageBody::N352WhoReply {
client,
@ -251,24 +251,24 @@ impl ServerMessageBody {
realname,
} => {
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(&username).await?;
writer.write_all(username.as_bytes()).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(&server).await?;
writer.write_all(server.as_bytes()).await?;
writer.write_all(b" ").await?;
match flags {
AwayStatus::Here => writer.write_all(b"H").await?,
AwayStatus::Gone => writer.write_all(b"G").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(hops.to_string().as_bytes()).await?;
writer.write_all(b" ").await?;
writer.write_all(&realname).await?;
writer.write_all(realname.as_bytes()).await?;
}
ServerMessageBody::N353NamesReply {
client,
@ -276,15 +276,15 @@ impl ServerMessageBody {
members,
} => {
writer.write_all(b"353 ").await?;
writer.write_all(&client).await?;
writer.write_all(client.as_bytes()).await?;
writer.write_all(b" = ").await?;
chan.write_async(writer).await?;
writer.write_all(b" :").await?;
writer.write_all(&members).await?;
writer.write_all(members.as_bytes()).await?;
}
ServerMessageBody::N366NamesReplyEnd { client, chan } => {
writer.write_all(b"366 ").await?;
writer.write_all(&client).await?;
writer.write_all(client.as_bytes()).await?;
writer.write_all(b" ").await?;
chan.write_async(writer).await?;
writer.write_all(b" :End of /NAMES list").await?;
@ -295,11 +295,11 @@ impl ServerMessageBody {
message,
} => {
writer.write_all(b"474 ").await?;
writer.write_all(&client).await?;
writer.write_all(client.as_bytes()).await?;
writer.write_all(b" ").await?;
chan.write_async(writer).await?;
writer.write_all(b" :").await?;
writer.write_all(&message).await?;
writer.write_all(message.as_bytes()).await?;
}
}
Ok(())
@ -312,7 +312,7 @@ pub enum AwayStatus {
Gone,
}
fn server_message_body(input: &[u8]) -> IResult<&[u8], ServerMessageBody> {
fn server_message_body(input: &str) -> IResult<&str, ServerMessageBody> {
alt((
server_message_body_notice,
server_message_body_ping,
@ -320,14 +320,14 @@ fn server_message_body(input: &[u8]) -> IResult<&[u8], ServerMessageBody> {
))(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, 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();
let first_target = first_target.into();
let text = text.into();
Ok((
input,
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, token) = token(input)?;
Ok((
input,
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, from) = receiver(input)?;
let (input, _) = tag(" :")(input)?;
@ -359,8 +359,8 @@ fn server_message_body_pong(input: &[u8]) -> IResult<&[u8], ServerMessageBody> {
Ok((
input,
ServerMessageBody::Pong {
from: from.to_owned(),
token: token.to_owned(),
from: from.into(),
token: token.into(),
},
))
}
@ -374,14 +374,14 @@ mod test {
#[test]
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 {
tags: vec![],
sender: None,
body: ServerMessageBody::Notice {
first_target: b"*".to_vec(),
first_target: "*".into(),
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))
.unwrap()
.unwrap();
assert_eq!(bytes, input);
assert_eq!(bytes, input.as_bytes());
}
#[test]
fn test_server_message_pong() {
let input = b"PONG server.example :LAG004911\n";
let input = "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(),
from: "server.example".into(),
token: "LAG004911".into(),
},
};
@ -414,6 +414,6 @@ mod test {
sync_future(expected.write_async(&mut bytes))
.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
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Name(pub String);
pub struct Name(pub Box<str>);
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Server(pub String);
@ -49,7 +49,7 @@ impl Jid {
.captures(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 = Server(server.as_str().to_string());
let resource = m

View File

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