xmlstream: make sink implementation generic

This allows to use any serialisable type. The advantage is that moves
and clones are avoided (which would otherwise be needed to construct
e.g. a XmppStreamElement from a Stanza or Message).
This commit is contained in:
Jonas Schäfer 2024-09-02 17:27:23 +02:00
commit c6f928d5c8
6 changed files with 62 additions and 34 deletions

View file

@ -5,7 +5,7 @@ use crate::{
client::{login::client_login, stream::ClientState}, client::{login::client_login, stream::ClientState},
connect::ServerConnector, connect::ServerConnector,
error::Error, error::Error,
xmlstream::Timeouts, xmlstream::{Timeouts, XmppStream, XmppStreamElement},
Stanza, Stanza,
}; };
@ -75,7 +75,9 @@ impl<C: ServerConnector> Client<C> {
/// Make sure to disable reconnect. /// Make sure to disable reconnect.
pub async fn send_end(&mut self) -> Result<(), Error> { pub async fn send_end(&mut self) -> Result<(), Error> {
match self.state { match self.state {
ClientState::Connected { ref mut stream, .. } => Ok(stream.close().await?), ClientState::Connected { ref mut stream, .. } => {
Ok(<XmppStream<C::Stream> as SinkExt<&XmppStreamElement>>::close(stream).await?)
}
ClientState::Connecting { .. } => { ClientState::Connecting { .. } => {
self.state = ClientState::Disconnected; self.state = ClientState::Disconnected;
Ok(()) Ok(())

View file

@ -97,7 +97,10 @@ impl<C: ServerConnector> Stream for Client<C> {
bound_jid, bound_jid,
} => { } => {
// Poll sink // Poll sink
match Pin::new(&mut stream).poll_ready(cx) { match <XmppStream<C::Stream> as Sink<&XmppStreamElement>>::poll_ready(
Pin::new(&mut stream),
cx,
) {
Poll::Pending => (), Poll::Pending => (),
Poll::Ready(Ok(())) => (), Poll::Ready(Ok(())) => (),
Poll::Ready(Err(e)) => { Poll::Ready(Err(e)) => {
@ -195,36 +198,45 @@ impl<C: ServerConnector> Sink<Stanza> for Client<C> {
fn start_send(mut self: Pin<&mut Self>, item: Stanza) -> Result<(), Self::Error> { fn start_send(mut self: Pin<&mut Self>, item: Stanza) -> Result<(), Self::Error> {
match self.state { match self.state {
ClientState::Connected { ref mut stream, .. } => Pin::new(stream) ClientState::Connected { ref mut stream, .. } => {
.start_send(&item.into()) Pin::new(stream).start_send(&item).map_err(|e| e.into())
.map_err(|e| e.into()), }
_ => Err(Error::InvalidState), _ => Err(Error::InvalidState),
} }
} }
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> { fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
match self.state { match self.state {
ClientState::Connected { ref mut stream, .. } => { ClientState::Connected { ref mut stream, .. } => <XmppStream<C::Stream> as Sink<
Pin::new(stream).poll_ready(cx).map_err(|e| e.into()) &XmppStreamElement,
} >>::poll_ready(
Pin::new(stream), cx
)
.map_err(|e| e.into()),
_ => Poll::Pending, _ => Poll::Pending,
} }
} }
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> { fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
match self.state { match self.state {
ClientState::Connected { ref mut stream, .. } => { ClientState::Connected { ref mut stream, .. } => <XmppStream<C::Stream> as Sink<
Pin::new(stream).poll_flush(cx).map_err(|e| e.into()) &XmppStreamElement,
} >>::poll_flush(
Pin::new(stream), cx
)
.map_err(|e| e.into()),
_ => Poll::Pending, _ => Poll::Pending,
} }
} }
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> { fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
match self.state { match self.state {
ClientState::Connected { ref mut stream, .. } => { ClientState::Connected { ref mut stream, .. } => <XmppStream<C::Stream> as Sink<
Pin::new(stream).poll_close(cx).map_err(|e| e.into()) &XmppStreamElement,
} >>::poll_close(
Pin::new(stream), cx
)
.map_err(|e| e.into()),
_ => Poll::Pending, _ => Poll::Pending,
} }
} }

View file

@ -6,7 +6,10 @@ use std::pin::Pin;
use std::task::Context; use std::task::Context;
use crate::{ use crate::{
component::Component, connect::ServerConnector, xmlstream::XmppStreamElement, Error, Stanza, component::Component,
connect::ServerConnector,
xmlstream::{XmppStream, XmppStreamElement},
Error, Stanza,
}; };
impl<C: ServerConnector> Stream for Component<C> { impl<C: ServerConnector> Stream for Component<C> {
@ -42,25 +45,31 @@ impl<C: ServerConnector> Sink<Stanza> for Component<C> {
fn start_send(mut self: Pin<&mut Self>, item: Stanza) -> Result<(), Self::Error> { fn start_send(mut self: Pin<&mut Self>, item: Stanza) -> Result<(), Self::Error> {
Pin::new(&mut self.stream) Pin::new(&mut self.stream)
.start_send(&item.into()) .start_send(&item)
.map_err(|e| e.into()) .map_err(|e| e.into())
} }
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> { fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
Pin::new(&mut self.stream) <XmppStream<C::Stream> as Sink<&XmppStreamElement>>::poll_ready(
.poll_ready(cx) Pin::new(&mut self.stream),
.map_err(|e| e.into()) cx,
)
.map_err(|e| e.into())
} }
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> { fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
Pin::new(&mut self.stream) <XmppStream<C::Stream> as Sink<&XmppStreamElement>>::poll_flush(
.poll_flush(cx) Pin::new(&mut self.stream),
.map_err(|e| e.into()) cx,
)
.map_err(|e| e.into())
} }
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> { fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Result<(), Self::Error>> {
Pin::new(&mut self.stream) <XmppStream<C::Stream> as Sink<&XmppStreamElement>>::poll_close(
.poll_close(cx) Pin::new(&mut self.stream),
.map_err(|e| e.into()) cx,
)
.map_err(|e| e.into())
} }
} }

View file

@ -6,6 +6,7 @@
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use xmpp_parsers::{iq::Iq, jid::Jid, message::Message, presence::Presence}; use xmpp_parsers::{iq::Iq, jid::Jid, message::Message, presence::Presence};
use xso::{AsXml, FromXml};
use crate::xmlstream::XmppStreamElement; use crate::xmlstream::XmppStreamElement;
use crate::Error; use crate::Error;
@ -16,15 +17,19 @@ fn make_id() -> String {
} }
/// A stanza sent/received over the stream. /// A stanza sent/received over the stream.
#[derive(Debug)] #[derive(FromXml, AsXml, Debug)]
#[xml()]
pub enum Stanza { pub enum Stanza {
/// IQ stanza /// IQ stanza
#[xml(transparent)]
Iq(Iq), Iq(Iq),
/// Message stanza /// Message stanza
#[xml(transparent)]
Message(Message), Message(Message),
/// Presence stanza /// Presence stanza
#[xml(transparent)]
Presence(Presence), Presence(Presence),
} }

View file

@ -454,7 +454,7 @@ impl<Io: AsyncWrite + Unpin, T: FromXml + AsXml> XmlStream<Io, T> {
} }
} }
impl<'x, Io: AsyncWrite, T: FromXml + AsXml> Sink<&'x T> for XmlStream<Io, T> { impl<'x, Io: AsyncWrite, T: FromXml + AsXml, U: AsXml> Sink<&'x U> for XmlStream<Io, T> {
type Error = io::Error; type Error = io::Error;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@ -475,7 +475,7 @@ impl<'x, Io: AsyncWrite, T: FromXml + AsXml> Sink<&'x T> for XmlStream<Io, T> {
this.inner.poll_close(cx) this.inner.poll_close(cx)
} }
fn start_send(self: Pin<&mut Self>, item: &'x T) -> Result<(), Self::Error> { fn start_send(self: Pin<&mut Self>, item: &'x U) -> Result<(), Self::Error> {
let this = self.project(); let this = self.project();
this.write_state.check_writable()?; this.write_state.check_writable()?;
this.inner.start_send_xso(item) this.inner.start_send_xso(item)

View file

@ -158,7 +158,7 @@ async fn test_clean_shutdown() {
) )
.await?; .await?;
let (_, mut stream) = stream.recv_features::<Data>().await?; let (_, mut stream) = stream.recv_features::<Data>().await?;
stream.close().await?; SinkExt::<&Data>::close(&mut stream).await?;
match stream.next().await { match stream.next().await {
Some(Err(ReadError::StreamFooterReceived)) => (), Some(Err(ReadError::StreamFooterReceived)) => (),
other => panic!("unexpected stream message: {:?}", other), other => panic!("unexpected stream message: {:?}", other),
@ -181,7 +181,7 @@ async fn test_clean_shutdown() {
Some(Err(ReadError::StreamFooterReceived)) => (), Some(Err(ReadError::StreamFooterReceived)) => (),
other => panic!("unexpected stream message: {:?}", other), other => panic!("unexpected stream message: {:?}", other),
} }
stream.close().await?; SinkExt::<&Data>::close(&mut stream).await?;
Ok::<_, io::Error>(()) Ok::<_, io::Error>(())
}); });
@ -229,7 +229,7 @@ async fn test_exchange_data_stream_reset_and_shutdown() {
contents: "once more".to_owned(), contents: "once more".to_owned(),
}) })
.await?; .await?;
stream.close().await?; SinkExt::<&Data>::close(&mut stream).await?;
match stream.next().await { match stream.next().await {
Some(Ok(Data { contents })) => assert_eq!(contents, "hello world!"), Some(Ok(Data { contents })) => assert_eq!(contents, "hello world!"),
other => panic!("unexpected stream message: {:?}", other), other => panic!("unexpected stream message: {:?}", other),
@ -283,7 +283,7 @@ async fn test_exchange_data_stream_reset_and_shutdown() {
Some(Ok(Data { contents })) => assert_eq!(contents, "once more"), Some(Ok(Data { contents })) => assert_eq!(contents, "once more"),
other => panic!("unexpected stream message: {:?}", other), other => panic!("unexpected stream message: {:?}", other),
} }
stream.close().await?; SinkExt::<&Data>::close(&mut stream).await?;
match stream.next().await { match stream.next().await {
Some(Err(ReadError::StreamFooterReceived)) => (), Some(Err(ReadError::StreamFooterReceived)) => (),
other => panic!("unexpected stream message: {:?}", other), other => panic!("unexpected stream message: {:?}", other),
@ -427,7 +427,7 @@ async fn test_can_receive_after_shutdown() {
contents: "world!".to_owned(), contents: "world!".to_owned(),
}) })
.await?; .await?;
stream.close().await?; <XmlStream<_, _> as SinkExt<&Data>>::close(&mut stream).await?;
Ok::<_, io::Error>(()) Ok::<_, io::Error>(())
}); });