tokio_xmpp::AsyncClient and xmpp::Agent take a fully parsed Jid (#72)

This commit is contained in:
xmppftw 2023-06-01 11:58:04 +02:00
parent 7064ef5c17
commit 209bab1441
8 changed files with 56 additions and 36 deletions

3
tokio-xmpp/ChangeLog Normal file
View file

@ -0,0 +1,3 @@
xxxxxxxxxx
* Breaking changes:
- AsyncClient::new takes a parsed Jid instead of string (#72)

View file

@ -2,13 +2,14 @@ use futures::stream::StreamExt;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::env::args; use std::env::args;
use std::process::exit; use std::process::exit;
use std::str::FromStr;
use tokio_xmpp::AsyncClient as Client; use tokio_xmpp::AsyncClient as Client;
use xmpp_parsers::{ use xmpp_parsers::{
disco::{DiscoInfoQuery, DiscoInfoResult}, disco::{DiscoInfoQuery, DiscoInfoResult},
iq::{Iq, IqType}, iq::{Iq, IqType},
ns, ns,
server_info::ServerInfo, server_info::ServerInfo,
Element, Jid, BareJid, Element, Jid,
}; };
#[tokio::main] #[tokio::main]
@ -18,12 +19,12 @@ async fn main() {
println!("Usage: {} <jid> <password> <target>", args[0]); println!("Usage: {} <jid> <password> <target>", args[0]);
exit(1); exit(1);
} }
let jid = &args[1]; let jid = BareJid::from_str(&args[1]).expect(&format!("Invalid JID: {}", &args[1]));
let password = args[2].clone(); let password = args[2].clone();
let target = &args[3]; let target = &args[3];
// Client instance // Client instance
let mut client = Client::new(jid, password).unwrap(); let mut client = Client::new(jid, password);
// Main loop, processes events // Main loop, processes events
let mut wait_for_stream_end = false; let mut wait_for_stream_end = false;

View file

@ -22,7 +22,7 @@ use xmpp_parsers::{
NodeName, NodeName,
}, },
stanza_error::{DefinedCondition, ErrorType, StanzaError}, stanza_error::{DefinedCondition, ErrorType, StanzaError},
Element, Jid, BareJid, Element, Jid,
}; };
#[tokio::main] #[tokio::main]
@ -32,11 +32,11 @@ async fn main() {
println!("Usage: {} <jid> <password>", args[0]); println!("Usage: {} <jid> <password>", args[0]);
exit(1); exit(1);
} }
let jid = &args[1]; let jid = BareJid::from_str(&args[1]).expect(&format!("Invalid JID: {}", &args[1]));
let password = args[2].clone(); let password = args[2].clone();
// Client instance // Client instance
let mut client = Client::new(jid, password).unwrap(); let mut client = Client::new(jid.clone(), password);
let disco_info = make_disco(); let disco_info = make_disco();
@ -94,7 +94,7 @@ async fn main() {
} else if let IqType::Result(Some(payload)) = iq.payload { } else if let IqType::Result(Some(payload)) = iq.payload {
if payload.is("pubsub", ns::PUBSUB) { if payload.is("pubsub", ns::PUBSUB) {
let pubsub = PubSub::try_from(payload).unwrap(); let pubsub = PubSub::try_from(payload).unwrap();
let from = iq.from.clone().unwrap_or(Jid::from_str(jid).unwrap()); let from = iq.from.clone().unwrap_or(jid.clone().into());
handle_iq_result(pubsub, &from); handle_iq_result(pubsub, &from);
} }
} else if let IqType::Set(_) = iq.payload { } else if let IqType::Set(_) = iq.payload {

View file

@ -2,11 +2,12 @@ use futures::stream::StreamExt;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::env::args; use std::env::args;
use std::process::exit; use std::process::exit;
use std::str::FromStr;
use tokio; use tokio;
use tokio_xmpp::AsyncClient as Client; use tokio_xmpp::AsyncClient as Client;
use xmpp_parsers::message::{Body, Message, MessageType}; use xmpp_parsers::message::{Body, Message, MessageType};
use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType}; use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType};
use xmpp_parsers::{Element, Jid}; use xmpp_parsers::{BareJid, Element, Jid};
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@ -15,11 +16,11 @@ async fn main() {
println!("Usage: {} <jid> <password>", args[0]); println!("Usage: {} <jid> <password>", args[0]);
exit(1); exit(1);
} }
let jid = &args[1]; let jid = BareJid::from_str(&args[1]).expect(&format!("Invalid JID: {}", &args[1]));
let password = &args[2]; let password = &args[2];
// Client instance // Client instance
let mut client = Client::new(jid, password.to_owned()).unwrap(); let mut client = Client::new(jid, password.to_owned());
client.set_reconnect(true); client.set_reconnect(true);
// Main loop, processes events // Main loop, processes events

