forked from lavina/lavina
clean up ByteVec type aliases
This commit is contained in:
parent
55b69f4c8a
commit
4057b4a910
|
@ -27,9 +27,10 @@ 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(Box<str>);
|
pub struct PlayerId(Str);
|
||||||
impl PlayerId {
|
impl PlayerId {
|
||||||
pub fn from_bytes(bytes: Box<str>) -> Result<PlayerId> {
|
pub fn from(str: impl Into<Str>) -> Result<PlayerId> {
|
||||||
|
let bytes = str.into();
|
||||||
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"));
|
||||||
}
|
}
|
||||||
|
@ -38,10 +39,10 @@ impl PlayerId {
|
||||||
}
|
}
|
||||||
Ok(PlayerId(bytes))
|
Ok(PlayerId(bytes))
|
||||||
}
|
}
|
||||||
pub fn as_bytes(&self) -> &Box<str> {
|
pub fn as_inner(&self) -> &Str {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
pub fn into_bytes(self) -> Box<str> {
|
pub fn into_inner(self) -> Str {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +56,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: Box<str>) -> Result<()> {
|
pub async fn send_message(&mut self, room_id: RoomId, body: 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
|
||||||
|
@ -67,7 +68,7 @@ impl PlayerConnection {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn change_topic(&mut self, room_id: RoomId, new_topic: ByteVec) -> Result<()> {
|
pub async fn change_topic(&mut self, room_id: RoomId, new_topic: Str) -> Result<()> {
|
||||||
let (promise, deferred) = oneshot();
|
let (promise, deferred) = oneshot();
|
||||||
let cmd = Cmd::ChangeTopic {
|
let cmd = Cmd::ChangeTopic {
|
||||||
room_id,
|
room_id,
|
||||||
|
@ -129,7 +130,7 @@ impl PlayerHandle {
|
||||||
&self,
|
&self,
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
connection_id: ConnectionId,
|
connection_id: ConnectionId,
|
||||||
body: Box<str>,
|
body: Str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let (promise, deferred) = oneshot();
|
let (promise, deferred) = oneshot();
|
||||||
let cmd = Cmd::SendMessage {
|
let cmd = Cmd::SendMessage {
|
||||||
|
@ -186,12 +187,12 @@ pub enum Cmd {
|
||||||
},
|
},
|
||||||
SendMessage {
|
SendMessage {
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
body: Box<str>,
|
body: Str,
|
||||||
promise: Promise<()>,
|
promise: Promise<()>,
|
||||||
},
|
},
|
||||||
ChangeTopic {
|
ChangeTopic {
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
new_topic: ByteVec,
|
new_topic: Str,
|
||||||
promise: Promise<()>,
|
promise: Promise<()>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -206,12 +207,12 @@ pub enum JoinResult {
|
||||||
pub enum Updates {
|
pub enum Updates {
|
||||||
RoomTopicChanged {
|
RoomTopicChanged {
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
new_topic: ByteVec,
|
new_topic: Str,
|
||||||
},
|
},
|
||||||
NewMessage {
|
NewMessage {
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
author_id: PlayerId,
|
author_id: PlayerId,
|
||||||
body: Box<str>,
|
body: Str,
|
||||||
},
|
},
|
||||||
RoomJoined {
|
RoomJoined {
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
|
@ -288,7 +289,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("empty".into()).unwrap()]),
|
banned_from: HashSet::from([RoomId::from("empty").unwrap()]),
|
||||||
rx,
|
rx,
|
||||||
handle,
|
handle,
|
||||||
rooms,
|
rooms,
|
||||||
|
|
|
@ -16,9 +16,10 @@ use crate::{
|
||||||
|
|
||||||
/// Opaque room id
|
/// Opaque room id
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
|
||||||
pub struct RoomId(ByteVec);
|
pub struct RoomId(Str);
|
||||||
impl RoomId {
|
impl RoomId {
|
||||||
pub fn from_bytes(bytes: ByteVec) -> Result<RoomId> {
|
pub fn from(str: impl Into<Str>) -> Result<RoomId> {
|
||||||
|
let bytes = str.into();
|
||||||
if bytes.len() > 32 {
|
if bytes.len() > 32 {
|
||||||
return Err(anyhow::Error::msg(
|
return Err(anyhow::Error::msg(
|
||||||
"Room name cannot be longer than 32 symbols",
|
"Room name cannot be longer than 32 symbols",
|
||||||
|
@ -29,10 +30,10 @@ impl RoomId {
|
||||||
}
|
}
|
||||||
Ok(RoomId(bytes))
|
Ok(RoomId(bytes))
|
||||||
}
|
}
|
||||||
pub fn as_bytes(&self) -> &ByteVec {
|
pub fn as_inner(&self) -> &Str {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
pub fn into_bytes(self) -> ByteVec {
|
pub fn into_inner(self) -> Str {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,7 +113,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: Box<str>) {
|
pub async fn send_message(&self, player_id: PlayerId, body: 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;
|
||||||
}
|
}
|
||||||
|
@ -130,7 +131,7 @@ impl RoomHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_topic(&mut self, changer_id: PlayerId, new_topic: ByteVec) {
|
pub async fn set_topic(&mut self, changer_id: PlayerId, new_topic: Str) {
|
||||||
let mut lock = self.0.write().await;
|
let mut lock = self.0.write().await;
|
||||||
lock.topic = new_topic.clone();
|
lock.topic = new_topic.clone();
|
||||||
let update = Updates::RoomTopicChanged {
|
let update = Updates::RoomTopicChanged {
|
||||||
|
@ -144,7 +145,7 @@ impl RoomHandle {
|
||||||
struct Room {
|
struct Room {
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
subscriptions: HashMap<PlayerId, PlayerHandle>,
|
subscriptions: HashMap<PlayerId, PlayerHandle>,
|
||||||
topic: ByteVec,
|
topic: Str,
|
||||||
}
|
}
|
||||||
impl Room {
|
impl Room {
|
||||||
async fn add_subscriber(&mut self, player_id: PlayerId, player_handle: PlayerHandle) {
|
async fn add_subscriber(&mut self, player_id: PlayerId, player_handle: PlayerHandle) {
|
||||||
|
@ -157,7 +158,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: Box<str>) {
|
async fn send_message(&self, author_id: PlayerId, body: 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(),
|
||||||
|
@ -182,5 +183,5 @@ impl Room {
|
||||||
pub struct RoomInfo {
|
pub struct RoomInfo {
|
||||||
pub id: RoomId,
|
pub id: RoomId,
|
||||||
pub members: Vec<PlayerId>,
|
pub members: Vec<PlayerId>,
|
||||||
pub topic: ByteVec,
|
pub topic: Str,
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,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 Str = std::sync::Arc<str>;
|
||||||
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: Box<str>,
|
pub server_name: Str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct RegisteredUser {
|
struct RegisteredUser {
|
||||||
nickname: Box<str>,
|
nickname: 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: Box<str>,
|
username: Str,
|
||||||
realname: Box<str>,
|
realname: Str,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_socket(
|
async fn handle_socket(
|
||||||
|
@ -83,8 +83,8 @@ async fn handle_registration<'a>(
|
||||||
) -> Result<RegisteredUser> {
|
) -> Result<RegisteredUser> {
|
||||||
let mut buffer = vec![];
|
let mut buffer = vec![];
|
||||||
|
|
||||||
let mut future_nickname: Option<Box<str>> = None;
|
let mut future_nickname: Option<Str> = None;
|
||||||
let mut future_username: Option<(Box<str>, Box<str>)> = None;
|
let mut future_username: Option<(Str, Str)> = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let res = reader.read_until(b'\n', &mut buffer).await;
|
let res = reader.read_until(b'\n', &mut buffer).await;
|
||||||
|
@ -153,7 +153,7 @@ async fn handle_registered_socket<'a>(
|
||||||
let mut buffer = vec![];
|
let mut buffer = vec![];
|
||||||
log::info!("Handling registered user: {user:?}");
|
log::info!("Handling registered user: {user:?}");
|
||||||
|
|
||||||
let player_id = PlayerId::from_bytes(user.nickname.clone())?;
|
let player_id = PlayerId::from(user.nickname.clone())?;
|
||||||
let mut connection = players.connect_to_player(player_id.clone()).await;
|
let mut connection = players.connect_to_player(player_id.clone()).await;
|
||||||
|
|
||||||
ServerMessage {
|
ServerMessage {
|
||||||
|
@ -213,7 +213,7 @@ async fn handle_registered_socket<'a>(
|
||||||
produce_on_join_cmd_messages(
|
produce_on_join_cmd_messages(
|
||||||
&config,
|
&config,
|
||||||
&user,
|
&user,
|
||||||
&Chan::Global(room.id.as_bytes().clone()),
|
&Chan::Global(room.id.as_inner().clone()),
|
||||||
room,
|
room,
|
||||||
writer,
|
writer,
|
||||||
)
|
)
|
||||||
|
@ -280,7 +280,7 @@ async fn handle_update(
|
||||||
if player_id == &new_member_id {
|
if player_id == &new_member_id {
|
||||||
if let Some(room) = rooms.get_room(&room_id) {
|
if let Some(room) = rooms.get_room(&room_id) {
|
||||||
let room_info = room.get_room_info().await;
|
let room_info = room.get_room_info().await;
|
||||||
let chan = Chan::Global(room_id.as_bytes().clone());
|
let chan = Chan::Global(room_id.as_inner().clone());
|
||||||
produce_on_join_cmd_messages(&config, &user, &chan, &room_info, writer).await?;
|
produce_on_join_cmd_messages(&config, &user, &chan, &room_info, writer).await?;
|
||||||
writer.flush().await?;
|
writer.flush().await?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -289,8 +289,8 @@ async fn handle_update(
|
||||||
} else {
|
} else {
|
||||||
ServerMessage {
|
ServerMessage {
|
||||||
tags: vec![],
|
tags: vec![],
|
||||||
sender: Some(new_member_id.as_bytes().clone()),
|
sender: Some(new_member_id.as_inner().clone()),
|
||||||
body: ServerMessageBody::Join(Chan::Global(room_id.as_bytes().clone())),
|
body: ServerMessageBody::Join(Chan::Global(room_id.as_inner().clone())),
|
||||||
}
|
}
|
||||||
.write_async(writer)
|
.write_async(writer)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -303,8 +303,8 @@ async fn handle_update(
|
||||||
} => {
|
} => {
|
||||||
ServerMessage {
|
ServerMessage {
|
||||||
tags: vec![],
|
tags: vec![],
|
||||||
sender: Some(former_member_id.as_bytes().clone()),
|
sender: Some(former_member_id.as_inner().clone()),
|
||||||
body: ServerMessageBody::Part(Chan::Global(room_id.as_bytes().clone())),
|
body: ServerMessageBody::Part(Chan::Global(room_id.as_inner().clone())),
|
||||||
}
|
}
|
||||||
.write_async(writer)
|
.write_async(writer)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -317,9 +317,9 @@ async fn handle_update(
|
||||||
} => {
|
} => {
|
||||||
ServerMessage {
|
ServerMessage {
|
||||||
tags: vec![],
|
tags: vec![],
|
||||||
sender: Some(author_id.as_bytes().clone()),
|
sender: Some(author_id.as_inner().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_inner().clone())),
|
||||||
body: body.clone(),
|
body: body.clone(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -333,7 +333,7 @@ async fn handle_update(
|
||||||
sender: Some(config.server_name.clone()),
|
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_inner().clone()),
|
||||||
topic: new_topic,
|
topic: new_topic,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -345,8 +345,8 @@ async fn handle_update(
|
||||||
// TODO think about the case when the user was banned, but was not in the room - no need to send PART in this case
|
// TODO think about the case when the user was banned, but was not in the room - no need to send PART in this case
|
||||||
ServerMessage {
|
ServerMessage {
|
||||||
tags: vec![],
|
tags: vec![],
|
||||||
sender: Some(player_id.as_bytes().clone()),
|
sender: Some(player_id.as_inner().clone()),
|
||||||
body: ServerMessageBody::Part(Chan::Global(room_id.as_bytes().clone())),
|
body: ServerMessageBody::Part(Chan::Global(room_id.as_inner().clone())),
|
||||||
}
|
}
|
||||||
.write_async(writer)
|
.write_async(writer)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -393,7 +393,7 @@ async fn handle_incoming_message(
|
||||||
}
|
}
|
||||||
ClientMessage::PrivateMessage { recipient, body } => match recipient {
|
ClientMessage::PrivateMessage { recipient, body } => match recipient {
|
||||||
Recipient::Chan(Chan::Global(chan)) => {
|
Recipient::Chan(Chan::Global(chan)) => {
|
||||||
let room_id = RoomId::from_bytes(chan)?;
|
let room_id = RoomId::from(chan)?;
|
||||||
user_handle.send_message(room_id, body).await?;
|
user_handle.send_message(room_id, body).await?;
|
||||||
},
|
},
|
||||||
_ => log::warn!("Unsupported target type"),
|
_ => log::warn!("Unsupported target type"),
|
||||||
|
@ -401,7 +401,7 @@ async fn handle_incoming_message(
|
||||||
ClientMessage::Topic { chan, topic } => {
|
ClientMessage::Topic { chan, topic } => {
|
||||||
match chan {
|
match chan {
|
||||||
Chan::Global(chan) => {
|
Chan::Global(chan) => {
|
||||||
let room_id = RoomId::from_bytes(chan)?;
|
let room_id = RoomId::from(chan)?;
|
||||||
user_handle
|
user_handle
|
||||||
.change_topic(room_id.clone(), topic.clone())
|
.change_topic(room_id.clone(), topic.clone())
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -410,7 +410,7 @@ async fn handle_incoming_message(
|
||||||
sender: Some(config.server_name.clone()),
|
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_inner().clone()),
|
||||||
topic,
|
topic,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -447,14 +447,14 @@ async fn handle_incoming_message(
|
||||||
writer.flush().await?;
|
writer.flush().await?;
|
||||||
}
|
}
|
||||||
Recipient::Chan(Chan::Global(chan)) => {
|
Recipient::Chan(Chan::Global(chan)) => {
|
||||||
let room = rooms.get_room(&RoomId::from_bytes(chan.clone())?);
|
let room = rooms.get_room(&RoomId::from(chan.clone())?);
|
||||||
if let Some(room) = room {
|
if let Some(room) = room {
|
||||||
let room_info = room.get_room_info().await;
|
let room_info = room.get_room_info().await;
|
||||||
for member in room_info.members {
|
for member in room_info.members {
|
||||||
ServerMessage {
|
ServerMessage {
|
||||||
tags: vec![],
|
tags: vec![],
|
||||||
sender: Some(config.server_name.clone()),
|
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_inner()),
|
||||||
}
|
}
|
||||||
.write_async(writer)
|
.write_async(writer)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -519,7 +519,7 @@ async fn handle_incoming_message(
|
||||||
fn user_to_who_msg(
|
fn user_to_who_msg(
|
||||||
config: &ServerConfig,
|
config: &ServerConfig,
|
||||||
requestor: &RegisteredUser,
|
requestor: &RegisteredUser,
|
||||||
target_user_nickname: &ByteVec,
|
target_user_nickname: &Str,
|
||||||
) -> ServerMessageBody {
|
) -> ServerMessageBody {
|
||||||
// Username is equal to nickname
|
// Username is equal to nickname
|
||||||
let username = format!("~{target_user_nickname}").into();
|
let username = format!("~{target_user_nickname}").into();
|
||||||
|
@ -549,7 +549,7 @@ async fn handle_join(
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match chan {
|
match chan {
|
||||||
Chan::Global(chan_name) => {
|
Chan::Global(chan_name) => {
|
||||||
let room_id = RoomId::from_bytes(chan_name.clone())?;
|
let room_id = RoomId::from(chan_name.clone())?;
|
||||||
if let JoinResult::Success(room_info) = user_handle.join_room(room_id).await? {
|
if let JoinResult::Success(room_info) = user_handle.join_room(room_id).await? {
|
||||||
produce_on_join_cmd_messages(&config, &user, chan, &room_info, writer).await?;
|
produce_on_join_cmd_messages(&config, &user, chan, &room_info, writer).await?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -580,7 +580,7 @@ async fn handle_part(
|
||||||
writer: &mut (impl AsyncWrite + Unpin),
|
writer: &mut (impl AsyncWrite + Unpin),
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if let Chan::Global(chan_name) = chan {
|
if let Chan::Global(chan_name) = chan {
|
||||||
let room_id = RoomId::from_bytes(chan_name.clone())?;
|
let room_id = RoomId::from(chan_name.clone())?;
|
||||||
user_handle.leave_room(room_id).await?;
|
user_handle.leave_room(room_id).await?;
|
||||||
ServerMessage {
|
ServerMessage {
|
||||||
tags: vec![],
|
tags: vec![],
|
||||||
|
@ -622,13 +622,13 @@ async fn produce_on_join_cmd_messages(
|
||||||
.write_async(writer)
|
.write_async(writer)
|
||||||
.await?;
|
.await?;
|
||||||
let mut members: String = 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_inner().clone()
|
||||||
} else {
|
} else {
|
||||||
user.nickname.clone()
|
user.nickname.clone()
|
||||||
}.into();
|
}.as_ref().into();
|
||||||
for i in &room_info.members[1..] {
|
for i in &room_info.members[1..] {
|
||||||
members.push(' ');
|
members.push(' ');
|
||||||
members.push_str(i.as_bytes());
|
members.push_str(i.as_inner());
|
||||||
}
|
}
|
||||||
ServerMessage {
|
ServerMessage {
|
||||||
tags: vec![],
|
tags: vec![],
|
||||||
|
|
|
@ -253,11 +253,13 @@ 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?;
|
||||||
|
|
||||||
|
let name: Str = "darova".into();
|
||||||
Ok(Authenticated {
|
Ok(Authenticated {
|
||||||
player_id: PlayerId::from_bytes("darova".into())?,
|
player_id: PlayerId::from("darova")?,
|
||||||
xmpp_name: Name("darova".into()),
|
xmpp_name: Name(name.clone()),
|
||||||
xmpp_resource: Resource("darova".to_owned()),
|
xmpp_resource: Resource(name.clone()),
|
||||||
xmpp_muc_name: Resource("darova".to_owned()),
|
xmpp_muc_name: Resource(name),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,9 +339,9 @@ 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(room_id.into_bytes().into())),
|
name: Some(Name(room_id.into_inner().into())),
|
||||||
server: Server("rooms.localhost".into()),
|
server: Server("rooms.localhost".into()),
|
||||||
resource: Some(Resource(author_id.into_bytes().into())),
|
resource: Some(Resource(author_id.into_inner().into())),
|
||||||
}),
|
}),
|
||||||
id: None,
|
id: None,
|
||||||
r#type: xmpp::client::MessageType::Groupchat,
|
r#type: xmpp::client::MessageType::Groupchat,
|
||||||
|
@ -391,10 +393,10 @@ async fn handle_packet(
|
||||||
resource: _,
|
resource: _,
|
||||||
}) = m.to
|
}) = m.to
|
||||||
{
|
{
|
||||||
if server.0 == "rooms.localhost" && m.r#type == MessageType::Groupchat {
|
if server.0.as_ref() == "rooms.localhost" && m.r#type == MessageType::Groupchat {
|
||||||
user_handle
|
user_handle
|
||||||
.send_message(
|
.send_message(
|
||||||
RoomId::from_bytes(name.0.clone().into())?,
|
RoomId::from(name.0.clone())?,
|
||||||
m.body.clone().into(),
|
m.body.clone().into(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -446,7 +448,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())?)
|
.join_room(RoomId::from(name.0.clone())?)
|
||||||
.await?;
|
.await?;
|
||||||
Presence::<()> {
|
Presence::<()> {
|
||||||
to: Some(Jid {
|
to: Some(Jid {
|
||||||
|
@ -484,8 +486,8 @@ async fn handle_iq(output: &mut Vec<Event<'static>>, iq: Iq<IqClientBody>, rooms
|
||||||
r#type: xmpp::client::IqType::Result,
|
r#type: xmpp::client::IqType::Result,
|
||||||
body: BindResponse(Jid {
|
body: BindResponse(Jid {
|
||||||
name: Some(Name("darova".into())),
|
name: Some(Name("darova".into())),
|
||||||
server: Server("localhost".to_string()),
|
server: Server("localhost".into()),
|
||||||
resource: Some(Resource("kek".to_string())),
|
resource: Some(Resource("kek".into())),
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
req.serialize(output);
|
req.serialize(output);
|
||||||
|
@ -592,7 +594,7 @@ async fn disco_items(to: Option<&str>, req: &ItemQuery, rooms: &RoomRegistry) ->
|
||||||
vec![Item {
|
vec![Item {
|
||||||
jid: Jid {
|
jid: Jid {
|
||||||
name: None,
|
name: None,
|
||||||
server: Server("rooms.localhost".to_string()),
|
server: Server("rooms.localhost".into()),
|
||||||
resource: None,
|
resource: None,
|
||||||
},
|
},
|
||||||
name: None,
|
name: None,
|
||||||
|
@ -605,8 +607,8 @@ 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(room_info.id.into_bytes())),
|
name: Some(Name(room_info.id.into_inner())),
|
||||||
server: Server("rooms.localhost".to_string()),
|
server: Server("rooms.localhost".into()),
|
||||||
resource: None,
|
resource: None,
|
||||||
},
|
},
|
||||||
name: None,
|
name: None,
|
||||||
|
|
|
@ -9,20 +9,20 @@ pub enum ClientMessage {
|
||||||
},
|
},
|
||||||
/// PING <token>
|
/// PING <token>
|
||||||
Ping {
|
Ping {
|
||||||
token: Box<str>,
|
token: Str,
|
||||||
},
|
},
|
||||||
/// PONG <token>
|
/// PONG <token>
|
||||||
Pong {
|
Pong {
|
||||||
token: Box<str>,
|
token: Str,
|
||||||
},
|
},
|
||||||
/// NICK <nickname>
|
/// NICK <nickname>
|
||||||
Nick {
|
Nick {
|
||||||
nickname: Box<str>,
|
nickname: Str,
|
||||||
},
|
},
|
||||||
/// USER <username> 0 * :<realname>
|
/// USER <username> 0 * :<realname>
|
||||||
User {
|
User {
|
||||||
username: Box<str>,
|
username: Str,
|
||||||
realname: Box<str>,
|
realname: Str,
|
||||||
},
|
},
|
||||||
/// JOIN <chan>
|
/// JOIN <chan>
|
||||||
Join(Chan),
|
Join(Chan),
|
||||||
|
@ -37,20 +37,20 @@ pub enum ClientMessage {
|
||||||
/// TOPIC <chan> :<topic>
|
/// TOPIC <chan> :<topic>
|
||||||
Topic {
|
Topic {
|
||||||
chan: Chan,
|
chan: Chan,
|
||||||
topic: Box<str>,
|
topic: Str,
|
||||||
},
|
},
|
||||||
Part {
|
Part {
|
||||||
chan: Chan,
|
chan: Chan,
|
||||||
message: Box<str>,
|
message: Str,
|
||||||
},
|
},
|
||||||
/// PRIVMSG <target> :<msg>
|
/// PRIVMSG <target> :<msg>
|
||||||
PrivateMessage {
|
PrivateMessage {
|
||||||
recipient: Recipient,
|
recipient: Recipient,
|
||||||
body: Box<str>,
|
body: Str,
|
||||||
},
|
},
|
||||||
/// QUIT :<reason>
|
/// QUIT :<reason>
|
||||||
Quit {
|
Quit {
|
||||||
reason: Box<str>,
|
reason: Str,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ pub mod client;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
use std::io::Result;
|
use std::io::Result;
|
||||||
|
use crate::prelude::Str;
|
||||||
|
|
||||||
use nom::{
|
use nom::{
|
||||||
branch::alt,
|
branch::alt,
|
||||||
|
@ -11,12 +12,10 @@ use nom::{
|
||||||
};
|
};
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
use tokio::io::{AsyncWrite, AsyncWriteExt};
|
||||||
|
|
||||||
type ByteVec = Box<str>;
|
|
||||||
|
|
||||||
/// Single message tag value.
|
/// Single message tag value.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
key: ByteVec,
|
key: Str,
|
||||||
value: Option<u8>,
|
value: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,9 +30,9 @@ fn token(input: &str) -> IResult<&str, &str> {
|
||||||
#[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(Box<str>),
|
Global(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(Box<str>),
|
Local(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<()> {
|
||||||
|
@ -69,7 +68,7 @@ fn chan(input: &str) -> IResult<&str, Chan> {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Recipient {
|
pub enum Recipient {
|
||||||
Nick(ByteVec),
|
Nick(Str),
|
||||||
Chan(Chan),
|
Chan(Chan),
|
||||||
}
|
}
|
||||||
impl Recipient {
|
impl Recipient {
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub struct ServerMessage {
|
||||||
/// Optional tags section, prefixed with `@`
|
/// Optional tags section, prefixed with `@`
|
||||||
pub tags: Vec<Tag>,
|
pub tags: Vec<Tag>,
|
||||||
/// Optional server name, prefixed with `:`.
|
/// Optional server name, prefixed with `:`.
|
||||||
pub sender: Option<ByteVec>,
|
pub sender: Option<Str>,
|
||||||
pub body: ServerMessageBody,
|
pub body: ServerMessageBody,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,93 +44,93 @@ pub fn server_message(input: &str) -> IResult<&str, ServerMessage> {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum ServerMessageBody {
|
pub enum ServerMessageBody {
|
||||||
Notice {
|
Notice {
|
||||||
first_target: ByteVec,
|
first_target: Str,
|
||||||
rest_targets: Vec<ByteVec>,
|
rest_targets: Vec<Str>,
|
||||||
text: ByteVec,
|
text: Str,
|
||||||
},
|
},
|
||||||
Ping {
|
Ping {
|
||||||
token: ByteVec,
|
token: Str,
|
||||||
},
|
},
|
||||||
Pong {
|
Pong {
|
||||||
from: ByteVec,
|
from: Str,
|
||||||
token: ByteVec,
|
token: Str,
|
||||||
},
|
},
|
||||||
PrivateMessage {
|
PrivateMessage {
|
||||||
target: Recipient,
|
target: Recipient,
|
||||||
body: ByteVec,
|
body: Str,
|
||||||
},
|
},
|
||||||
Join(Chan),
|
Join(Chan),
|
||||||
Part(Chan),
|
Part(Chan),
|
||||||
Error {
|
Error {
|
||||||
reason: ByteVec,
|
reason: Str,
|
||||||
},
|
},
|
||||||
N001Welcome {
|
N001Welcome {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
text: ByteVec,
|
text: Str,
|
||||||
},
|
},
|
||||||
N002YourHost {
|
N002YourHost {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
text: ByteVec,
|
text: Str,
|
||||||
},
|
},
|
||||||
N003Created {
|
N003Created {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
text: ByteVec,
|
text: Str,
|
||||||
},
|
},
|
||||||
N004MyInfo {
|
N004MyInfo {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
hostname: ByteVec,
|
hostname: Str,
|
||||||
softname: ByteVec,
|
softname: Str,
|
||||||
// TODO user modes, channel modes, channel modes with a parameter
|
// TODO user modes, channel modes, channel modes with a parameter
|
||||||
},
|
},
|
||||||
N005ISupport {
|
N005ISupport {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
params: ByteVec, // TODO make this a datatype
|
params: Str, // TODO make this a datatype
|
||||||
},
|
},
|
||||||
/// Reply to a client's [Mode](crate::protos::irc::client::ClientMessage::Mode) request.
|
/// Reply to a client's [Mode](crate::protos::irc::client::ClientMessage::Mode) request.
|
||||||
N221UserModeIs {
|
N221UserModeIs {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
modes: ByteVec,
|
modes: Str,
|
||||||
},
|
},
|
||||||
/// Final reply to a client's [Who](crate::protos::irc::client::ClientMessage::Who) request.
|
/// Final reply to a client's [Who](crate::protos::irc::client::ClientMessage::Who) request.
|
||||||
N315EndOfWho {
|
N315EndOfWho {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
mask: Recipient,
|
mask: Recipient,
|
||||||
/// Usually `b"End of WHO list"`
|
/// Usually `b"End of WHO list"`
|
||||||
msg: ByteVec,
|
msg: Str,
|
||||||
},
|
},
|
||||||
N332Topic {
|
N332Topic {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
chat: Chan,
|
chat: Chan,
|
||||||
topic: ByteVec,
|
topic: Str,
|
||||||
},
|
},
|
||||||
/// A reply to a client's [Who](crate::protos::irc::client::ClientMessage::Who) request.
|
/// A reply to a client's [Who](crate::protos::irc::client::ClientMessage::Who) request.
|
||||||
N352WhoReply {
|
N352WhoReply {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
// chan = *
|
// chan = *
|
||||||
username: ByteVec,
|
username: Str,
|
||||||
/// User's hostname
|
/// User's hostname
|
||||||
host: ByteVec,
|
host: Str,
|
||||||
/// Hostname of the server the user is connected to
|
/// Hostname of the server the user is connected to
|
||||||
server: ByteVec,
|
server: Str,
|
||||||
nickname: ByteVec,
|
nickname: Str,
|
||||||
/// Flags
|
/// Flags
|
||||||
flags: AwayStatus,
|
flags: AwayStatus,
|
||||||
hops: u8,
|
hops: u8,
|
||||||
realname: ByteVec,
|
realname: Str,
|
||||||
},
|
},
|
||||||
N353NamesReply {
|
N353NamesReply {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
chan: Chan,
|
chan: Chan,
|
||||||
members: ByteVec, // TODO make this a non-empty list with prefixes
|
members: Str, // TODO make this a non-empty list with prefixes
|
||||||
},
|
},
|
||||||
N366NamesReplyEnd {
|
N366NamesReplyEnd {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
chan: Chan,
|
chan: Chan,
|
||||||
},
|
},
|
||||||
N474BannedFromChan {
|
N474BannedFromChan {
|
||||||
client: ByteVec,
|
client: Str,
|
||||||
chan: Chan,
|
chan: Chan,
|
||||||
message: ByteVec,
|
message: Str,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,13 @@ 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 Box<str>);
|
pub struct Name(pub Str);
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
pub struct Server(pub String);
|
pub struct Server(pub Str);
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
pub struct Resource(pub String);
|
pub struct Resource(pub Str);
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||||
pub struct Jid {
|
pub struct Jid {
|
||||||
|
@ -51,10 +51,10 @@ impl Jid {
|
||||||
|
|
||||||
let name = m.get(2).map(|name| Name(name.as_str().into()));
|
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().into());
|
||||||
let resource = m
|
let resource = m
|
||||||
.get(5)
|
.get(5)
|
||||||
.map(|resource| Resource(resource.as_str().to_string()));
|
.map(|resource| Resource(resource.as_str().into()));
|
||||||
|
|
||||||
Ok(Jid {
|
Ok(Jid {
|
||||||
name,
|
name,
|
||||||
|
@ -101,6 +101,7 @@ impl FromXml for BindRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO rewrite as a generator
|
||||||
impl Parser for BindRequestParser {
|
impl Parser for BindRequestParser {
|
||||||
type Output = Result<BindRequest>;
|
type Output = Result<BindRequest>;
|
||||||
|
|
||||||
|
@ -138,7 +139,7 @@ impl Parser for BindRequestParser {
|
||||||
let Some(resource) = resource else {
|
let Some(resource) = resource else {
|
||||||
return Continuation::Final(Err(ffail!("No resource was provided")));
|
return Continuation::Final(Err(ffail!("No resource was provided")));
|
||||||
};
|
};
|
||||||
Continuation::Final(Ok(BindRequest(Resource(resource))))
|
Continuation::Final(Ok(BindRequest(Resource(resource.into()))))
|
||||||
}
|
}
|
||||||
_ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))),
|
_ => Continuation::Final(Err(ffail!("Unexpected XML event: {event:?}"))),
|
||||||
},
|
},
|
||||||
|
@ -208,7 +209,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(result, BindRequest(Resource("mobile".to_string())),)
|
assert_eq!(result, BindRequest(Resource("mobile".into())),)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -17,10 +17,10 @@ pub struct Message {
|
||||||
pub to: Option<Jid>,
|
pub to: Option<Jid>,
|
||||||
// default is Normal
|
// default is Normal
|
||||||
pub r#type: MessageType,
|
pub r#type: MessageType,
|
||||||
pub lang: Option<String>,
|
pub lang: Option<Str>,
|
||||||
|
|
||||||
pub subject: Option<String>,
|
pub subject: Option<Str>,
|
||||||
pub body: String,
|
pub body: Str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromXmlTag for Message {
|
impl FromXmlTag for Message {
|
||||||
|
@ -53,9 +53,9 @@ struct MessageParserState {
|
||||||
id: Option<String>,
|
id: Option<String>,
|
||||||
to: Option<Jid>,
|
to: Option<Jid>,
|
||||||
r#type: MessageType,
|
r#type: MessageType,
|
||||||
lang: Option<String>,
|
lang: Option<Str>,
|
||||||
subject: Option<String>,
|
subject: Option<Str>,
|
||||||
body: Option<String>,
|
body: Option<Str>,
|
||||||
}
|
}
|
||||||
impl Parser for MessageParser {
|
impl Parser for MessageParser {
|
||||||
type Output = Result<Message>;
|
type Output = Result<Message>;
|
||||||
|
@ -124,7 +124,7 @@ impl Parser for MessageParser {
|
||||||
InSubject(mut state) => match event {
|
InSubject(mut state) => match event {
|
||||||
Event::Text(ref bytes) => {
|
Event::Text(ref bytes) => {
|
||||||
let subject = fail_fast!(std::str::from_utf8(&*bytes));
|
let subject = fail_fast!(std::str::from_utf8(&*bytes));
|
||||||
state.subject = Some(subject.to_string());
|
state.subject = Some(subject.into());
|
||||||
Continuation::Continue(InSubject(state).into())
|
Continuation::Continue(InSubject(state).into())
|
||||||
}
|
}
|
||||||
Event::End(_) => Continuation::Continue(Outer(state).into()),
|
Event::End(_) => Continuation::Continue(Outer(state).into()),
|
||||||
|
@ -133,7 +133,7 @@ impl Parser for MessageParser {
|
||||||
InBody(mut state) => match event {
|
InBody(mut state) => match event {
|
||||||
Event::Text(ref bytes) => match std::str::from_utf8(&*bytes) {
|
Event::Text(ref bytes) => match std::str::from_utf8(&*bytes) {
|
||||||
Ok(subject) => {
|
Ok(subject) => {
|
||||||
state.body = Some(subject.to_string());
|
state.body = Some(subject.into());
|
||||||
Continuation::Continue(InBody(state).into())
|
Continuation::Continue(InBody(state).into())
|
||||||
}
|
}
|
||||||
Err(err) => Continuation::Final(Err(err.into())),
|
Err(err) => Continuation::Final(Err(err.into())),
|
||||||
|
@ -614,13 +614,13 @@ mod tests {
|
||||||
id: Some("aacea".to_string()),
|
id: Some("aacea".to_string()),
|
||||||
to: Some(Jid {
|
to: Some(Jid {
|
||||||
name: Some(Name("nikita".into())),
|
name: Some(Name("nikita".into())),
|
||||||
server: Server("vlnv.dev".to_owned()),
|
server: Server("vlnv.dev".into()),
|
||||||
resource: None
|
resource: None
|
||||||
}),
|
}),
|
||||||
r#type: MessageType::Chat,
|
r#type: MessageType::Chat,
|
||||||
lang: None,
|
lang: None,
|
||||||
subject: Some("daa".to_string()),
|
subject: Some("daa".into()),
|
||||||
body: "bbb".to_string(),
|
body: "bbb".into(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -655,7 +655,7 @@ mod tests {
|
||||||
id: "bind_1".to_string(),
|
id: "bind_1".to_string(),
|
||||||
to: None,
|
to: None,
|
||||||
r#type: IqType::Set,
|
r#type: IqType::Set,
|
||||||
body: BindRequest(Resource("mobile".to_string()))
|
body: BindRequest(Resource("mobile".into()))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue