forked from lavina/lavina
rewrite irc projection to use str
This commit is contained in:
parent
d0c579863e
commit
55b69f4c8a
|
@ -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,
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(¶ms).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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}),
|
}),
|
||||||
|
|
Loading…
Reference in New Issue