View file

@ -2,7 +2,6 @@ use futures::{sink::SinkExt, task::Poll, Future, Sink, Stream};
use sasl::common::{ChannelBinding, Credentials}; use sasl::common::{ChannelBinding, Credentials};
use std::mem::replace; use std::mem::replace;
use std::pin::Pin; use std::pin::Pin;
use std::str::FromStr;
use std::task::Context; use std::task::Context;
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
@ -10,7 +9,7 @@ use tokio::task::JoinHandle;
use tokio_native_tls::TlsStream; use tokio_native_tls::TlsStream;
#[cfg(feature = "tls-rust")] #[cfg(feature = "tls-rust")]
use tokio_rustls::client::TlsStream; use tokio_rustls::client::TlsStream;
use xmpp_parsers::{ns, Element, Jid, JidParseError}; use xmpp_parsers::{ns, Element, Jid};
use super::auth::auth; use super::auth::auth;
use super::bind::bind; use super::bind::bind;
@ -74,15 +73,13 @@ impl Client {
/// ///
/// Start polling the returned instance so that it will connect /// Start polling the returned instance so that it will connect
/// and yield events. /// and yield events.
pub fn new<P: Into<String>>(jid: &str, password: P) -> Result<Self, JidParseError> { pub fn new<J: Into<Jid>, P: Into<String>>(jid: J, password: P) -> Self {
let jid = Jid::from_str(jid)?;
let config = Config { let config = Config {
jid: jid.clone(), jid: jid.into(),
password: password.into(), password: password.into(),
server: ServerConfig::UseSrv, server: ServerConfig::UseSrv,
}; };
let client = Self::new_with_config(config); Self::new_with_config(config)
Ok(client)
} }
/// Start a new client given that the JID is already parsed. /// Start a new client given that the JID is already parsed.

View file

@ -1,7 +1,10 @@
Unreleased: Unreleased:
xxxxxxxxxx xxxxxxxxxx
* Breaking changes:
- ClientBuilder::new takes a parsed BareJid instead of string (#72)
* Improvements: * Improvements:
- Agent is now Send, by replacing Rc with Arc and RefCell with RwLock (#64) - Agent is now Send, by replacing Rc with Arc and RefCell with RwLock (#64)
- ClientBuilder now has a set_resource method for manual resource management (#72)
Version 0.4.0: Version 0.4.0:
2023-05-18 [ Maxime “pep” Buquet <pep@bouah.net>, Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> ] 2023-05-18 [ Maxime “pep” Buquet <pep@bouah.net>, Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> ]

View file

@ -6,8 +6,9 @@
use env_logger; use env_logger;
use std::env::args; use std::env::args;
use std::str::FromStr;
use xmpp::{ClientBuilder, ClientFeature, ClientType, Event}; use xmpp::{ClientBuilder, ClientFeature, ClientType, Event};
use xmpp_parsers::{message::MessageType, Jid}; use xmpp_parsers::{message::MessageType, BareJid, Jid};
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Option<()>> { async fn main() -> Result<(), Option<()>> {
@ -18,7 +19,8 @@ async fn main() -> Result<(), Option<()>> {
println!("Usage: {} <jid> <password>", args[0]); println!("Usage: {} <jid> <password>", args[0]);
return Err(None); return Err(None);
} }
let jid = &args[1];
let jid = BareJid::from_str(&args[1]).expect(&format!("Invalid JID: {}", &args[1]));
let password = &args[2]; let password = &args[2];
// Client instance // Client instance
@ -29,8 +31,7 @@ async fn main() -> Result<(), Option<()>> {
.enable_feature(ClientFeature::Avatars) .enable_feature(ClientFeature::Avatars)
.enable_feature(ClientFeature::ContactList) .enable_feature(ClientFeature::ContactList)
.enable_feature(ClientFeature::JoinRooms) .enable_feature(ClientFeature::JoinRooms)
.build() .build();
.unwrap();
while let Some(events) = client.wait_for_events().await { while let Some(events) = client.wait_for_events().await {
for event in events { for event in events {

View file

@ -94,19 +94,19 @@ pub enum Event {
HttpUploadedFile(String), HttpUploadedFile(String),
} }
#[derive(Default)]
pub struct ClientBuilder<'a> { pub struct ClientBuilder<'a> {
jid: &'a str, jid: BareJid,
password: &'a str, password: &'a str,
website: String, website: String,
default_nick: String, default_nick: String,
lang: Vec<String>, lang: Vec<String>,
disco: (ClientType, String), disco: (ClientType, String),
features: Vec<ClientFeature>, features: Vec<ClientFeature>,
resource: Option<String>,
} }
impl ClientBuilder<'_> { impl ClientBuilder<'_> {
pub fn new<'a>(jid: &'a str, password: &'a str) -> ClientBuilder<'a> { pub fn new<'a>(jid: BareJid, password: &'a str) -> ClientBuilder<'a> {
ClientBuilder { ClientBuilder {
jid, jid,
password, password,
@ -115,9 +115,16 @@ impl ClientBuilder<'_> {
lang: vec![String::from("en")], lang: vec![String::from("en")],
disco: (ClientType::default(), String::from("tokio-xmpp")), disco: (ClientType::default(), String::from("tokio-xmpp")),
features: vec![], features: vec![],
resource: None,
} }
} }
/// Optionally set a resource associated to this device on the client
pub fn set_resource(mut self, resource: &str) -> Self {
self.resource = Some(resource.to_string());
self
}
pub fn set_client(mut self, type_: ClientType, name: &str) -> Self { pub fn set_client(mut self, type_: ClientType, name: &str) -> Self {
self.disco = (type_, String::from(name)); self.disco = (type_, String::from(name));
self self
@ -168,26 +175,30 @@ impl ClientBuilder<'_> {
} }
} }
pub fn build(self) -> Result<Agent, Error> { pub fn build(self) -> Agent {
let client = TokioXmppClient::new(self.jid, self.password)?; let jid: Jid = if let Some(resource) = &self.resource {
Ok(self.build_impl(client)?) self.jid.clone().with_resource(resource.to_string()).into()
} else {
self.jid.clone().into()
};
let client = TokioXmppClient::new(jid, self.password);
self.build_impl(client)
} }
// This function is meant to be used for testing build // This function is meant to be used for testing build
pub(crate) fn build_impl(self, client: TokioXmppClient) -> Result<Agent, Error> { pub(crate) fn build_impl(self, client: TokioXmppClient) -> Agent {
let disco = self.make_disco(); let disco = self.make_disco();
let node = self.website; let node = self.website;
let agent = Agent { Agent {
client, client,
default_nick: Arc::new(RwLock::new(self.default_nick)), default_nick: Arc::new(RwLock::new(self.default_nick)),
lang: Arc::new(self.lang), lang: Arc::new(self.lang),
disco, disco,
node, node,
uploads: Vec::new(), uploads: Vec::new(),
}; }
Ok(agent)
} }
} }
@ -509,22 +520,25 @@ async fn handle_upload_result(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{Agent, ClientBuilder, ClientFeature, ClientType, Event}; use super::{Agent, BareJid, ClientBuilder, ClientFeature, ClientType, Event};
use std::str::FromStr;
use tokio_xmpp::AsyncClient as TokioXmppClient; use tokio_xmpp::AsyncClient as TokioXmppClient;
#[tokio::test] #[tokio::test]
async fn test_simple() { async fn test_simple() {
let client = TokioXmppClient::new("foo@bar", "meh").unwrap(); let jid = BareJid::from_str("foo@bar").unwrap();
let client = TokioXmppClient::new(jid.clone(), "meh");
// Client instance // Client instance
let client_builder = ClientBuilder::new("foo@bar", "meh") let client_builder = ClientBuilder::new(jid, "meh")
.set_client(ClientType::Bot, "xmpp-rs") .set_client(ClientType::Bot, "xmpp-rs")
.set_website("https://gitlab.com/xmpp-rs/xmpp-rs") .set_website("https://gitlab.com/xmpp-rs/xmpp-rs")
.set_default_nick("bot") .set_default_nick("bot")
.enable_feature(ClientFeature::Avatars) .enable_feature(ClientFeature::Avatars)
.enable_feature(ClientFeature::ContactList); .enable_feature(ClientFeature::ContactList);
let mut agent: Agent = client_builder.build_impl(client).unwrap(); let mut agent: Agent = client_builder.build_impl(client);
while let Some(events) = agent.wait_for_events().await { while let Some(events) = agent.wait_for_events().await {
assert!(match events[0] { assert!(match events[0] {