mirror of
https://gitlab.com/xmpp-rs/xmpp-rs.git
synced 2024-07-12 22:21:53 +00:00
Add Room, RoomNick and RoomParticipant types for more safe/correct API
This commit is contained in:
parent
76b68e932a
commit
bd3dcc3532
6 changed files with 168 additions and 24 deletions
|
@ -433,8 +433,13 @@ impl FullJid {
|
|||
self.inner.domain()
|
||||
}
|
||||
|
||||
/// The resource part of the full JID, as a [`ResourcePart`].
|
||||
pub fn resource(&self) -> ResourcePart {
|
||||
ResourcePart::new_unchecked(self.inner.resource().unwrap())
|
||||
}
|
||||
|
||||
/// The optional resource of the Jabber ID. Since this is a full JID it is always present.
|
||||
pub fn resource(&self) -> &str {
|
||||
pub fn resource_str(&self) -> &str {
|
||||
self.inner.resource().unwrap()
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#![warn(missing_docs)]
|
||||
|
||||
pub use crate::util::error::Error;
|
||||
pub use jid;
|
||||
pub use jid::{BareJid, Error as JidParseError, FullJid, Jid};
|
||||
pub use minidom::Element;
|
||||
|
||||
|
|
|
@ -67,16 +67,16 @@ async fn main() -> Result<(), Option<()>> {
|
|||
)
|
||||
.await;
|
||||
}
|
||||
Event::LeaveRoom(jid) => {
|
||||
Event::LeaveRoom(room) => {
|
||||
println!("Leaving room {}…", jid);
|
||||
}
|
||||
Event::LeaveAllRooms => {
|
||||
println!("Leaving all rooms…");
|
||||
}
|
||||
Event::RoomJoined(jid) => {
|
||||
Event::RoomJoined(room) => {
|
||||
println!("Joined room {}.", jid);
|
||||
client
|
||||
.send_message(Jid::Bare(jid), MessageType::Groupchat, "en", "Hello world!")
|
||||
.send_message(Jid::Bare(jid.bare().clone()), MessageType::Groupchat, "en", "Hello world!")
|
||||
.await;
|
||||
}
|
||||
Event::RoomLeft(jid) => {
|
||||
|
|
|
@ -39,6 +39,8 @@ use xmpp_parsers::{
|
|||
extern crate log;
|
||||
|
||||
mod pubsub;
|
||||
mod room;
|
||||
pub use room::{Room, RoomNick, RoomParticipant};
|
||||
|
||||
pub type Error = tokio_xmpp::Error;
|
||||
|
||||
|
@ -72,7 +74,6 @@ pub enum ClientFeature {
|
|||
}
|
||||
|
||||
pub type Id = Option<String>;
|
||||
pub type RoomNick = String;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
|
@ -84,15 +85,22 @@ pub enum Event {
|
|||
#[cfg(feature = "avatars")]
|
||||
AvatarRetrieved(Jid, String),
|
||||
ChatMessage(Id, BareJid, Body),
|
||||
JoinRoom(BareJid, Conference),
|
||||
LeaveRoom(BareJid),
|
||||
/// Received a bookmark from the server about a specific Room, with information
|
||||
/// about autojoin settings.
|
||||
JoinRoom(Room, Conference),
|
||||
/// The client left a Room (TODO: only this client or any client?)
|
||||
LeaveRoom(Room),
|
||||
/// The client is leaving all rooms (TODO: is this received when another client asks to leave all rooms?)
|
||||
LeaveAllRooms,
|
||||
RoomJoined(BareJid),
|
||||
RoomLeft(BareJid),
|
||||
RoomMessage(Id, BareJid, RoomNick, Body),
|
||||
/// You just joined a room (TODO: on this client or any client?)
|
||||
RoomJoined(Room),
|
||||
/// You just left a room (TODO: on this client or any client ?)
|
||||
RoomLeft(Room),
|
||||
/// Received a message from a Room, by a person identified by their RoomNick.
|
||||
RoomMessage(Id, RoomParticipant, Body),
|
||||
/// A private message received from a room, containing the message ID, the room's BareJid,
|
||||
/// the sender's nickname, and the message body.
|
||||
RoomPrivateMessage(Id, BareJid, RoomNick, Body),
|
||||
RoomPrivateMessage(Id, Room, RoomNick, Body),
|
||||
ServiceMessage(Id, BareJid, Body),
|
||||
HttpUploadedFile(String),
|
||||
}
|
||||
|
@ -221,7 +229,7 @@ impl Agent {
|
|||
|
||||
pub async fn join_room(
|
||||
&mut self,
|
||||
room: BareJid,
|
||||
room: Room,
|
||||
nick: Option<String>,
|
||||
password: Option<String>,
|
||||
lang: &str,
|
||||
|
@ -233,7 +241,7 @@ impl Agent {
|
|||
}
|
||||
|
||||
let nick = nick.unwrap_or_else(|| self.default_nick.read().unwrap().clone());
|
||||
let room_jid = room.with_resource_str(&nick).unwrap();
|
||||
let room_jid = room.bare().with_resource_str(&nick).unwrap();
|
||||
let mut presence = Presence::new(PresenceType::None).with_to(room_jid);
|
||||
presence.add_payload(muc);
|
||||
presence.set_status(String::from(lang), String::from(status));
|
||||
|
@ -262,7 +270,7 @@ impl Agent {
|
|||
lang: &str,
|
||||
text: &str,
|
||||
) {
|
||||
let recipient: Jid = room.with_resource_str(&recipient).unwrap().into();
|
||||
let recipient: Jid = room.with_resource(recipient.resource()).into();
|
||||
let mut message = Message::new(recipient).with_payload(MucUser::new());
|
||||
message.type_ = MessageType::Chat;
|
||||
message
|
||||
|
@ -367,8 +375,10 @@ impl Agent {
|
|||
let event = match from.clone() {
|
||||
Jid::Full(full) => Event::RoomMessage(
|
||||
message.id.clone(),
|
||||
from.to_bare(),
|
||||
full.resource().to_owned(),
|
||||
RoomParticipant::new(
|
||||
&Room::from_bare_dangerous(&from.to_bare()),
|
||||
&RoomNick::from_resource_dangerous(&full.resource()),
|
||||
),
|
||||
body.clone(),
|
||||
),
|
||||
Jid::Bare(bare) => {
|
||||
|
@ -390,8 +400,8 @@ impl Agent {
|
|||
}
|
||||
Jid::Full(full) => Event::RoomPrivateMessage(
|
||||
message.id.clone(),
|
||||
full.to_bare(),
|
||||
full.resource().to_owned(),
|
||||
Room::from_bare_dangerous(&full.to_bare()),
|
||||
RoomNick::from_resource_dangerous(&full.resource()),
|
||||
body.clone(),
|
||||
),
|
||||
};
|
||||
|
@ -431,7 +441,7 @@ impl Agent {
|
|||
};
|
||||
for status in muc_user.status.into_iter() {
|
||||
if status == Status::SelfPresence {
|
||||
events.push(Event::RoomJoined(from.clone()));
|
||||
events.push(Event::RoomJoined(Room::from_bare_dangerous(&from)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
use super::Agent;
|
||||
use crate::Event;
|
||||
use crate::{Event, Room};
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
use xmpp_parsers::{
|
||||
|
@ -41,9 +41,9 @@ pub(crate) async fn handle_event(from: &Jid, elem: Element, agent: &mut Agent) -
|
|||
match Conference::try_from(payload) {
|
||||
Ok(conference) => {
|
||||
if conference.autojoin == Autojoin::True {
|
||||
events.push(Event::JoinRoom(jid, conference));
|
||||
events.push(Event::JoinRoom(Room::from_bare_dangerous(&jid), conference));
|
||||
} else {
|
||||
events.push(Event::LeaveRoom(jid));
|
||||
events.push(Event::LeaveRoom(Room::from_bare_dangerous(&jid)));
|
||||
}
|
||||
}
|
||||
Err(err) => println!("not bookmark: {}", err),
|
||||
|
@ -59,7 +59,7 @@ pub(crate) async fn handle_event(from: &Jid, elem: Element, agent: &mut Agent) -
|
|||
assert_eq!(items.len(), 1);
|
||||
let item = items.clone().pop().unwrap();
|
||||
let jid = BareJid::from_str(&item.0).unwrap();
|
||||
events.push(Event::LeaveRoom(jid));
|
||||
events.push(Event::LeaveRoom(Room::from_bare_dangerous(&jid)));
|
||||
}
|
||||
ref node => unimplemented!("node {}", node),
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ pub(crate) fn handle_iq_result(from: &Jid, elem: Element) -> impl IntoIterator<I
|
|||
match Conference::try_from(payload) {
|
||||
Ok(conference) => {
|
||||
if let Autojoin::True = conference.autojoin {
|
||||
events.push(Event::JoinRoom(jid, conference));
|
||||
events.push(Event::JoinRoom(Room::from_bare_dangerous(&jid), conference));
|
||||
}
|
||||
}
|
||||
Err(err) => panic!("Wrong payload type in bookmarks 2 item: {}", err),
|
||||
|
|
128
xmpp/src/room.rs
Normal file
128
xmpp/src/room.rs
Normal file
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) 2019 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#![deny(bare_trait_objects)]
|
||||
|
||||
use xmpp_parsers::{BareJid, FullJid};
|
||||
use xmpp_parsers::jid::ResourcePart;
|
||||
use std::fmt;
|
||||
//use crate::Error;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Room(BareJid);
|
||||
|
||||
impl AsRef<BareJid> for Room {
|
||||
fn as_ref(&self) -> &BareJid {
|
||||
self.bare()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Room {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl Room {
|
||||
/// Builds a [`Room`] bare JID from any [`BareJid`].
|
||||
/// Make sure that bare JID is actually a room before using!
|
||||
pub fn from_bare_dangerous(jid: &BareJid) -> Room {
|
||||
Room(jid.to_owned())
|
||||
}
|
||||
|
||||
/// Returns a reference to the inner [`BareJid`] for this room.
|
||||
pub fn bare(&self) -> &BareJid {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns the full JID of a room participant as a [`RoomParticipant`] instance.
|
||||
/// TODO: Dangerous? See note about RoomNick type
|
||||
pub fn participant(&self, nick: &RoomNick) -> RoomParticipant {
|
||||
RoomParticipant::new(&self, nick)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct RoomParticipant(FullJid);
|
||||
|
||||
impl AsRef<FullJid> for RoomParticipant {
|
||||
fn as_ref(&self) -> &FullJid {
|
||||
self.full()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RoomParticipant {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl RoomParticipant {
|
||||
/// Builds a [`RoomParticipant`] from a typed [`Room`] and [`RoomNick`].
|
||||
pub fn new(room: &Room, nick: &RoomNick) -> RoomParticipant {
|
||||
RoomParticipant::from_full_dangerous(
|
||||
&room.bare().with_resource(nick.resource())
|
||||
)
|
||||
}
|
||||
|
||||
/// Builds a [`RoomParticipant`] bare JID from any [`BareJid`].
|
||||
/// Make sure that bare JID is actually a room before using!
|
||||
pub fn from_full_dangerous(jid: &FullJid) -> RoomParticipant {
|
||||
RoomParticipant(jid.to_owned())
|
||||
}
|
||||
|
||||
/// Returns a reference to the inner [`FullJid`] for this room.
|
||||
pub fn full(&self) -> &FullJid {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns an owned [`Room`] the [`RoomParticipant`] is in.
|
||||
pub fn room(&self) -> Room {
|
||||
Room::from_bare_dangerous(&self.0.to_bare())
|
||||
}
|
||||
|
||||
/// Returns an owned [`RoomNick`] for the [`RoomParticipant`].
|
||||
pub fn nick(&self) -> RoomNick {
|
||||
RoomNick::from_resource_dangerous(&self.0.resource())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Do we really really want to have a RoomNick type? This makes it possible to mix nicks from different
|
||||
// rooms. RoomParticipant on the other hand, by having an inner FullJid, deeply associated those two infos.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct RoomNick(ResourcePart);
|
||||
|
||||
impl AsRef<ResourcePart> for RoomNick {
|
||||
fn as_ref(&self) -> &ResourcePart {
|
||||
self.resource()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RoomNick {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl RoomNick {
|
||||
/// Builds a [`RoomParticipant`] bare JID from any [`BareJid`].
|
||||
/// Make sure that bare JID is actually a room before using!
|
||||
pub fn from_resource_dangerous(resource: &ResourcePart) -> RoomNick {
|
||||
RoomNick(resource.to_owned())
|
||||
}
|
||||
|
||||
/// Returns a reference to the inner [`ResourcePart`] for this nick.
|
||||
pub fn resource(&self) -> &ResourcePart {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns an owned [`RoomParticipant`] for this nick in a specific [`Room`].
|
||||
/// TODO: Dangerous? See note about RoomNick type
|
||||
pub fn in_room(&self, room: &Room) -> RoomParticipant {
|
||||
room.participant(self)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue